Bootable volume support (bump)

Change-Id: I2989587204766dc1dfc51979608a691578d1f660
This commit is contained in:
mklyus 2020-11-17 18:04:35 +03:00 committed by Max Klyus
parent 34a82c9f35
commit 5ac228a575
6 changed files with 98 additions and 11 deletions

11
.zuul.yaml Normal file
View File

@ -0,0 +1,11 @@
- project:
check:
jobs:
- openstack-tox-pep8
- openstack-tox-py36
- openstack-tox-docs
gate:
jobs:
- openstack-tox-pep8
- openstack-tox-py36
- openstack-tox-docs

View File

@ -7,11 +7,11 @@ hacking<1.2.0,>=1.1.0 # Apache-2.0
coverage!=4.4 # Apache-2.0 coverage!=4.4 # Apache-2.0
discover discover
python-subunit # Apache-2.0/BSD python-subunit # Apache-2.0/BSD
sphinx!=1.6.6,!=1.6.7,<2.0.0;python_version=='2.7' # BSD
sphinx!=1.6.6,!=1.6.7;python_version>='3.4' # BSD sphinx!=1.6.6,!=1.6.7;python_version>='3.4' # BSD
sphinx-rtd-theme>=0.1.9 sphinx-rtd-theme>=0.1.9
oslosphinx # Apache-2.0 oslosphinx # Apache-2.0
oslotest # Apache-2.0 oslotest # Apache-2.0
pyflakes==2.1.1
testrepository # Apache-2.0/BSD testrepository # Apache-2.0/BSD
testscenarios # Apache-2.0/BSD testscenarios # Apache-2.0/BSD
testtools # MIT testtools # MIT

View File

@ -149,16 +149,21 @@ class Compute(object):
# and check that it gets into the ACTIVE state # and check that it gets into the ACTIVE state
def create_server(self, vmname, image, flavor, key_name, def create_server(self, vmname, image, flavor, key_name,
nic, sec_group, avail_zone=None, user_data=None, nic, sec_group, avail_zone=None, user_data=None,
config_drive=None, files=None, retry_count=10): config_drive=None, files=None, retry_count=10, volume=None):
if sec_group: if sec_group:
security_groups = [sec_group["name"]] security_groups = [sec_group["name"]]
else: else:
security_groups = None security_groups = None
# Also attach the created security group for the test # Also attach the created security group for the test
vol_map = None
if volume:
vol_map = [{"source_type": "volume", "boot_index": "0",
"uuid": volume.id, "destination_type": "volume"}]
image = None
instance = self.novaclient.servers.create(name=vmname, instance = self.novaclient.servers.create(name=vmname,
image=image, image=image,
block_device_mapping_v2=vol_map,
flavor=flavor, flavor=flavor,
key_name=key_name, key_name=key_name,
nics=nic, nics=nic,
@ -363,7 +368,7 @@ class Compute(object):
if len(avail_list) == 2: if len(avail_list) == 2:
break break
LOG.info('Using hypervisors ' + ', '.join(avail_list)) LOG.info('Using hypervisors ' + ', '.join(avail_list))
else: if len(avail_list) < 2:
for host in host_list: for host in host_list:
# this host must be a compute node # this host must be a compute node
if host.binary != 'nova-compute' or host.state != 'up' or host.status != 'enabled': if host.binary != 'nova-compute' or host.state != 'up' or host.status != 'enabled':
@ -374,7 +379,7 @@ class Compute(object):
candidate = self.normalize_az_host(None, host.host) candidate = self.normalize_az_host(None, host.host)
else: else:
candidate = self.normalize_az_host(host.zone, host.host) candidate = self.normalize_az_host(host.zone, host.host)
if candidate: if candidate and candidate not in avail_list:
avail_list.append(candidate) avail_list.append(candidate)
# pick first 2 matches at most # pick first 2 matches at most
if len(avail_list) == 2: if len(avail_list) == 2:

View File

@ -146,7 +146,8 @@ class Instance(object):
az, az,
internal_network_name, internal_network_name,
sec_group, sec_group,
init_file_name=None): init_file_name=None,
volume=None):
# if ssh is created it means this is a native host not a vm # if ssh is created it means this is a native host not a vm
if self.ssh: if self.ssh:
return True return True
@ -185,7 +186,8 @@ class Instance(object):
user_data, user_data,
self.config_drive, self.config_drive,
files, files,
self.config.generic_retry_count) self.config.generic_retry_count,
volume)
if user_data: if user_data:
user_data.close() user_data.close()
if not self.instance: if not self.instance:

View File

@ -36,7 +36,7 @@ class PerfInstance(Instance):
ssh_access=None, nics=None, az=None, ssh_access=None, nics=None, az=None,
management_network_name=None, management_network_name=None,
sec_group=None, sec_group=None,
init_file_name=None): init_file_name=None, volume=None):
'''Create an instance '''Create an instance
:return: True on success, False on error :return: True on success, False on error
''' '''
@ -44,7 +44,7 @@ class PerfInstance(Instance):
nics, az, nics, az,
management_network_name, management_network_name,
sec_group, sec_group,
init_file_name) init_file_name, volume)
if not rc: if not rc:
return False return False
if self.tp_tool and not self.tp_tool.install(): if self.tp_tool and not self.tp_tool.install():

View File

@ -24,6 +24,7 @@ import pprint
import re import re
import sys import sys
import traceback import traceback
import time
from .__init__ import __version__ from .__init__ import __version__
from . import compute from . import compute
@ -31,8 +32,11 @@ from .config import config_load
from .config import config_loads from .config import config_loads
from . import credentials from . import credentials
from .fluentd import FluentLogHandler from .fluentd import FluentLogHandler
import cinderclient.exceptions as cinder_exception
from cinderclient.v2 import client as cinderclient
from glanceclient.v2 import client as glanceclient from glanceclient.v2 import client as glanceclient
from . import iperf_tool from . import iperf_tool
import keystoneauth1
from keystoneclient import client as keystoneclient from keystoneclient import client as keystoneclient
from .log import CONLOG from .log import CONLOG
from .log import FILELOG from .log import FILELOG
@ -159,6 +163,14 @@ class VmtpTest(object):
def create_instance(self, inst, az, int_net): def create_instance(self, inst, az, int_net):
fn = self.config.user_data_file fn = self.config.user_data_file
user_data_file = fn if fn and os.path.isfile(fn) else None user_data_file = fn if fn and os.path.isfile(fn) else None
volume = None
if self.config.volume:
volume = self.create_volume(self.cinder_client,
self.image_instance,
self.config.image_name + "_" +
inst.name + "_" + time.strftime('%d%m_%H%M%S'),
self.config.volume)
self.assert_true(inst.create(self.image_instance, self.assert_true(inst.create(self.image_instance,
self.flavor_type, self.flavor_type,
self.instance_access, self.instance_access,
@ -166,12 +178,49 @@ class VmtpTest(object):
az, az,
int_net['name'], int_net['name'],
self.sec_group, self.sec_group,
init_file_name=user_data_file)) init_file_name=user_data_file,
volume=volume))
def assert_true(self, cond): def assert_true(self, cond):
if not cond: if not cond:
raise VmtpException('Assert failure') raise VmtpException('Assert failure')
def create_volume(self, cinder_client, image, vol_name, vol_size):
LOG.info('Creating new volume: {}'.format(vol_name))
retry = 0
retry_count = 60
volume = cinder_client.volumes.create(name=str(vol_name), size=vol_size, imageRef=image.id)
while volume.status in ['creating', 'downloading'] and retry < retry_count:
try:
volume = cinder_client.volumes.find(name=volume.name)
except (cinder_exception.NotFound, keystoneauth1.exceptions.http.NotFound):
pass
retry = retry + 1
LOG.debug("Volume not yet active, retrying %s of %s...", retry, retry_count)
time.sleep(2)
if volume.status != 'available':
raise Exception
return volume
def delete_volumes(self, cinder_client, volumes):
if isinstance(volumes, str):
volumes = [volumes]
for volume in volumes:
LOG.info('Deleting volume: {}'.format(volume.name))
cinder_client.volumes.delete(volume)
def find_volumes(self, cinder_client, volume_name):
res_vol = []
try:
req_vols = cinder_client.volumes.list()
for vol in req_vols:
if volume_name in vol.name:
res_vol.append(vol)
return res_vol
except (cinder_exception.NotFound, keystoneauth1.exceptions.http.NotFound):
pass
return None
def setup(self): def setup(self):
# This is a template host access that will be used for all instances # This is a template host access that will be used for all instances
# (the only specific field specific to each instance is the host IP) # (the only specific field specific to each instance is the host IP)
@ -208,9 +257,16 @@ class VmtpTest(object):
nova_client = novaclient.Client('2', session=sess) nova_client = novaclient.Client('2', session=sess)
neutron = neutronclient.Client('2.0', session=sess) neutron = neutronclient.Client('2.0', session=sess)
self.glance_client = glanceclient.Client('2', session=sess) self.glance_client = glanceclient.Client('2', session=sess)
self.cinder_client = cinderclient.Client('2', session=sess) \
if self.config.volume else None
self.comp = compute.Compute(nova_client, neutron, self.config) self.comp = compute.Compute(nova_client, neutron, self.config)
if self.config.volume:
volumes = self.find_volumes(self.cinder_client, self.config.image_name)
if volumes:
LOG.info("Removing old VMTP volumes: {}".format(volumes))
self.delete_volumes(self.cinder_client, volumes)
# Add the appropriate public key to openstack # Add the appropriate public key to openstack
self.comp.init_key_pair(self.config.public_key_name, self.instance_access) self.comp.init_key_pair(self.config.public_key_name, self.instance_access)
@ -295,6 +351,7 @@ class VmtpTest(object):
# avail_list = self.comp.list_hypervisor(config.availability_zone) # avail_list = self.comp.list_hypervisor(config.availability_zone)
avail_list = self.comp.get_az_host_list() avail_list = self.comp.get_az_host_list()
if not avail_list: if not avail_list:
self.teardown()
sys.exit(5) sys.exit(5)
# compute the list of client vm placements to run # compute the list of client vm placements to run
@ -452,6 +509,11 @@ class VmtpTest(object):
if not self.config.reuse_existing_vm and self.net: if not self.config.reuse_existing_vm and self.net:
self.net.dispose() self.net.dispose()
# Remove the public key # Remove the public key
if self.config.volume:
volumes = self.find_volumes(self.cinder_client, self.config.image_name)
if volumes:
LOG.info("Removing VMTP volumes: {}".format(volumes))
self.delete_volumes(self.cinder_client, volumes)
if self.comp: if self.comp:
self.comp.remove_public_key(self.config.public_key_name) self.comp.remove_public_key(self.config.public_key_name)
# Finally remove the security group # Finally remove the security group
@ -619,7 +681,7 @@ def gen_report_data(proto, result):
if proto in ['TCP', 'Upload', 'Download']: if proto in ['TCP', 'Upload', 'Download']:
for key in list(retval.keys()): for key in list(retval.keys()):
if retval[key]: if retval[key]:
retval[key] = '{0:n}'.format(retval[key] / tcp_test_count) retval[key] = '{0:n}'.format(int(retval[key] / tcp_test_count))
else: else:
retval.pop(key) retval.pop(key)
@ -969,6 +1031,12 @@ def parse_opts_from_cli():
help='Filename for saving VMTP logs', help='Filename for saving VMTP logs',
metavar='<log_file>') metavar='<log_file>')
parser.add_argument('--volume', dest='volume',
default=None,
action='store',
help='create bootable volumes for instances',
metavar='<volume>')
return parser.parse_known_args()[0] return parser.parse_known_args()[0]
@ -1012,6 +1080,7 @@ def merge_opts_to_configs(opts):
config.keep_first_flow_and_exit = opts.keep_first_flow_and_exit config.keep_first_flow_and_exit = opts.keep_first_flow_and_exit
config.inter_node_only = opts.inter_node_only config.inter_node_only = opts.inter_node_only
config.same_network_only = opts.same_network_only config.same_network_only = opts.same_network_only
config.volume = opts.volume
if config.public_key_file and not os.path.isfile(config.public_key_file): if config.public_key_file and not os.path.isfile(config.public_key_file):
LOG.warning('Invalid public_key_file:' + config.public_key_file) LOG.warning('Invalid public_key_file:' + config.public_key_file)