def get_ip_neutron(instance_id): subnets = json.loads(misc.sh("neutron subnet-list -f json -c id -c ip_version")) subnet_id = None for subnet in subnets: if subnet['ip_version'] == 4: subnet_id = subnet['id'] break if not subnet_id: raise Exception("no subnet with ip_version == 4") ports = json.loads(misc.sh("neutron port-list -f json -c fixed_ips -c device_id")) fixed_ips = None for port in ports: if port['device_id'] == instance_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'] == subnet_id: ip = record['ip_address'] break if not ip: raise Exception("no ip") return ip
def push_directory(path, remote, remote_dir): """ local_temp_path=`mktemp` tar czf $local_temp_path $path ssh remote mkdir -p remote_dir remote_temp_path=`mktemp` scp $local_temp_path $remote_temp_path rm $local_temp_path tar xzf $remote_temp_path -C $remote_dir ssh remote:$remote_temp_path """ fd, local_temp_path = tempfile.mkstemp(suffix='.tgz', prefix='rebuild_mondb-') os.close(fd) cmd = ' '.join(['tar', 'cz', '-f', local_temp_path, '-C', path, '--', '.']) teuthology.sh(cmd) _, fname = os.path.split(local_temp_path) fd, remote_temp_path = tempfile.mkstemp(suffix='.tgz', prefix='rebuild_mondb-') os.close(fd) remote.put_file(local_temp_path, remote_temp_path) os.remove(local_temp_path) remote.run(args=['sudo', 'tar', 'xz', '-C', remote_dir, '-f', remote_temp_path]) remote.run(args=['sudo', 'rm', '-fr', remote_temp_path])
def get_ip_neutron(self): subnets = json.loads( misc.sh("neutron subnet-list -f json -c id -c ip_version")) subnet_id = None for subnet in subnets: if subnet['ip_version'] == 4: subnet_id = subnet['id'] break if not subnet_id: raise Exception("no subnet with ip_version == 4") ports = json.loads( misc.sh("neutron port-list -f json -c fixed_ips -c device_id")) fixed_ips = None 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'] == subnet_id: ip = record['ip_address'] break if not ip: raise Exception("no ip") return ip
def _push_directory(path, remote, remote_dir): """ local_temp_path=`mktemp` tar czf $local_temp_path $path ssh remote mkdir -p remote_dir remote_temp_path=`mktemp` scp $local_temp_path $remote_temp_path rm $local_temp_path tar xzf $remote_temp_path -C $remote_dir ssh remote:$remote_temp_path """ fd, local_temp_path = tempfile.mkstemp(suffix='.tgz', prefix='rebuild_mondb-') os.close(fd) cmd = ' '.join(['tar', 'cz', '-f', local_temp_path, '-C', path, '--', '.']) teuthology.sh(cmd) _, fname = os.path.split(local_temp_path) fd, remote_temp_path = tempfile.mkstemp(suffix='.tgz', prefix='rebuild_mondb-') os.close(fd) remote.put_file(local_temp_path, remote_temp_path) os.remove(local_temp_path) remote.run( args=['sudo', 'tar', 'xz', '-C', remote_dir, '-f', remote_temp_path]) remote.run(args=['sudo', 'rm', '-fr', remote_temp_path])
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. """ try: self.run("security group show teuthology") return except subprocess.CalledProcessError: pass # TODO(loic): this leaves the teuthology vm very exposed # it would be better to be very liberal for 192.168.0.0/16 # and 172.16.0.0/12 and 10.0.0.0/8 and only allow 80/8081/22 # for the rest. misc.sh( """ openstack security group create teuthology openstack security group rule create --dst-port 1:65535 teuthology openstack security group rule create --proto udp --dst-port 53 teuthology # dns openstack security group rule create --proto udp --dst-port 111 teuthology # for nfs openstack security group rule create --proto udp --dst-port 2049 teuthology # for nfs openstack security group rule create --proto udp --dst-port 16000:65535 teuthology # for nfs """ )
def test_sh_fail(caplog): with pytest.raises(subprocess.CalledProcessError) as excinfo: misc.sh("/bin/echo -n AB ; /bin/echo C ; exit 111", 2) == "ABC\n" assert excinfo.value.returncode == 111 for record in caplog.records: if record.levelname == 'ERROR': assert ('replay full' in record.message or 'ABC\n' == record.message)
def image_create(self, name): """ Upload an image into OpenStack with glance. The image has to be qcow2. """ misc.sh("wget -c -O " + name + ".qcow2 " + self.image2url[name]) misc.sh("glance image-create --property ownedby=teuthology " + " --disk-format=qcow2 --container-format=bare " + " --file " + name + ".qcow2 --name " + self.image_name(name))
def begin(self): misc.sh(""" set -x pip install tox tox # tox -e py27-integration tox -e openstack-integration """)
def teardown(self): """ Delete all instances run by the teuthology cluster and delete the instance running the teuthology cluster. """ self.ssh("sudo /etc/init.d/teuthology stop || true") instance_id = self.get_instance_id(self.args.name) self.delete_floating_ip(instance_id) misc.sh("openstack server delete --wait " + self.args.name)
def test_floating_ip(self): if not self.can_create_floating_ips: pytest.skip('unable to create floating ips') expected = TeuthologyOpenStack.create_floating_ip() ip = TeuthologyOpenStack.get_unassociated_floating_ip() assert expected == ip ip_id = TeuthologyOpenStack.get_floating_ip_id(ip) misc.sh("openstack ip floating delete " + ip_id)
def clobber(self): misc.sh( """ openstack server delete {name} --wait || true openstack keypair delete {key_name} || true """.format( key_name=self.key_name, name=self.name ) )
def test_floating_ip(self): if not self.can_create_floating_ips: pytest.skip('unable to create floating ips') expected = TeuthologyOpenStack.create_floating_ip() ip = TeuthologyOpenStack.get_unassociated_floating_ip() assert expected == ip ip_id = TeuthologyOpenStack.get_floating_ip_id(ip) misc.sh("openstack -q ip floating delete " + ip_id)
def delete_floating_ip(instance_id): """ Remove the floating ip from instance_id and delete it. """ ip = TeuthologyOpenStack.get_floating_ip(instance_id) if not ip: return misc.sh("openstack ip floating remove " + ip + " " + instance_id) ip_id = TeuthologyOpenStack.get_floating_ip_id(ip) misc.sh("openstack ip floating delete " + ip_id)
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: misc.sh("openstack ip floating add " + ip + " " + name_or_id)
def test_sh_progress(caplog): misc.sh("echo AB ; sleep 5 ; /bin/echo C", 2) == "ABC\n" records = caplog.records assert ':sh: ' in records[0].message assert 'AB' == records[1].message assert 'C' == records[2].message # # With a sleep 5 between the first and the second message, # there must be at least 2 seconds between the log record # of the first message and the log record of the second one # assert (records[2].created - records[1].created) > 2
def verify_openstack(self): """ Check there is a working connection to an OpenStack cluster and set the provider data member if it is among those we know already. """ try: misc.sh("openstack flavor list | tail -2") except subprocess.CalledProcessError: log.exception("openstack flavor list") raise Exception("verify openrc.sh has been sourced") self.set_provider()
def verify_openstack(self): """ Check there is a working connection to an OpenStack cluster and set the provider data member if it is among those we know already. """ try: misc.sh("openstack server list") except subprocess.CalledProcessError: log.exception("openstack server list") raise Exception("verify openrc.sh has been sourced") self.set_provider()
def setup(self): self.key_filename = tempfile.mktemp() self.key_name = 'teuthology-test' self.name = 'teuthology-test' self.clobber() misc.sh(""" openstack keypair create {key_name} > {key_filename} chmod 600 {key_filename} """.format(key_filename=self.key_filename, key_name=self.key_name)) self.options = [ '--key-name', self.key_name, '--key-filename', self.key_filename, '--name', self.name, '--verbose' ]
def create_floating_ip(): pools = json.loads(misc.sh("openstack ip floating pool list -f json")) if not pools: return None pool = pools[0]['Name'] try: ip = json.loads(misc.sh( "openstack ip floating create -f json '" + pool + "'")) return TeuthologyOpenStack.get_value(ip, 'ip') except subprocess.CalledProcessError: log.debug("create_floating_ip: not creating a floating ip") pass return None
def test_sh_progress(caplog): misc.sh("echo AB ; sleep 5 ; /bin/echo C", 2) == "ABC\n" records = caplog.records assert ':sh: ' in records[0].message assert 'AB' == records[1].message assert 'C' == records[2].message # # With a sleep 5 between the first and the second message, # there must be at least 2 seconds between the log record # of the first message and the log record of the second one # t1 = datetime.strptime(records[1].asctime.split(',')[0], "%Y-%m-%d %H:%M:%S") t2 = datetime.strptime(records[2].asctime.split(',')[0], "%Y-%m-%d %H:%M:%S") assert (t2 - t1).total_seconds() > 2
def setup(self): self.key_filename = tempfile.mktemp() self.key_name = 'teuthology-test' self.name = 'teuthology-test' self.clobber() misc.sh(""" openstack keypair create {key_name} > {key_filename} chmod 600 {key_filename} """.format(key_filename=self.key_filename, key_name=self.key_name)) self.options = ['--key-name', self.key_name, '--key-filename', self.key_filename, '--name', self.name, '--verbose']
def setup_class(self): if 'OS_AUTH_URL' not in os.environ: pytest.skip('no OS_AUTH_URL environment variable') teuthology.log.setLevel(logging.DEBUG) set_config_attr(argparse.Namespace()) ip = TeuthologyOpenStack.create_floating_ip() if ip: ip_id = TeuthologyOpenStack.get_floating_ip_id(ip) misc.sh("openstack -q ip floating delete " + ip_id) self.can_create_floating_ips = True else: self.can_create_floating_ips = False
def setup_class(self): if 'OS_AUTH_URL' not in os.environ: pytest.skip('no OS_AUTH_URL environment variable') teuthology.log.setLevel(logging.DEBUG) teuthology.misc.read_config(argparse.Namespace()) ip = TeuthologyOpenStack.create_floating_ip() if ip: ip_id = TeuthologyOpenStack.get_floating_ip_id(ip) misc.sh("openstack ip floating delete " + ip_id) self.can_create_floating_ips = True else: self.can_create_floating_ips = False
def _attach_volume(self, volume_id, name): """ Attach volume to OpenStack instance. Try and attach volume to server, wait until volume gets in-use state. """ with safe_while(sleep=20, increment=20, tries=3, action="add volume " + volume_id) as proceed: while proceed(): try: misc.sh("openstack server add volume " + name + " " + volume_id) break except subprocess.CalledProcessError: log.warning("openstack add volume failed unexpectedly; retrying") self._await_volume_status(volume_id, 'in-use')
def create_cluster(self): user_data = self.get_user_data() if self.provider == 'rackspace': security_group = '' else: security_group = " --security-group teuthology" misc.sh("openstack server create " + " --image '" + self.image('ubuntu', '14.04') + "' " + " --flavor '" + self.flavor() + "' " + " " + self.net() + " --key-name " + self.args.key_name + " --user-data " + user_data + security_group + " --wait " + self.args.name + " -f json") os.unlink(user_data) self.instance = OpenStackInstance(self.args.name) self.associate_floating_ip(self.instance['id']) return self.cloud_init_wait(self.instance)
def flavor(self, hint, select): """ Return the smallest flavor that satisfies the desired size. """ flavors_string = misc.sh("openstack flavor list -f json") flavors = json.loads(flavors_string) found = [] for flavor in flavors: if select and not re.match(select, flavor['Name']): continue if (flavor['RAM'] >= hint['ram'] and flavor['VCPUs'] >= hint['cpus'] and flavor['Disk'] >= hint['disk']): found.append(flavor) if not found: raise Exception("openstack flavor list: " + flavors_string + " does not contain a flavor in which" + " the desired " + str(hint) + " can fit") def sort_flavor(a, b): return (a['VCPUs'] - b['VCPUs'] or a['RAM'] - b['RAM'] or a['Disk'] - b['Disk']) sorted_flavor = sorted(found, cmp=sort_flavor) log.debug("sorted flavor = " + str(sorted_flavor)) return sorted_flavor[0]['Name']
def net_id(self, network): """ Return the uuid of the network in OpenStack. """ r = json.loads(misc.sh("openstack network show -f json " + network)) return self.get_value(r, 'id')
def image_exists(self, image): """ Return true if the image exists in OpenStack. """ found = misc.sh("openstack image list -f json --property name='" + self.image_name(image) + "'") return len(json.loads(found)) > 0
def set_info(self): try: self.info = json.loads( misc.sh("openstack server show -f json " + self.name_or_id)) enforce_json_dictionary(self.info) except CalledProcessError: self.info = None
def create_cluster(self): """ Create an OpenStack instance that runs the teuthology cluster and wait for it to come up. """ user_data = self.get_user_data() if self.provider == 'rackspace': security_group = '' else: security_group = " --security-group teuthology" instance = misc.sh( "openstack server create " + " --image '" + self.image('ubuntu', '14.04') + "' " + " --flavor '" + self.flavor() + "' " + " " + self.net() + " --key-name " + self.args.key_name + " --user-data " + user_data + security_group + " --wait " + self.args.name + " -f json") instance_id = self.get_value(json.loads(instance), 'id') os.unlink(user_data) self.associate_floating_ip(instance_id) ip = self.get_floating_ip_or_ip(instance_id) return self.cloud_init_wait(ip)
def cache_token(self): if self.provider != 'ovh': return False if (OpenStack.token is None and os.environ.get('OS_AUTH_TYPE') == 'v2token' and 'OS_TOKEN' in os.environ and 'OS_TOKEN_EXPIRES' in os.environ): log.debug("get token from the environment of the parent process") OpenStack.token = os.environ['OS_TOKEN'] OpenStack.token_expires = int(os.environ['OS_TOKEN_EXPIRES']) if (OpenStack.token_expires is not None and OpenStack.token_expires < time.time()): log.debug("token discarded because it has expired") OpenStack.token = None if OpenStack.token is None: if os.environ.get('OS_AUTH_TYPE') == 'v2token': del os.environ['OS_AUTH_TYPE'] OpenStack.token = misc.sh("openstack -q token issue -c id -f value").strip() os.environ['OS_AUTH_TYPE'] = 'v2token' os.environ['OS_TOKEN'] = OpenStack.token OpenStack.token_expires = int(time.time() + OpenStack.token_cache_duration) os.environ['OS_TOKEN_EXPIRES'] = str(OpenStack.token_expires) log.info("caching OS_TOKEN and setting OS_AUTH_TYPE=v2token " "during %s seconds" % OpenStack.token_cache_duration) return True
def set_info(self): try: info = json.loads( misc.sh("openstack server show -f json " + self.name_or_id)) self.info = dict( map(lambda p: (p['Field'].lower(), p['Value']), info)) except CalledProcessError: self.info = None
def set_info(self): try: info = json.loads( misc.sh("openstack server show -f json " + self.name_or_id)) self.info = dict(map( lambda p: (p['Field'].lower(), p['Value']), info)) except CalledProcessError: self.info = None
def list_volumes(): ownedby = "ownedby='" + teuth_config.openstack['ip'] + "'" all = json.loads(misc.sh( "openstack volume list -f json --long")) def select(volume): return (ownedby in volume['Properties'] and volume['Display Name'].startswith('target')) return filter(select, all)
def openstack_remove_again(): """ Volumes and servers with REMOVE-ME in the name are leftover that failed to be removed. It is not uncommon for a failed removal to succeed later on. """ sh(""" openstack server list --name REMOVE-ME --column ID --format value | xargs --no-run-if-empty --max-args 1 -P20 openstack server delete --wait true """) volumes = json.loads(OpenStack().run("volume list -f json --long")) remove_me = [openstack_volume_id(v) for v in volumes if 'REMOVE-ME' in openstack_volume_name(v)] for i in remove_me: log.info("Trying to remove stale volume %s" % i) openstack_delete_volume(i)
def get_floating_ip(instance_id): """ Return the floating IP of the OpenStack instance_id. """ ips = json.loads(misc.sh("openstack ip floating list -f json")) for ip in ips: if ip['Instance ID'] == instance_id: return ip['IP'] return None
def get_unassociated_floating_ip(): """ Return a floating IP address not associated with an instance or None. """ ips = json.loads(misc.sh("openstack ip floating list -f json")) for ip in ips: if not ip['Instance ID']: return ip['IP'] return None
def get_floating_ip_id(ip): """ Return the id of a floating IP """ results = json.loads(misc.sh("openstack ip floating list -f json")) for result in results: if result['IP'] == ip: return str(result['ID']) return None
def exists(self, name_or_id): """ Return true if the OpenStack name_or_id instance exists, false otherwise. """ servers = json.loads(misc.sh("openstack server list -f json")) for server in servers: if (server['ID'] == name_or_id or server['Name'] == name_or_id): return True return False
def create_cluster(self): user_data = self.get_user_data() if self.provider == 'rackspace': security_group = '' else: security_group = " --security-group teuthology" misc.sh( "openstack server create " + " --image '" + self.image('ubuntu', '14.04') + "' " + " --flavor '" + self.flavor() + "' " + " " + self.net() + " --key-name " + self.args.key_name + " --user-data " + user_data + security_group + " --wait " + self.args.name + " -f json") os.unlink(user_data) self.instance = OpenStackInstance(self.args.name) self.associate_floating_ip(self.instance['id']) return self.cloud_init_wait(self.instance)
def verify_openstack(self): """ Check there is a working connection to an OpenStack cluster and set the provider data member if it is among those we know already. """ try: misc.sh("openstack server list") except subprocess.CalledProcessError: log.exception("openstack server list") raise Exception("verify openrc.sh has been sourced") if 'OS_AUTH_URL' not in os.environ: raise Exception('no OS_AUTH_URL environment variable') providers = (('cloud.ovh.net', 'ovh'), ('entercloudsuite.com', 'entercloudsuite')) self.provider = None for (pattern, provider) in providers: if pattern in os.environ['OS_AUTH_URL']: self.provider = provider break
def show(name_or_id): """ Run "openstack server show -f json <name_or_id>" and return the result. Does not handle exceptions. """ try: return json.loads( misc.sh("openstack server show -f json %s" % name_or_id) ) except CalledProcessError: return False
def archive_upload(ctx, config): """ Upload the archive directory to a designated location """ try: yield finally: upload = ctx.config.get("archive_upload") archive_path = ctx.config.get("archive_path") if upload and archive_path: log.info("Uploading archives ...") upload_key = ctx.config.get("archive_upload_key") if upload_key: ssh = "RSYNC_RSH='ssh -i " + upload_key + "'" else: ssh = "" split_path = archive_path.split("/") split_path.insert(-2, ".") misc.sh(ssh + " rsync -avz --relative /" + os.path.join(*split_path) + " " + upload) else: log.info("Not uploading archives.")