from teuthology.config import set_config_attr
from teuthology.orchestra import connection
from teuthology import misc
+from openstack import connection as openstack_connection
from yaml.representer import SafeRepresenter
self.name_or_id = name_or_id
self.private_or_floating_ip = None
self.private_ip = None
+ self.info = info
+ self.conn = self._create_connection()
if info is None:
self.set_info()
else:
errmsg = '{}: {}'.format(errmsg, self.info['message'])
raise Exception(errmsg)
+ def _create_connection(self):
+ return openstack_connection.from_config(cloud=None)
+
def set_info(self):
try:
- self.info = json.loads(
- OpenStack().run("server show -f json " + self.name_or_id))
- enforce_json_dictionary(self.info)
+ server = self.conn.compute.find_server(self.name_or_id)
+ if server:
+ self.info = {k.lower(): v for k, v in server.to_dict().items()}
except CalledProcessError:
self.info = None
self.set_info()
def get_ip_neutron(self):
- subnets = json.loads(misc.sh("unset OS_AUTH_TYPE OS_TOKEN ; "
- "neutron subnet-list -f json -c id -c ip_version"))
- subnet_ids = []
- for subnet in subnets:
- if subnet['ip_version'] == 4:
- subnet_ids.append(subnet['id'])
- if not subnet_ids:
- raise Exception("no subnet with ip_version == 4")
- ports = json.loads(misc.sh("unset OS_AUTH_TYPE OS_TOKEN ; "
- "neutron port-list -f json -c fixed_ips -c device_id"))
- fixed_ips = None
+ conn = OpenStack().conn
+ subnets = [subnet.id for subnet in conn.network.subnets() if subnet.ip_version == 4]
+ if not subnets:
+ raise Exception("No subnet with ip_version == 4 found")
+ ports = conn.network.ports(device_id=self['id'])
for port in ports:
- if port['device_id'] == self['id']:
- fixed_ips = port['fixed_ips'].split("\n")
- break
- if not fixed_ips:
- raise Exception("no fixed ip record found")
- ip = None
- for fixed_ip in fixed_ips:
- record = json.loads(fixed_ip)
- if record['subnet_id'] in subnet_ids:
- ip = record['ip_address']
- break
- if not ip:
- raise Exception("no ip")
- return ip
+ for fixed_ip in port.fixed_ips:
+ if fixed_ip.get('subnet_id') in subnets:
+ return fixed_ip['ip_address']
+
+ raise Exception("No IP found for instance")
def get_ip(self, network):
"""
'ubuntu-16.04-x86_64': 'https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img',
'ubuntu-16.04-aarch64': 'https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-arm64-disk1.img',
'ubuntu-16.04-i686': 'https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-i386-disk1.img',
+ 'ubuntu-18.04-x86_64': 'https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img',
+ 'ubuntu-18.04-aarch64': 'https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-arm64.img',
+ 'ubuntu-18.04-i686': 'https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-i386.img',
+ 'ubuntu-20.04-x86_64': 'https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img',
+ 'ubuntu-20.04-aarch64': 'https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img',
+ 'ubuntu-22.04-x86_64': 'https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img',
+ 'ubuntu-22.04-aarch64': 'https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-arm64.img',
+ 'ubuntu-24.04-x86_64': 'https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img',
'debian-8.0-x86_64': 'http://cdimage.debian.org/cdimage/openstack/current/debian-8.7.1-20170215-openstack-amd64.qcow2',
}
self.username = 'ubuntu'
self.up_string = "UNKNOWN"
self.teuthology_suite = 'teuthology-suite'
+ self.conn = self._create_connection()
token = None
token_expires = None
token_cache_duration = 3600
+ def _create_connection(self):
+ return openstack_connection.from_config(cloud=None)
+
def cache_token(self):
if self.provider != 'ovh':
return False
"""
Return the uuid of the network in OpenStack.
"""
- r = json.loads(self.run("network show -f json " +
- network))
- return self.get_value(r, 'id')
+ conn = self.conn
+ network = conn.network.find_network(network)
+ if network:
+ return network.id
def type_version_arch(self, os_type, os_version, arch):
"""
@staticmethod
def sort_flavors(flavors):
- def sort_flavor(a, b):
- return (a['VCPUs'] - b['VCPUs'] or
- a['RAM'] - b['RAM'] or
- a['Disk'] - b['Disk'])
- return sorted(flavors, cmp=sort_flavor)
+ def sort_key(flavor):
+ # Create a tuple for sorting: (VCPUs, RAM, Disk)
+ return (flavor['VCPUs'], flavor['RAM'], flavor['Disk'])
+ return sorted(flavors, key=sort_key)
def get_os_flavors(self):
flavors = json.loads(self.run("flavor list -f json"))
@staticmethod
def list_instances():
+ conn = OpenStack().conn
ownedby = "ownedby='" + teuth_config.openstack['ip'] + "'"
- all = json.loads(OpenStack().run(
- "server list -f json --long --name 'target'"))
- return filter(lambda instance: ownedby in instance['Properties'], all)
+ instances = conn.compute.servers(all_projects=True)
+ return [inst for inst in instances if ownedby in (getattr(inst, 'metadata', {}) or {}).get('Properties', '')]
@staticmethod
def list_volumes():
+ conn = OpenStack().conn
ownedby = "ownedby='" + teuth_config.openstack['ip'] + "'"
- all = json.loads(OpenStack().run("volume list -f json --long"))
+ volumes = conn.block_storage.volumes()
def select(volume):
- return (ownedby in volume['Properties'] and
- volume['Display Name'].startswith('target'))
- return filter(select, all)
+ props = volume.metadata or {}
+ return (ownedby in props.get('Properties', '') and
+ props.get('display_name', '').startswith('target'))
+ return filter(select, volumes)
def cloud_init_wait(self, instance):
"""
with safe_while(sleep=30, tries=30,
action="cloud_init_wait " + ip) as proceed:
success = False
- # CentOS 6.6 logs in /var/log/clout-init-output.log
- # CentOS 7.0 logs in /var/log/clout-init.log
tail = ("tail --follow=name --retry"
" /var/log/cloud-init*.log /tmp/init.out")
while proceed():
try:
+ log.debug("Attempting to connect to instance at IP: " + ip)
client = connection.connect(**client_args)
except paramiko.PasswordRequiredException:
raise Exception(
break
except socket.timeout:
client.close()
- log.debug('cloud_init_wait socket.timeout ' + tail)
continue
- except socket.error as e:
+ except socket.error:
client.close()
- log.debug('cloud_init_wait socket.error ' + str(e) + ' ' + tail)
continue
- client.close()
+ finally:
+ client.close()
if success:
+ log.debug('Cloud-init completed successfully for IP: ' + ip)
break
+ if not success:
+ log.debug('Cloud-init did not complete successfully within the given retries.')
return success
def get_ip(self, instance_id, network):
if ceph_repo:
command = (
"perl -pi -e 's|.*{opt}.*|{opt}: {value}|'"
- " ~/.teuthology.yaml"
+ " ~/.teuthology.yaml || true"
).format(opt='ceph_git_url', value=ceph_repo)
self.ssh(command)
user_home = '/home/' + self.username
" --machine-type openstack " +
" ".join(map(lambda x: "'" + x + "'", argv))
)
+ log.info("Running teuthology-suite: " + command)
return self.ssh(command)
def reminders(self):
logging.getLogger("paramiko.transport").setLevel(logging.DEBUG)
teuthology.log.setLevel(loglevel)
- def ssh(self, command):
+ def ssh(self, command, timeout=300):
"""
Run a command in the OpenStack instance of the teuthology cluster.
Return the stdout / stderr of the command.
# get the I/O channel to iterate line by line
transport = client.get_transport()
channel = transport.open_session()
- channel.get_pty()
- channel.settimeout(900)
- output = channel.makefile('r', 1)
- log.debug(":ssh@" + ip + ":" + command)
+ channel.settimeout(timeout)
+ log.debug(f"ssh {self.instance.get_floating_ip_or_ip()}: {command}")
channel.exec_command(command)
- for line in iter(output.readline, b''):
- log.info(line.strip())
- return channel.recv_exit_status()
+ stdout, stderr = [], []
+ start_time = time.time()
+ while True:
+ if channel.recv_ready():
+ stdout.append(channel.recv(4096).decode())
+ if channel.recv_stderr_ready():
+ stderr.append(channel.recv_stderr(4096).decode())
+ if channel.exit_status_ready():
+ break
+ if time.time() - start_time > timeout:
+ raise TimeoutError("SSH command timed out!")
+ time.sleep(0.1) # Small sleep to avoid busy waiting
+ exit_status = channel.recv_exit_status()
+ stdout_txt, stderr_txt = ''.join(stdout), ''.join(stderr)
+ if exit_status != 0:
+ log.warning(f"SSH command failed with exit status {exit_status}")
+ return exit_status, stdout_txt, stderr_txt
+
def verify_openstack(self):
"""
fd, path = tempfile.mkstemp()
os.close(fd)
- with open(os.path.dirname(__file__) + '/bootstrap-teuthology.sh', 'rb') as f:
+ bootstrap_path = os.getcwd() + "/teuthology/openstack" + '/bootstrap-teuthology.sh'
+ with open(bootstrap_path, 'rb') as f:
b64_bootstrap = base64.b64encode(f.read())
bootstrap_content = str(b64_bootstrap.decode())
openrc_sh = ''
cacert_cmd = None
- for (var, value) in os.environ.items():
- if var in ('OS_TOKEN_VALUE', 'OS_TOKEN_EXPIRES'):
- continue
- if var == 'OS_CACERT':
- cacert_path = '/home/%s/.openstack.crt' % self.username
- cacert_file = value
- openrc_sh += 'export %s=%s\n' % (var, cacert_path)
- cacert_cmd = (
- "su - -c 'cat > {path}' {user} <<EOF\n"
- "{data}\n"
- "EOF\n").format(
- path=cacert_path,
- user=self.username,
- data=open(cacert_file).read())
- elif var.startswith('OS_'):
- openrc_sh += 'export %s=%s\n' % (var, value)
+ clouds_yaml_path = os.path.expanduser('~/.config/openstack/clouds.yaml')
+ if os.path.exists(clouds_yaml_path):
+ log.debug(f"clouds.yaml found at {clouds_yaml_path}, processing for openrc.sh")
+ with open(clouds_yaml_path, 'r') as f:
+ clouds_data = yaml.safe_load(f)
+ cloud_name = os.environ.get('OS_CLOUD', 'default')
+ cloud_config = clouds_data.get('clouds', {}).get(cloud_name, {})
+ if not cloud_config:
+ raise Exception(f"Cloud '{cloud_name}' not found in clouds.yaml")
+ auth = cloud_config.get('auth', {})
+ for key, value in {**auth, **cloud_config}.items():
+ if isinstance(value, str):
+ openrc_sh += f"export OS_{key.upper()}={value}\n"
+ else:
+ for (var, value) in os.environ.items():
+ if var in ('OS_TOKEN_VALUE', 'OS_TOKEN_EXPIRES'):
+ continue
+ if var == 'OS_CACERT':
+ cacert_path = '/home/%s/.openstack.crt' % self.username
+ cacert_file = value
+ openrc_sh += 'export %s=%s\n' % (var, cacert_path)
+ cacert_cmd = (
+ "su - -c 'cat > {path}' {user} <<EOF\n"
+ "{data}\n"
+ "EOF\n").format(
+ path=cacert_path,
+ user=self.username,
+ data=open(cacert_file).read())
+ elif var.startswith('OS_'):
+ openrc_sh += 'export %s=%s\n' % (var, value)
b64_openrc_sh = base64.b64encode(openrc_sh.encode())
openrc_sh_content = str(b64_openrc_sh.decode())
network = OpenStack().get_network()
+ log.debug(f"Network to be used: {network}")
ceph_workbench = ''
if self.args.ceph_workbench_git_url:
ceph_workbench += (" --ceph-workbench-branch " +
'--server-group %s' % self.server_group(),
'--worker-group %s' % self.worker_group(),
'--package-repo %s' % self.packages_repository(),
- #'--setup-all',
]
+ log.debug(f"Setup options: {setup_options}")
+
all_options = [
- '--install', #do_install_packages=true
- #'--setup-ceph-workbench', #do_ceph_workbench=true
- '--config', #do_create_config=true
- '--setup-keypair', #do_setup_keypair=true
- #'', #do_apt_get_update=true
- '--setup-docker', #do_setup_docker=true
- '--setup-salt-master', #do_setup_salt_master=true
- '--setup-dnsmasq', #do_setup_dnsmasq=true
- '--setup-fail2ban', #do_setup_fail2ban=true
- '--setup-paddles', #do_setup_paddles=true
- '--setup-pulpito', #do_setup_pulpito=true
- '--populate-paddles', #do_populate_paddles=true
+ '--install',
+ '--config',
+ '--setup-docker',
+ '--setup-salt-master',
+ '--setup-dnsmasq',
+ '--setup-fail2ban',
+ '--setup-paddles',
+ '--setup-pulpito',
+ '--populate-paddles',
]
if self.args.ceph_workbench_git_url:
"{user} >> /tmp/init.out "
"2>&1".format(user=self.username,
opts=' '.join(setup_options + all_options))),
- # wa: we want to stop paddles and pulpito started by
- # setup-openstack before starting teuthology service
"pkill -f 'pecan serve'",
"pkill -f 'python run.py'",
"systemctl enable teuthology",
'name': self.username
}
},
- 'packages': [
- 'python-virtualenv',
- 'git',
- 'rsync',
- ],
'write_files': [
{
'path': '/tmp/bootstrap-teuthology.sh',
'permissions': '0644',
}
],
- 'runcmd': cmds,
+ 'runcmd': [
+ 'apt-get update && apt-get install -y python3-virtualenv git rsync >> /tmp/init.out 2>&1'
+ ] + cmds,
'final_message': 'teuthology is up and running after $UPTIME seconds'
}
user_data = "#cloud-config\n%s" % \
return "teuth-%s-worker" % self.args.name
def create_security_group(self):
- """
- Create a security group that will be used by all teuthology
- created instances. This should not be necessary in most cases
- but some OpenStack providers enforce firewall restrictions even
- among instances created within the same tenant.
- """
- groups = misc.sh('openstack security group list -c Name -f value').split('\n')
- if all(g in groups for g in [self.server_group(), self.worker_group()]):
- return
- misc.sh("""
-openstack security group delete {server} || true
-openstack security group delete {worker} || true
-openstack security group create {server}
-openstack security group create {worker}
-# access to teuthology VM from the outside
-openstack security group rule create --proto tcp --dst-port 22 {server} # ssh
-openstack security group rule create --proto tcp --dst-port 80 {server} # for log access
-openstack security group rule create --proto tcp --dst-port 8080 {server} # pulpito
-openstack security group rule create --proto tcp --dst-port 8081 {server} # paddles
-# access between teuthology and workers
-openstack security group rule create --src-group {worker} --dst-port 1:65535 {server}
-openstack security group rule create --protocol udp --src-group {worker} --dst-port 1:65535 {server}
-openstack security group rule create --src-group {server} --dst-port 1:65535 {worker}
-openstack security group rule create --protocol udp --src-group {server} --dst-port 1:65535 {worker}
-# access between members of one group
-openstack security group rule create --src-group {worker} --dst-port 1:65535 {worker}
-openstack security group rule create --protocol udp --src-group {worker} --dst-port 1:65535 {worker}
-openstack security group rule create --src-group {server} --dst-port 1:65535 {server}
-openstack security group rule create --protocol udp --src-group {server} --dst-port 1:65535 {server}
- """.format(server=self.server_group(), worker=self.worker_group()))
+ conn = OpenStack().conn
+ server_sg = conn.network.find_security_group(self.server_group())
+ worker_sg = conn.network.find_security_group(self.worker_group())
+ if not server_sg:
+ server_sg = conn.network.create_security_group(name=self.server_group())
+ if not worker_sg:
+ worker_sg = conn.network.create_security_group(name=self.worker_group())
+ def add_rule(sg_id, protocol, port, remote_group_id=None):
+ rule_args = {
+ 'security_group_id': sg_id,
+ 'direction': 'ingress',
+ 'protocol': protocol,
+ 'port_range_min': port,
+ 'port_range_max': port,
+ 'ethertype': 'IPv4',
+ }
+ if remote_group_id:
+ rule_args['remote_group_id'] = remote_group_id
+ else:
+ rule_args['remote_ip_prefix'] = '0.0.0.0/0'
+ try:
+ conn.network.create_security_group_rule(**rule_args)
+ except Exception as e:
+ log.warning(f"Security group rule creation skipped or failed: {e}")
+ # Rules for SSH, log, pulpito and paddles
+ for port in (22, 80, 8080, 8081):
+ add_rule(server_sg.id, 'tcp', port)
+ # access between teuthology and workers
+ for port in (65535,):
+ add_rule(worker_sg.id, 'udp', port, remote_group_id=server_sg.id)
+ add_rule(server_sg.id, 'udp', port, remote_group_id=worker_sg.id)
+ # access between members of one group
+ add_rule(server_sg.id, 'udp', 65535, remote_group_id=server_sg.id)
+ # access within worker group
+ add_rule(worker_sg.id, 'udp', 65535, remote_group_id=worker_sg.id)
@staticmethod
def get_unassociated_floating_ip():
@staticmethod
def create_floating_ip():
- try:
- pools = json.loads(OpenStack().run("ip floating pool list -f json"))
- except subprocess.CalledProcessError as e:
- if 'Floating ip pool operations are only available for Compute v2 network.' \
- in e.output:
- log.debug(e.output)
- log.debug('Trying newer API than Compute v2')
- try:
- network = 'floating'
- ip = json.loads(misc.sh("openstack --quiet floating ip create -f json '%s'" % network))
- return ip['floating_ip_address']
- except subprocess.CalledProcessError:
- log.debug("Can't create floating ip for network '%s'" % network)
-
- log.debug("create_floating_ip: ip floating pool list failed")
- return None
- if not pools:
+ conn = OpenStack().conn
+ network_name = 'floating'
+ network = conn.network.find_network(network_name)
+ if not network:
+ log.debug(f"Floating network {network_name} not found.")
return None
- pool = pools[0]['Name']
- try:
- ip = json.loads(OpenStack().run(
- "ip floating create -f json '" + pool + "'"))
- return ip['ip']
- except subprocess.CalledProcessError:
- log.debug("create_floating_ip: not creating a floating ip")
- return None
+ floating_ip = conn.network.create_ip(floating_network_id=network.id)
+ return floating_ip.floating_ip_address
@staticmethod
def associate_floating_ip(name_or_id):
Associate a floating IP to the OpenStack instance
or do nothing if no floating ip can be created.
"""
- ip = TeuthologyOpenStack.get_unassociated_floating_ip()
- if not ip:
- ip = TeuthologyOpenStack.create_floating_ip()
- if ip:
- OpenStack().run("ip floating add " + ip + " " + name_or_id)
+ conn = OpenStack().conn
+ server = conn.compute.find_server(name_or_id)
+ ip_address = TeuthologyOpenStack.get_unassociated_floating_ip()
+ if not ip_address:
+ ip_address = TeuthologyOpenStack.create_floating_ip()
+ if ip_address:
+ conn.compute.add_floating_ip_to_server(server, ip_address)
@staticmethod
def get_os_floating_ips():
- try:
- ips = json.loads(OpenStack().run("ip floating list -f json"))
- except subprocess.CalledProcessError as e:
- log.warning(e)
- if e.returncode == 1:
- return []
- else:
- raise e
- return ips
+ conn = OpenStack().conn
+ return list(conn.network.ips())
@staticmethod
def get_floating_ip_id(ip):
"""
Remove the floating ip from instance_id and delete it.
"""
- ip = OpenStackInstance(instance_id).get_floating_ip()
- if not ip:
+ conn = OpenStack().conn
+ server = conn.compute.find_server(instance_id)
+ if not server:
+ return
+ ip_address = OpenStackInstance(instance_id).get_floating_ip()
+ if not ip_address:
return
- OpenStack().run("ip floating remove " + ip + " " + instance_id)
- ip_id = TeuthologyOpenStack.get_floating_ip_id(ip)
- OpenStack().run("ip floating delete " + ip_id)
+ conn.compute.remove_floating_ip_from_server(server, ip_address)
+ floating_ip_obj = conn.network.find_ip(ip_address)
+ if floating_ip_obj:
+ conn.network.delete_ip(floating_ip_obj)
def create_cluster(self):
user_data = self.get_user_data()
if not key_name:
raise Exception('No key name provided, use --key-name option')
log.debug('Using key name: %s' % self.args.key_name)
- self.run(
+ image_name = self.image('ubuntu', '22.04', arch)
+ log.debug("Using image: %s" % image_name)
+ net_config = self.net()
+ try:
+ self.run(
"server create " +
- " --image '" + self.image('ubuntu', '16.04', arch) + "' " +
+ " --image '" + image_name + "' " +
" --flavor '" + flavor + "' " +
- " " + self.net() +
+ " " + net_config +
" --key-name " + key_name +
" --user-data " + user_data +
security_group +
" --wait " + self.server_name() +
" -f json")
- os.unlink(user_data)
+ except Exception as e:
+ log.error("Error during server creation: %s" % str(e))
+ raise
+ finally:
+ os.unlink(user_data)
self.instance = OpenStackInstance(self.server_name())
+ log.debug("OpenStackInstance created for server: %s" % self.server_name())
self.associate_floating_ip(self.instance['id'])
+ log.debug("Floating IP associated for instance ID: %s" % self.instance.get('id'))
return self.cloud_init_wait(self.instance)
def packages_repository(self):
local server_group="${11}"
local worker_group="${12}"
local package_repo="${13}"
+ local teuthology_branch="${14}"
+ local teuthology_git_url="${15}"
if test "$network" ; then
network="network: $network"
lab_domain: $labdomain
max_job_time: 32400 # 9 hours
teuthology_path: .
+teuthology_branch: $teuthology_branch
+teuthology_git_url: $teuthology_git_url
canonical_tags: $canonical_tags
openstack:
clone: git clone http://github.com/ceph/teuthology
}
function setup_docker() {
- if test -f /etc/apt/sources.list.d/docker.list ; then
- echo "OK docker is installed"
- else
- sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
- echo deb https://apt.dockerproject.org/repo ubuntu-trusty main | sudo tee -a /etc/apt/sources.list.d/docker.list
- sudo apt-get -qq install -y docker-engine
+ source /etc/os-release
+ if ! $VERSION_CODENAME; then
+ echo "ERROR: VERSION_CODENAME is not set. Cannot proceed with Docker installation."
+ return
+ fi
+ if !command -v docker &> /dev/null; then
+ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
+ sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
+ echo \
+ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
+ https://download.docker.com/linux/ubuntu $VERSION_CODENAME stable" | \
+ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+ sudo apt-get update
+ sudo apt-get install -y docker-ce docker-ce-cli containerd.io
echo "INSTALLED docker"
+ else
+ echo "OK docker is installed"
fi
}
git clone https://github.com/ceph/paddles.git $paddles_dir || return 1
fi
- sudo apt-get -qq install -y --force-yes beanstalkd postgresql postgresql-contrib postgresql-server-dev-all supervisor
+ sudo apt-get -qq install -y beanstalkd postgresql postgresql-contrib postgresql-server-dev-all supervisor
if ! sudo /etc/init.d/postgresql status ; then
sudo mkdir -p /etc/postgresql
cd $paddles_dir || return 1
git pull --rebase
git clean -ffqdx
- sed -e "s|^address.*|address = 'http://localhost'|" \
- -e "s|^job_log_href_templ = 'http://qa-proxy.ceph.com/teuthology|job_log_href_templ = 'http://$public_ip|" \
+ sed -e "/^address = os.environ.get(/,/^)/c\address = os.environ.get('PADDLES_ADDRESS', 'http://localhost')" \
+ -e "s|^job_log_href_templ = os.environ.get(.*)|job_log_href_templ = os.environ.get('PADDLES_JOB_LOG_HREF_TEMPL', 'http://$public_ip')|" \
-e "/sqlite/d" \
- -e "s|.*'postgresql+psycop.*'|'url': 'postgresql://paddles:paddles@localhost/paddles'|" \
+ -e "s|^ *'url':.*|'url': 'postgresql://paddles:paddles@localhost/paddles',|" \
-e "s/'host': '127.0.0.1'/'host': '0.0.0.0'/" \
< config.py.in > config.py
virtualenv ./virtualenv
git clone https://github.com/ceph/pulpito.git $pulpito_dir || return 1
fi
- sudo apt-get -qq install -y --force-yes nginx
+ sudo apt-get -qq install -y nginx
local nginx_conf=/etc/nginx/sites-available/default
sudo sed -i '/text\/plain/a\ text\/plain log;' \
/etc/nginx/mime.types
virtualenv ./virtualenv
source ./virtualenv/bin/activate
pip install --upgrade pip
- pip install 'setuptools==18.2.0'
+ pip install 'setuptools>=58.0.0'
pip install -r requirements.txt
python run.py &
)
if ! test -f /etc/dnsmasq.d/resolv ; then
resolver=$(grep nameserver /etc/resolv.conf | head -1 | perl -ne 'print $1 if(/\s*nameserver\s+([\d\.]+)/)')
- sudo apt-get -qq install -y --force-yes dnsmasq resolvconf
+ sudo apt-get -qq install -y dnsmasq resolvconf
# FIXME: this opens up dnsmasq to DNS reflection/amplification attacks, and can be reverted
# FIXME: once we figure out how to configure dnsmasq to accept DNS queries from all subnets
sudo perl -pi -e 's/--local-service//' /etc/init.d/dnsmasq
}
function install_packages() {
-
- if ! test -f /etc/apt/sources.list.d/trusty-backports.list ; then
- echo deb http://archive.ubuntu.com/ubuntu trusty-backports main universe | sudo tee /etc/apt/sources.list.d/trusty-backports.list
+ source /etc/os-release
+ if ! $VERSION_CODENAME; then
+ echo "ERROR: VERSION_CODENAME is not set. Cannot proceed with Docker installation."
+ return
+ fi
+ local codename=$VERSION_CODENAME
+ local backports_file="/etc/apt/sources.list.d/${codename}-backports.list"
+ if [ ! -f "$backports_file" ]; then
+ echo "Adding backports repo for $codename..."
+ echo "deb http://archive.ubuntu.com/ubuntu ${codename}-backports main universe" | sudo tee "$backports_file"
sudo apt-get update
fi
-
- local packages="jq realpath curl"
- sudo apt-get -qq install -y --force-yes $packages
-
+ local packages="jq curl"
+ sudo apt-get -qq install -y $packages
echo "INSTALL required packages $packages"
}
CAT=${CAT:-cat}
function verify_openstack() {
- if ! openstack server list > /dev/null ; then
+ if ! openstack server list --format json > /dev/null ; then
echo ERROR: the credentials from ~/openrc.sh are not working >&2
return 1
fi
# assume the first available IPv4 subnet is going to be used to assign IP to the instance
#
[ -z "$network" ] && {
- local default_subnets=$(openstack subnet list --ip-version 4 -f json | jq -r '.[] | .Subnet' | sort | uniq)
+ local default_subnets=$(openstack subnet list --ip-version 4 -f json | jq -r '.[] | select(.Name != null) | .Subnet' | sort | uniq)
} || {
local network_id=$(openstack network list -f json | jq -r ".[] | select(.Name == \"$network\") | .ID")
local default_subnets=$(openstack subnet list --ip-version 4 -f json \
| jq -r ".[] | select(.Network == \"$network_id\") | .Subnet" | sort | uniq)
}
subnets=$(echo $subnets $default_subnets)
+ echo "subnets: $subnets"
case $provider in
entercloudsuite)
esac
local ip
- for dev in eth0 ens3 ; do
- ip=$(ip a show dev $dev 2>/dev/null | sed -n "s:.*inet \(.*\)/.*:\1:p")
- test "$ip" && break
+ for dev in $(ip -o link show | awk -F': ' '{print $2}' | grep -v '^lo$'); do
+ ip=$(ip -4 addr show dev "$dev" | awk '/inet / {print $2}' | cut -d/ -f1)
+ if [ -n "$ip" ]; then
+ nameserver="$ip"
+ break
+ fi
done
- : ${nameserver:=$ip}
+
+ local teuthology_branch="$(git -C $(dirname $0)/../../../teuthology rev-parse --abbrev-ref HEAD)"
+ local teuthology_git_url="$(git -C $(dirname $0)/../../../teuthology config --get remote.origin.url)"
if $do_create_config ; then
create_config "$network" "$subnets" "$nameserver" "$labdomain" "$ip" \
"$archive_upload" "$canonical_tags" "$selfname" "$keypair" \
- "$server_name" "$server_group" "$worker_group" "$package_repo" || return 1
+ "$server_name" "$server_group" "$worker_group" "$package_repo" "$teuthology_branch" "$teuthology_git_url" || return 1
setup_ansible "$subnets" $labdomain || return 1
setup_ssh_config || return 1
setup_authorized_keys || return 1