teuthology 1.2.0__py3-none-any.whl → 1.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scripts/node_cleanup.py +18 -2
- scripts/suite.py +2 -0
- teuthology/__init__.py +0 -1
- teuthology/config.py +28 -7
- teuthology/dispatcher/supervisor.py +9 -6
- teuthology/lock/cli.py +4 -2
- teuthology/lock/ops.py +10 -9
- teuthology/lock/query.py +28 -4
- teuthology/lock/util.py +1 -1
- teuthology/misc.py +13 -58
- teuthology/openstack/__init__.py +202 -176
- teuthology/openstack/setup-openstack.sh +52 -27
- teuthology/orchestra/connection.py +3 -1
- teuthology/orchestra/daemon/cephadmunit.py +2 -2
- teuthology/orchestra/opsys.py +15 -0
- teuthology/orchestra/remote.py +54 -2
- teuthology/orchestra/run.py +8 -2
- teuthology/provision/downburst.py +84 -43
- teuthology/provision/fog.py +2 -2
- teuthology/repo_utils.py +3 -1
- teuthology/run.py +1 -1
- teuthology/scrape.py +5 -2
- teuthology/suite/merge.py +3 -1
- teuthology/suite/run.py +51 -37
- teuthology/suite/util.py +2 -2
- teuthology/task/install/rpm.py +8 -16
- teuthology/task/internal/__init__.py +2 -1
- teuthology/task/internal/syslog.py +17 -13
- teuthology/task/kernel.py +1 -1
- {teuthology-1.2.0.dist-info → teuthology-1.2.2.dist-info}/METADATA +11 -10
- {teuthology-1.2.0.dist-info → teuthology-1.2.2.dist-info}/RECORD +38 -44
- {teuthology-1.2.0.dist-info → teuthology-1.2.2.dist-info}/WHEEL +1 -1
- teuthology/lock/test/__init__.py +0 -0
- teuthology/lock/test/test_lock.py +0 -7
- teuthology/task/tests/__init__.py +0 -170
- teuthology/task/tests/test_fetch_coredumps.py +0 -116
- teuthology/task/tests/test_locking.py +0 -25
- teuthology/task/tests/test_run.py +0 -40
- {teuthology-1.2.0.data → teuthology-1.2.2.data}/scripts/adjust-ulimits +0 -0
- {teuthology-1.2.0.data → teuthology-1.2.2.data}/scripts/daemon-helper +0 -0
- {teuthology-1.2.0.data → teuthology-1.2.2.data}/scripts/stdin-killer +0 -0
- {teuthology-1.2.0.dist-info → teuthology-1.2.2.dist-info}/entry_points.txt +0 -0
- {teuthology-1.2.0.dist-info → teuthology-1.2.2.dist-info/licenses}/LICENSE +0 -0
- {teuthology-1.2.0.dist-info → teuthology-1.2.2.dist-info}/top_level.txt +0 -0
teuthology/openstack/__init__.py
CHANGED
@@ -35,7 +35,6 @@ import subprocess
|
|
35
35
|
import tempfile
|
36
36
|
import teuthology
|
37
37
|
import time
|
38
|
-
import types
|
39
38
|
import yaml
|
40
39
|
import base64
|
41
40
|
|
@@ -46,6 +45,7 @@ from teuthology.config import config as teuth_config
|
|
46
45
|
from teuthology.config import set_config_attr
|
47
46
|
from teuthology.orchestra import connection
|
48
47
|
from teuthology import misc
|
48
|
+
from openstack import connection as openstack_connection
|
49
49
|
|
50
50
|
from yaml.representer import SafeRepresenter
|
51
51
|
|
@@ -78,6 +78,8 @@ class OpenStackInstance(object):
|
|
78
78
|
self.name_or_id = name_or_id
|
79
79
|
self.private_or_floating_ip = None
|
80
80
|
self.private_ip = None
|
81
|
+
self.info = info
|
82
|
+
self.conn = self._create_connection()
|
81
83
|
if info is None:
|
82
84
|
self.set_info()
|
83
85
|
else:
|
@@ -88,11 +90,14 @@ class OpenStackInstance(object):
|
|
88
90
|
errmsg = '{}: {}'.format(errmsg, self.info['message'])
|
89
91
|
raise Exception(errmsg)
|
90
92
|
|
93
|
+
def _create_connection(self):
|
94
|
+
return openstack_connection.from_config(cloud=None)
|
95
|
+
|
91
96
|
def set_info(self):
|
92
97
|
try:
|
93
|
-
|
94
|
-
|
95
|
-
|
98
|
+
server = self.conn.compute.find_server(self.name_or_id)
|
99
|
+
if server:
|
100
|
+
self.info = {k.lower(): v for k, v in server.to_dict().items()}
|
96
101
|
except CalledProcessError:
|
97
102
|
self.info = None
|
98
103
|
|
@@ -129,32 +134,17 @@ class OpenStackInstance(object):
|
|
129
134
|
self.set_info()
|
130
135
|
|
131
136
|
def get_ip_neutron(self):
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
subnet_ids.append(subnet['id'])
|
138
|
-
if not subnet_ids:
|
139
|
-
raise Exception("no subnet with ip_version == 4")
|
140
|
-
ports = json.loads(misc.sh("unset OS_AUTH_TYPE OS_TOKEN ; "
|
141
|
-
"neutron port-list -f json -c fixed_ips -c device_id"))
|
142
|
-
fixed_ips = None
|
137
|
+
conn = OpenStack().conn
|
138
|
+
subnets = [subnet.id for subnet in conn.network.subnets() if subnet.ip_version == 4]
|
139
|
+
if not subnets:
|
140
|
+
raise Exception("No subnet with ip_version == 4 found")
|
141
|
+
ports = conn.network.ports(device_id=self['id'])
|
143
142
|
for port in ports:
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
ip = None
|
150
|
-
for fixed_ip in fixed_ips:
|
151
|
-
record = json.loads(fixed_ip)
|
152
|
-
if record['subnet_id'] in subnet_ids:
|
153
|
-
ip = record['ip_address']
|
154
|
-
break
|
155
|
-
if not ip:
|
156
|
-
raise Exception("no ip")
|
157
|
-
return ip
|
143
|
+
for fixed_ip in port.fixed_ips:
|
144
|
+
if fixed_ip.get('subnet_id') in subnets:
|
145
|
+
return fixed_ip['ip_address']
|
146
|
+
|
147
|
+
raise Exception("No IP found for instance")
|
158
148
|
|
159
149
|
def get_ip(self, network):
|
160
150
|
"""
|
@@ -212,6 +202,7 @@ class OpenStack(object):
|
|
212
202
|
image2url = {
|
213
203
|
'centos-7.2-x86_64': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1511.qcow2',
|
214
204
|
'centos-7.3-x86_64': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1701.qcow2',
|
205
|
+
'centos-9.stream-x86_64': 'https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-20240703.1.x86_64.qcow2',
|
215
206
|
'opensuse-42.1-x86_64': 'http://download.opensuse.org/repositories/Cloud:/Images:/Leap_42.1/images/openSUSE-Leap-42.1-OpenStack.x86_64.qcow2',
|
216
207
|
'opensuse-42.2-x86_64': 'http://download.opensuse.org/repositories/Cloud:/Images:/Leap_42.2/images/openSUSE-Leap-42.2-OpenStack.x86_64.qcow2',
|
217
208
|
'opensuse-42.3-x86_64': 'http://download.opensuse.org/repositories/Cloud:/Images:/Leap_42.3/images/openSUSE-Leap-42.3-OpenStack.x86_64.qcow2',
|
@@ -221,6 +212,14 @@ class OpenStack(object):
|
|
221
212
|
'ubuntu-16.04-x86_64': 'https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img',
|
222
213
|
'ubuntu-16.04-aarch64': 'https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-arm64-disk1.img',
|
223
214
|
'ubuntu-16.04-i686': 'https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-i386-disk1.img',
|
215
|
+
'ubuntu-18.04-x86_64': 'https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img',
|
216
|
+
'ubuntu-18.04-aarch64': 'https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-arm64.img',
|
217
|
+
'ubuntu-18.04-i686': 'https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-i386.img',
|
218
|
+
'ubuntu-20.04-x86_64': 'https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img',
|
219
|
+
'ubuntu-20.04-aarch64': 'https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img',
|
220
|
+
'ubuntu-22.04-x86_64': 'https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img',
|
221
|
+
'ubuntu-22.04-aarch64': 'https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-arm64.img',
|
222
|
+
'ubuntu-24.04-x86_64': 'https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img',
|
224
223
|
'debian-8.0-x86_64': 'http://cdimage.debian.org/cdimage/openstack/current/debian-8.7.1-20170215-openstack-amd64.qcow2',
|
225
224
|
}
|
226
225
|
|
@@ -230,11 +229,15 @@ class OpenStack(object):
|
|
230
229
|
self.username = 'ubuntu'
|
231
230
|
self.up_string = "UNKNOWN"
|
232
231
|
self.teuthology_suite = 'teuthology-suite'
|
232
|
+
self.conn = self._create_connection()
|
233
233
|
|
234
234
|
token = None
|
235
235
|
token_expires = None
|
236
236
|
token_cache_duration = 3600
|
237
237
|
|
238
|
+
def _create_connection(self):
|
239
|
+
return openstack_connection.from_config(cloud=None)
|
240
|
+
|
238
241
|
def cache_token(self):
|
239
242
|
if self.provider != 'ovh':
|
240
243
|
return False
|
@@ -348,9 +351,10 @@ class OpenStack(object):
|
|
348
351
|
"""
|
349
352
|
Return the uuid of the network in OpenStack.
|
350
353
|
"""
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
+
conn = self.conn
|
355
|
+
network = conn.network.find_network(network)
|
356
|
+
if network:
|
357
|
+
return network.id
|
354
358
|
|
355
359
|
def type_version_arch(self, os_type, os_version, arch):
|
356
360
|
"""
|
@@ -409,11 +413,10 @@ class OpenStack(object):
|
|
409
413
|
|
410
414
|
@staticmethod
|
411
415
|
def sort_flavors(flavors):
|
412
|
-
def
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
return sorted(flavors, cmp=sort_flavor)
|
416
|
+
def sort_key(flavor):
|
417
|
+
# Create a tuple for sorting: (VCPUs, RAM, Disk)
|
418
|
+
return (flavor['VCPUs'], flavor['RAM'], flavor['Disk'])
|
419
|
+
return sorted(flavors, key=sort_key)
|
417
420
|
|
418
421
|
def get_os_flavors(self):
|
419
422
|
flavors = json.loads(self.run("flavor list -f json"))
|
@@ -519,7 +522,7 @@ class OpenStack(object):
|
|
519
522
|
result = copy.deepcopy(defaults)
|
520
523
|
if not hints:
|
521
524
|
return result
|
522
|
-
if
|
525
|
+
if isinstance(hints, dict):
|
523
526
|
raise TypeError("openstack: " + str(hints) +
|
524
527
|
" must be an array, not a dict")
|
525
528
|
for hint in hints:
|
@@ -533,19 +536,21 @@ class OpenStack(object):
|
|
533
536
|
|
534
537
|
@staticmethod
|
535
538
|
def list_instances():
|
539
|
+
conn = OpenStack().conn
|
536
540
|
ownedby = "ownedby='" + teuth_config.openstack['ip'] + "'"
|
537
|
-
|
538
|
-
|
539
|
-
return filter(lambda instance: ownedby in instance['Properties'], all)
|
541
|
+
instances = conn.compute.servers(all_projects=True)
|
542
|
+
return [inst for inst in instances if ownedby in (getattr(inst, 'metadata', {}) or {}).get('Properties', '')]
|
540
543
|
|
541
544
|
@staticmethod
|
542
545
|
def list_volumes():
|
546
|
+
conn = OpenStack().conn
|
543
547
|
ownedby = "ownedby='" + teuth_config.openstack['ip'] + "'"
|
544
|
-
|
548
|
+
volumes = conn.block_storage.volumes()
|
545
549
|
def select(volume):
|
546
|
-
|
547
|
-
|
548
|
-
|
550
|
+
props = volume.metadata or {}
|
551
|
+
return (ownedby in props.get('Properties', '') and
|
552
|
+
props.get('display_name', '').startswith('target'))
|
553
|
+
return filter(select, volumes)
|
549
554
|
|
550
555
|
def cloud_init_wait(self, instance):
|
551
556
|
"""
|
@@ -564,12 +569,11 @@ class OpenStack(object):
|
|
564
569
|
with safe_while(sleep=30, tries=30,
|
565
570
|
action="cloud_init_wait " + ip) as proceed:
|
566
571
|
success = False
|
567
|
-
# CentOS 6.6 logs in /var/log/clout-init-output.log
|
568
|
-
# CentOS 7.0 logs in /var/log/clout-init.log
|
569
572
|
tail = ("tail --follow=name --retry"
|
570
573
|
" /var/log/cloud-init*.log /tmp/init.out")
|
571
574
|
while proceed():
|
572
575
|
try:
|
576
|
+
log.debug("Attempting to connect to instance at IP: " + ip)
|
573
577
|
client = connection.connect(**client_args)
|
574
578
|
except paramiko.PasswordRequiredException:
|
575
579
|
raise Exception(
|
@@ -611,15 +615,17 @@ class OpenStack(object):
|
|
611
615
|
break
|
612
616
|
except socket.timeout:
|
613
617
|
client.close()
|
614
|
-
log.debug('cloud_init_wait socket.timeout ' + tail)
|
615
618
|
continue
|
616
|
-
except socket.error
|
619
|
+
except socket.error:
|
617
620
|
client.close()
|
618
|
-
log.debug('cloud_init_wait socket.error ' + str(e) + ' ' + tail)
|
619
621
|
continue
|
620
|
-
|
622
|
+
finally:
|
623
|
+
client.close()
|
621
624
|
if success:
|
625
|
+
log.debug('Cloud-init completed successfully for IP: ' + ip)
|
622
626
|
break
|
627
|
+
if not success:
|
628
|
+
log.debug('Cloud-init did not complete successfully within the given retries.')
|
623
629
|
return success
|
624
630
|
|
625
631
|
def get_ip(self, instance_id, network):
|
@@ -899,7 +905,7 @@ class TeuthologyOpenStack(OpenStack):
|
|
899
905
|
if ceph_repo:
|
900
906
|
command = (
|
901
907
|
"perl -pi -e 's|.*{opt}.*|{opt}: {value}|'"
|
902
|
-
" ~/.teuthology.yaml"
|
908
|
+
" ~/.teuthology.yaml || true"
|
903
909
|
).format(opt='ceph_git_url', value=ceph_repo)
|
904
910
|
self.ssh(command)
|
905
911
|
user_home = '/home/' + self.username
|
@@ -914,6 +920,7 @@ class TeuthologyOpenStack(OpenStack):
|
|
914
920
|
" --machine-type openstack " +
|
915
921
|
" ".join(map(lambda x: "'" + x + "'", argv))
|
916
922
|
)
|
923
|
+
log.info("Running teuthology-suite: " + command)
|
917
924
|
return self.ssh(command)
|
918
925
|
|
919
926
|
def reminders(self):
|
@@ -951,7 +958,7 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
951
958
|
logging.getLogger("paramiko.transport").setLevel(logging.DEBUG)
|
952
959
|
teuthology.log.setLevel(loglevel)
|
953
960
|
|
954
|
-
def ssh(self, command):
|
961
|
+
def ssh(self, command, timeout=300):
|
955
962
|
"""
|
956
963
|
Run a command in the OpenStack instance of the teuthology cluster.
|
957
964
|
Return the stdout / stderr of the command.
|
@@ -969,14 +976,27 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
969
976
|
# get the I/O channel to iterate line by line
|
970
977
|
transport = client.get_transport()
|
971
978
|
channel = transport.open_session()
|
972
|
-
channel.
|
973
|
-
|
974
|
-
output = channel.makefile('r', 1)
|
975
|
-
log.debug(":ssh@" + ip + ":" + command)
|
979
|
+
channel.settimeout(timeout)
|
980
|
+
log.debug(f"ssh {self.instance.get_floating_ip_or_ip()}: {command}")
|
976
981
|
channel.exec_command(command)
|
977
|
-
|
978
|
-
|
979
|
-
|
982
|
+
stdout, stderr = [], []
|
983
|
+
start_time = time.time()
|
984
|
+
while True:
|
985
|
+
if channel.recv_ready():
|
986
|
+
stdout.append(channel.recv(4096).decode())
|
987
|
+
if channel.recv_stderr_ready():
|
988
|
+
stderr.append(channel.recv_stderr(4096).decode())
|
989
|
+
if channel.exit_status_ready():
|
990
|
+
break
|
991
|
+
if time.time() - start_time > timeout:
|
992
|
+
raise TimeoutError("SSH command timed out!")
|
993
|
+
time.sleep(0.1) # Small sleep to avoid busy waiting
|
994
|
+
exit_status = channel.recv_exit_status()
|
995
|
+
stdout_txt, stderr_txt = ''.join(stdout), ''.join(stderr)
|
996
|
+
if exit_status != 0:
|
997
|
+
log.warning(f"SSH command failed with exit status {exit_status}")
|
998
|
+
return exit_status, stdout_txt, stderr_txt
|
999
|
+
|
980
1000
|
|
981
1001
|
def verify_openstack(self):
|
982
1002
|
"""
|
@@ -1029,32 +1049,48 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
1029
1049
|
fd, path = tempfile.mkstemp()
|
1030
1050
|
os.close(fd)
|
1031
1051
|
|
1032
|
-
|
1052
|
+
bootstrap_path = os.getcwd() + "/teuthology/openstack" + '/bootstrap-teuthology.sh'
|
1053
|
+
with open(bootstrap_path, 'rb') as f:
|
1033
1054
|
b64_bootstrap = base64.b64encode(f.read())
|
1034
1055
|
bootstrap_content = str(b64_bootstrap.decode())
|
1035
1056
|
|
1036
1057
|
openrc_sh = ''
|
1037
1058
|
cacert_cmd = None
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1059
|
+
clouds_yaml_path = os.path.expanduser('~/.config/openstack/clouds.yaml')
|
1060
|
+
if os.path.exists(clouds_yaml_path):
|
1061
|
+
log.debug(f"clouds.yaml found at {clouds_yaml_path}, processing for openrc.sh")
|
1062
|
+
with open(clouds_yaml_path, 'r') as f:
|
1063
|
+
clouds_data = yaml.safe_load(f)
|
1064
|
+
cloud_name = os.environ.get('OS_CLOUD', 'default')
|
1065
|
+
cloud_config = clouds_data.get('clouds', {}).get(cloud_name, {})
|
1066
|
+
if not cloud_config:
|
1067
|
+
raise Exception(f"Cloud '{cloud_name}' not found in clouds.yaml")
|
1068
|
+
auth = cloud_config.get('auth', {})
|
1069
|
+
for key, value in {**auth, **cloud_config}.items():
|
1070
|
+
if isinstance(value, str):
|
1071
|
+
openrc_sh += f"export OS_{key.upper()}={value}\n"
|
1072
|
+
else:
|
1073
|
+
for (var, value) in os.environ.items():
|
1074
|
+
if var in ('OS_TOKEN_VALUE', 'OS_TOKEN_EXPIRES'):
|
1075
|
+
continue
|
1076
|
+
if var == 'OS_CACERT':
|
1077
|
+
cacert_path = '/home/%s/.openstack.crt' % self.username
|
1078
|
+
cacert_file = value
|
1079
|
+
openrc_sh += 'export %s=%s\n' % (var, cacert_path)
|
1080
|
+
cacert_cmd = (
|
1081
|
+
"su - -c 'cat > {path}' {user} <<EOF\n"
|
1082
|
+
"{data}\n"
|
1083
|
+
"EOF\n").format(
|
1084
|
+
path=cacert_path,
|
1085
|
+
user=self.username,
|
1086
|
+
data=open(cacert_file).read())
|
1087
|
+
elif var.startswith('OS_'):
|
1088
|
+
openrc_sh += 'export %s=%s\n' % (var, value)
|
1054
1089
|
b64_openrc_sh = base64.b64encode(openrc_sh.encode())
|
1055
1090
|
openrc_sh_content = str(b64_openrc_sh.decode())
|
1056
1091
|
|
1057
1092
|
network = OpenStack().get_network()
|
1093
|
+
log.debug(f"Network to be used: {network}")
|
1058
1094
|
ceph_workbench = ''
|
1059
1095
|
if self.args.ceph_workbench_git_url:
|
1060
1096
|
ceph_workbench += (" --ceph-workbench-branch " +
|
@@ -1069,21 +1105,19 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
1069
1105
|
'--server-group %s' % self.server_group(),
|
1070
1106
|
'--worker-group %s' % self.worker_group(),
|
1071
1107
|
'--package-repo %s' % self.packages_repository(),
|
1072
|
-
#'--setup-all',
|
1073
1108
|
]
|
1109
|
+
log.debug(f"Setup options: {setup_options}")
|
1110
|
+
|
1074
1111
|
all_options = [
|
1075
|
-
'--install',
|
1076
|
-
|
1077
|
-
'--
|
1078
|
-
'--setup-
|
1079
|
-
|
1080
|
-
'--setup-
|
1081
|
-
'--setup-
|
1082
|
-
'--setup-
|
1083
|
-
'--
|
1084
|
-
'--setup-paddles', #do_setup_paddles=true
|
1085
|
-
'--setup-pulpito', #do_setup_pulpito=true
|
1086
|
-
'--populate-paddles', #do_populate_paddles=true
|
1112
|
+
'--install',
|
1113
|
+
'--config',
|
1114
|
+
'--setup-docker',
|
1115
|
+
'--setup-salt-master',
|
1116
|
+
'--setup-dnsmasq',
|
1117
|
+
'--setup-fail2ban',
|
1118
|
+
'--setup-paddles',
|
1119
|
+
'--setup-pulpito',
|
1120
|
+
'--populate-paddles',
|
1087
1121
|
]
|
1088
1122
|
|
1089
1123
|
if self.args.ceph_workbench_git_url:
|
@@ -1122,8 +1156,6 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
1122
1156
|
"{user} >> /tmp/init.out "
|
1123
1157
|
"2>&1".format(user=self.username,
|
1124
1158
|
opts=' '.join(setup_options + all_options))),
|
1125
|
-
# wa: we want to stop paddles and pulpito started by
|
1126
|
-
# setup-openstack before starting teuthology service
|
1127
1159
|
"pkill -f 'pecan serve'",
|
1128
1160
|
"pkill -f 'python run.py'",
|
1129
1161
|
"systemctl enable teuthology",
|
@@ -1143,11 +1175,6 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
1143
1175
|
'name': self.username
|
1144
1176
|
}
|
1145
1177
|
},
|
1146
|
-
'packages': [
|
1147
|
-
'python-virtualenv',
|
1148
|
-
'git',
|
1149
|
-
'rsync',
|
1150
|
-
],
|
1151
1178
|
'write_files': [
|
1152
1179
|
{
|
1153
1180
|
'path': '/tmp/bootstrap-teuthology.sh',
|
@@ -1163,7 +1190,9 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
1163
1190
|
'permissions': '0644',
|
1164
1191
|
}
|
1165
1192
|
],
|
1166
|
-
'runcmd':
|
1193
|
+
'runcmd': [
|
1194
|
+
'apt-get update && apt-get install -y python3-virtualenv git rsync >> /tmp/init.out 2>&1'
|
1195
|
+
] + cmds,
|
1167
1196
|
'final_message': 'teuthology is up and running after $UPTIME seconds'
|
1168
1197
|
}
|
1169
1198
|
user_data = "#cloud-config\n%s" % \
|
@@ -1186,36 +1215,41 @@ ssh access : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
|
|
1186
1215
|
return "teuth-%s-worker" % self.args.name
|
1187
1216
|
|
1188
1217
|
def create_security_group(self):
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
# access
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1218
|
+
conn = OpenStack().conn
|
1219
|
+
server_sg = conn.network.find_security_group(self.server_group())
|
1220
|
+
worker_sg = conn.network.find_security_group(self.worker_group())
|
1221
|
+
if not server_sg:
|
1222
|
+
server_sg = conn.network.create_security_group(name=self.server_group())
|
1223
|
+
if not worker_sg:
|
1224
|
+
worker_sg = conn.network.create_security_group(name=self.worker_group())
|
1225
|
+
def add_rule(sg_id, protocol, port=None, remote_group_id=None):
|
1226
|
+
rule_args = {
|
1227
|
+
'security_group_id': sg_id,
|
1228
|
+
'direction': 'ingress',
|
1229
|
+
'protocol': protocol,
|
1230
|
+
'ethertype': 'IPv4',
|
1231
|
+
}
|
1232
|
+
if port is not None:
|
1233
|
+
rule_args['port_range_min'] = rule_args['port_range_max'] = port
|
1234
|
+
if remote_group_id:
|
1235
|
+
rule_args['remote_group_id'] = remote_group_id
|
1236
|
+
else:
|
1237
|
+
rule_args['remote_ip_prefix'] = '0.0.0.0/0'
|
1238
|
+
try:
|
1239
|
+
conn.network.create_security_group_rule(**rule_args)
|
1240
|
+
except Exception as e:
|
1241
|
+
log.warning(f"Security group rule creation skipped or failed: {e}")
|
1242
|
+
# tcp access to enable reliable inter-node communication
|
1243
|
+
for sg in (server_sg, worker_sg):
|
1244
|
+
add_rule(sg.id, 'tcp')
|
1245
|
+
# access between teuthology and workers
|
1246
|
+
for port in (65535,):
|
1247
|
+
add_rule(worker_sg.id, 'udp', port=port, remote_group_id=server_sg.id)
|
1248
|
+
add_rule(server_sg.id, 'udp', port=port, remote_group_id=worker_sg.id)
|
1249
|
+
# access between members of one group
|
1250
|
+
add_rule(server_sg.id, 'udp', port=65535, remote_group_id=server_sg.id)
|
1251
|
+
# access within worker group
|
1252
|
+
add_rule(worker_sg.id, 'udp', port=65535, remote_group_id=worker_sg.id)
|
1219
1253
|
|
1220
1254
|
@staticmethod
|
1221
1255
|
def get_unassociated_floating_ip():
|
@@ -1230,32 +1264,14 @@ openstack security group rule create --protocol udp --src-group {server} --dst-p
|
|
1230
1264
|
|
1231
1265
|
@staticmethod
|
1232
1266
|
def create_floating_ip():
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
log.debug(e.output)
|
1239
|
-
log.debug('Trying newer API than Compute v2')
|
1240
|
-
try:
|
1241
|
-
network = 'floating'
|
1242
|
-
ip = json.loads(misc.sh("openstack --quiet floating ip create -f json '%s'" % network))
|
1243
|
-
return ip['floating_ip_address']
|
1244
|
-
except subprocess.CalledProcessError:
|
1245
|
-
log.debug("Can't create floating ip for network '%s'" % network)
|
1246
|
-
|
1247
|
-
log.debug("create_floating_ip: ip floating pool list failed")
|
1248
|
-
return None
|
1249
|
-
if not pools:
|
1267
|
+
conn = OpenStack().conn
|
1268
|
+
network_name = 'floating'
|
1269
|
+
network = conn.network.find_network(network_name)
|
1270
|
+
if not network:
|
1271
|
+
log.debug(f"Floating network {network_name} not found.")
|
1250
1272
|
return None
|
1251
|
-
|
1252
|
-
|
1253
|
-
ip = json.loads(OpenStack().run(
|
1254
|
-
"ip floating create -f json '" + pool + "'"))
|
1255
|
-
return ip['ip']
|
1256
|
-
except subprocess.CalledProcessError:
|
1257
|
-
log.debug("create_floating_ip: not creating a floating ip")
|
1258
|
-
return None
|
1273
|
+
floating_ip = conn.network.create_ip(floating_network_id=network.id)
|
1274
|
+
return floating_ip.floating_ip_address
|
1259
1275
|
|
1260
1276
|
@staticmethod
|
1261
1277
|
def associate_floating_ip(name_or_id):
|
@@ -1263,23 +1279,18 @@ openstack security group rule create --protocol udp --src-group {server} --dst-p
|
|
1263
1279
|
Associate a floating IP to the OpenStack instance
|
1264
1280
|
or do nothing if no floating ip can be created.
|
1265
1281
|
"""
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
if
|
1270
|
-
|
1282
|
+
conn = OpenStack().conn
|
1283
|
+
server = conn.compute.find_server(name_or_id)
|
1284
|
+
ip_address = TeuthologyOpenStack.get_unassociated_floating_ip()
|
1285
|
+
if not ip_address:
|
1286
|
+
ip_address = TeuthologyOpenStack.create_floating_ip()
|
1287
|
+
if ip_address:
|
1288
|
+
conn.compute.add_floating_ip_to_server(server, ip_address)
|
1271
1289
|
|
1272
1290
|
@staticmethod
|
1273
1291
|
def get_os_floating_ips():
|
1274
|
-
|
1275
|
-
|
1276
|
-
except subprocess.CalledProcessError as e:
|
1277
|
-
log.warning(e)
|
1278
|
-
if e.returncode == 1:
|
1279
|
-
return []
|
1280
|
-
else:
|
1281
|
-
raise e
|
1282
|
-
return ips
|
1292
|
+
conn = OpenStack().conn
|
1293
|
+
return list(conn.network.ips())
|
1283
1294
|
|
1284
1295
|
@staticmethod
|
1285
1296
|
def get_floating_ip_id(ip):
|
@@ -1307,12 +1318,17 @@ openstack security group rule create --protocol udp --src-group {server} --dst-p
|
|
1307
1318
|
"""
|
1308
1319
|
Remove the floating ip from instance_id and delete it.
|
1309
1320
|
"""
|
1310
|
-
|
1311
|
-
|
1321
|
+
conn = OpenStack().conn
|
1322
|
+
server = conn.compute.find_server(instance_id)
|
1323
|
+
if not server:
|
1324
|
+
return
|
1325
|
+
ip_address = OpenStackInstance(instance_id).get_floating_ip()
|
1326
|
+
if not ip_address:
|
1312
1327
|
return
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1328
|
+
conn.compute.remove_floating_ip_from_server(server, ip_address)
|
1329
|
+
floating_ip_obj = conn.network.find_ip(ip_address)
|
1330
|
+
if floating_ip_obj:
|
1331
|
+
conn.network.delete_ip(floating_ip_obj)
|
1316
1332
|
|
1317
1333
|
def create_cluster(self):
|
1318
1334
|
user_data = self.get_user_data()
|
@@ -1329,19 +1345,29 @@ openstack security group rule create --protocol udp --src-group {server} --dst-p
|
|
1329
1345
|
if not key_name:
|
1330
1346
|
raise Exception('No key name provided, use --key-name option')
|
1331
1347
|
log.debug('Using key name: %s' % self.args.key_name)
|
1332
|
-
self.
|
1348
|
+
image_name = self.image('ubuntu', '22.04', arch)
|
1349
|
+
log.debug("Using image: %s" % image_name)
|
1350
|
+
net_config = self.net()
|
1351
|
+
try:
|
1352
|
+
self.run(
|
1333
1353
|
"server create " +
|
1334
|
-
" --image '" +
|
1354
|
+
" --image '" + image_name + "' " +
|
1335
1355
|
" --flavor '" + flavor + "' " +
|
1336
|
-
" " +
|
1356
|
+
" " + net_config +
|
1337
1357
|
" --key-name " + key_name +
|
1338
1358
|
" --user-data " + user_data +
|
1339
1359
|
security_group +
|
1340
1360
|
" --wait " + self.server_name() +
|
1341
1361
|
" -f json")
|
1342
|
-
|
1362
|
+
except Exception as e:
|
1363
|
+
log.error("Error during server creation: %s" % str(e))
|
1364
|
+
raise
|
1365
|
+
finally:
|
1366
|
+
os.unlink(user_data)
|
1343
1367
|
self.instance = OpenStackInstance(self.server_name())
|
1368
|
+
log.debug("OpenStackInstance created for server: %s" % self.server_name())
|
1344
1369
|
self.associate_floating_ip(self.instance['id'])
|
1370
|
+
log.debug("Floating IP associated for instance ID: %s" % self.instance.get('id'))
|
1345
1371
|
return self.cloud_init_wait(self.instance)
|
1346
1372
|
|
1347
1373
|
def packages_repository(self):
|