X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=nfvbench%2Fchain_clients.py;h=fa213593ec6bc2e82b086eb6bb4387033ff9bd06;hb=efc678c9d3843dcfd373b5749a88c51228b0b27c;hp=4be050f36d30b283c9b07c827f50306f053dc95a;hpb=04a7de082bd221eae3c7004f4e0b99dfa4f8be91;p=nfvbench.git diff --git a/nfvbench/chain_clients.py b/nfvbench/chain_clients.py index 4be050f..fa21359 100644 --- a/nfvbench/chain_clients.py +++ b/nfvbench/chain_clients.py @@ -14,14 +14,16 @@ # under the License. # -import compute +import os +import re +import time + from glanceclient.v2 import client as glanceclient -from log import LOG from neutronclient.neutron import client as neutronclient from novaclient.client import Client -import os -import time +import compute +from log import LOG class StageClientException(Exception): pass @@ -35,6 +37,7 @@ class BasicStageClient(object): def __init__(self, config, cred): self.comp = None self.image_instance = None + self.image_name = None self.config = config self.cred = cred self.nets = [] @@ -48,7 +51,9 @@ class BasicStageClient(object): self.host_ips = None def _ensure_vms_active(self): - for _ in range(self.config.generic_retry_count): + retry_count = (self.config.check_traffic_time_sec + + self.config.generic_poll_sec - 1) / self.config.generic_poll_sec + for _ in range(retry_count): for i, instance in enumerate(self.vms): if instance.status == 'ACTIVE': continue @@ -61,7 +66,8 @@ class BasicStageClient(object): LOG.info('Created instance: %s', instance.name) self.vms[i] = instance setattr(self.vms[i], 'is_reuse', is_reuse) - if all(map(lambda instance: instance.status == 'ACTIVE', self.vms)): + + if all([(vm.status == 'ACTIVE') for vm in self.vms]): return time.sleep(self.config.generic_poll_sec) raise StageClientException('Timed out waiting for VMs to spawn') @@ -78,11 +84,13 @@ class BasicStageClient(object): networks = self.neutron.list_networks(name=network_name) return networks['networks'][0] if networks['networks'] else None - def _create_net(self, name, subnet, cidr, network_type=None, segmentation_id=None): + def _create_net(self, name, subnet, cidr, network_type=None, + segmentation_id=None, physical_network=None): network = self._lookup_network(name) if network: - phys_net = self.config.internal_networks.physical_network - if segmentation_id is not None and phys_net is not None: + # a network of same name already exists, we need to verify it has the same + # characteristics + if segmentation_id: if network['provider:segmentation_id'] != segmentation_id: raise StageClientException("Mismatch of 'segmentation_id' for reused " "network '{net}'. Network has id '{seg_id1}', " @@ -91,15 +99,16 @@ class BasicStageClient(object): seg_id1=network['provider:segmentation_id'], seg_id2=segmentation_id)) - if network['provider:physical_network'] != phys_net: + if physical_network: + if network['provider:physical_network'] != physical_network: raise StageClientException("Mismatch of 'physical_network' for reused " "network '{net}'. Network has '{phys1}', " "configuration requires '{phys2}'." .format(net=name, phys1=network['provider:physical_network'], - phys2=phys_net)) + phys2=physical_network)) - LOG.info('Reusing existing network: ' + name) + LOG.info('Reusing existing network: %s', name) network['is_reuse'] = True return network @@ -112,10 +121,10 @@ class BasicStageClient(object): if network_type: body['network']['provider:network_type'] = network_type - phys_net = self.config.internal_networks.physical_network - if segmentation_id is not None and phys_net is not None: + if segmentation_id: body['network']['provider:segmentation_id'] = segmentation_id - body['network']['provider:physical_network'] = phys_net + if physical_network: + body['network']['provider:physical_network'] = physical_network network = self.neutron.create_network(body)['network'] body = { @@ -132,14 +141,14 @@ class BasicStageClient(object): # add subnet id to the network dict since it has just been added network['subnets'] = [subnet['id']] network['is_reuse'] = False - LOG.info('Created network: %s.' % name) + LOG.info('Created network: %s.', name) return network - def _create_port(self, net): + def _create_port(self, net, vnic_type='normal'): body = { "port": { 'network_id': net['id'], - 'binding:vnic_type': 'direct' if self.config.sriov else 'normal' + 'binding:vnic_type': vnic_type } } port = self.neutron.create_port(body) @@ -154,7 +163,7 @@ class BasicStageClient(object): except Exception: retry += 1 time.sleep(self.config.generic_poll_sec) - LOG.error('Unable to delete port: %s' % (port['id'])) + LOG.error('Unable to delete port: %s', port['id']) def __delete_net(self, network): retry = 0 @@ -165,7 +174,7 @@ class BasicStageClient(object): except Exception: retry += 1 time.sleep(self.config.generic_poll_sec) - LOG.error('Unable to delete network: %s' % (network['name'])) + LOG.error('Unable to delete network: %s', network['name']) def __get_server_az(self, server): availability_zone = getattr(server, 'OS-EXT-AZ:availability_zone', None) @@ -178,7 +187,7 @@ class BasicStageClient(object): def _lookup_servers(self, name=None, nets=None, az=None, flavor_id=None): error_msg = 'VM with the same name, but non-matching {} found. Aborting.' - networks = set(map(lambda net: net['name'], nets)) if nets else None + networks = set([net['name'] for net in nets]) if nets else None server_list = self.comp.get_server_list() matching_servers = [] @@ -186,9 +195,6 @@ class BasicStageClient(object): if name and server.name != name: continue - if az and self.__get_server_az(server) != az: - raise StageClientException(error_msg.format('availability zones')) - if flavor_id and server.flavor['id'] != flavor_id: raise StageClientException(error_msg.format('flavors')) @@ -204,7 +210,7 @@ class BasicStageClient(object): return matching_servers def _create_server(self, name, ports, az, nfvbenchvm_config): - port_ids = map(lambda port: {'port-id': port['id']}, ports) + port_ids = [{'port-id': port['id']} for port in ports] nfvbenchvm_config_location = os.path.join('/etc/', self.nfvbenchvm_config_name) server = self.comp.create_server(name, self.image_instance, @@ -218,31 +224,50 @@ class BasicStageClient(object): files={nfvbenchvm_config_location: nfvbenchvm_config}) if server: setattr(server, 'is_reuse', False) - LOG.info('Creating instance: %s on %s' % (name, az)) + LOG.info('Creating instance: %s on %s', name, az) else: raise StageClientException('Unable to create instance: %s.' % (name)) return server def _setup_resources(self): - if not self.image_instance: - self.image_instance = self.comp.find_image(self.config.image_name) - if self.image_instance is None: + # To avoid reuploading image in server mode, check whether image_name is set or not + if self.image_name: + self.image_instance = self.comp.find_image(self.image_name) + if self.image_instance: + LOG.info("Reusing image %s", self.image_name) + else: + image_name_search_pattern = r'(nfvbenchvm-\d+(\.\d+)*).qcow2' if self.config.vm_image_file: - LOG.info('%s: image for VM not found, trying to upload it ...' - % self.config.image_name) - res = self.comp.upload_image_via_url(self.config.image_name, + match = re.search(image_name_search_pattern, self.config.vm_image_file) + if match: + self.image_name = match.group(1) + LOG.info('Using provided VM image file %s', self.config.vm_image_file) + else: + raise StageClientException('Provided VM image file name %s must start with ' + '"nfvbenchvm-"' % self.config.vm_image_file) + else: + pkg_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + for f in os.listdir(pkg_root): + if re.search(image_name_search_pattern, f): + self.config.vm_image_file = pkg_root + '/' + f + self.image_name = f.replace('.qcow2', '') + LOG.info('Found built-in VM image file %s', f) + break + else: + raise StageClientException('Cannot find any built-in VM image file.') + if self.image_name: + self.image_instance = self.comp.find_image(self.image_name) + if not self.image_instance: + LOG.info('Uploading %s', self.image_name) + res = self.comp.upload_image_via_url(self.image_name, self.config.vm_image_file) if not res: raise StageClientException('Error uploading image %s from %s. ABORTING.' - % (self.config.image_name, + % (self.image_name, self.config.vm_image_file)) - self.image_instance = self.comp.find_image(self.config.image_name) - else: - raise StageClientException('%s: image to launch VM not found. ABORTING.' - % self.config.image_name) - - LOG.info('Found image %s to launch VM' % self.config.image_name) + LOG.info('Image %s successfully uploaded.', self.image_name) + self.image_instance = self.comp.find_image(self.image_name) self.__setup_flavor() @@ -261,7 +286,7 @@ class BasicStageClient(object): override=True, **flavor_dict) - LOG.info("Flavor '%s' was created." % self.config.flavor_type) + LOG.info("Flavor '%s' was created.", self.config.flavor_type) if extra_specs: self.flavor_type['flavor'].set_keys(extra_specs) @@ -274,23 +299,25 @@ class BasicStageClient(object): def __delete_flavor(self, flavor): if self.comp.delete_flavor(flavor=flavor): - LOG.info("Flavor '%s' deleted" % self.config.flavor_type) + LOG.info("Flavor '%s' deleted", self.config.flavor_type) self.flavor_type = {'is_reuse': False, 'flavor': None} else: - LOG.error('Unable to delete flavor: %s' % self.config.flavor_type) + LOG.error('Unable to delete flavor: %s', self.config.flavor_type) - def get_config_file(self, chain_index, src_mac, dst_mac): + def get_config_file(self, chain_index, src_mac, dst_mac, intf_mac1, intf_mac2): boot_script_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'nfvbenchvm/', self.nfvbenchvm_config_name) with open(boot_script_file, 'r') as boot_script: content = boot_script.read() - g1cidr = self.config.generator_config.src_device.gateway_ip_list[chain_index] + '/8' - g2cidr = self.config.generator_config.dst_device.gateway_ip_list[chain_index] + '/8' + g1cidr = self.config.generator_config.src_device.get_gw_ip(chain_index) + '/8' + g2cidr = self.config.generator_config.dst_device.get_gw_ip(chain_index) + '/8' vm_config = { 'forwarder': self.config.vm_forwarder, + 'intf_mac1': intf_mac1, + 'intf_mac2': intf_mac2, 'tg_gateway1_ip': self.config.traffic_generator.tg_gateway_ip_addrs[0], 'tg_gateway2_ip': self.config.traffic_generator.tg_gateway_ip_addrs[1], 'tg_net1': self.config.traffic_generator.ip_addrs[0], @@ -315,7 +342,7 @@ class BasicStageClient(object): """ Disable security at port level. """ - vm_ids = map(lambda vm: vm.id, self.vms) + vm_ids = [vm.id for vm in self.vms] for net in self.nets: for port in self.ports[net['id']]: if port['device_id'] in vm_ids: @@ -325,7 +352,7 @@ class BasicStageClient(object): 'port_security_enabled': False, } }) - LOG.info('Security disabled on port {}'.format(port['id'])) + LOG.info('Security disabled on port %s', port['id']) def get_loop_vm_hostnames(self): return [getattr(vm, 'OS-EXT-SRV-ATTR:hypervisor_hostname') for vm in self.vms] @@ -337,8 +364,7 @@ class BasicStageClient(object): if not self.host_ips: # get the hypervisor object from the host name self.host_ips = [self.comp.get_hypervisor( - getattr(vm, 'OS-EXT-SRV-ATTR:hypervisor_hostname')).host_ip - for vm in self.vms] + getattr(vm, 'OS-EXT-SRV-ATTR:hypervisor_hostname')).host_ip for vm in self.vms] return self.host_ips def get_loop_vm_compute_nodes(self): @@ -354,11 +380,10 @@ class BasicStageClient(object): flavor_id=self.flavor_type['flavor'].id) if servers: server = servers[0] - LOG.info('Reusing existing server: ' + name) + LOG.info('Reusing existing server: %s', name) setattr(server, 'is_reuse', True) return server - else: - return None + return None def get_networks_uuids(self): """ @@ -376,7 +401,7 @@ class BasicStageClient(object): """ vlans = [] for net in self.nets: - assert(net['provider:network_type'] == 'vlan') + assert net['provider:network_type'] == 'vlan' vlans.append(net['provider:segmentation_id']) return vlans @@ -397,7 +422,7 @@ class BasicStageClient(object): if not getattr(vm, 'is_reuse', True): self.comp.delete_server(vm) else: - LOG.info('Server %s not removed since it is reused' % vm.name) + LOG.info('Server %s not removed since it is reused', vm.name) for port in self.created_ports: self.__delete_port(port) @@ -407,17 +432,13 @@ class BasicStageClient(object): if 'is_reuse' in net and not net['is_reuse']: self.__delete_net(net) else: - LOG.info('Network %s not removed since it is reused' % (net['name'])) + LOG.info('Network %s not removed since it is reused', net['name']) if not self.flavor_type['is_reuse']: self.__delete_flavor(self.flavor_type['flavor']) class EXTStageClient(BasicStageClient): - - def __init__(self, config, cred): - super(EXTStageClient, self).__init__(config, cred) - def setup(self): super(EXTStageClient, self).setup() @@ -431,14 +452,10 @@ class EXTStageClient(BasicStageClient): class PVPStageClient(BasicStageClient): - - def __init__(self, config, cred): - super(PVPStageClient, self).__init__(config, cred) - def get_end_port_macs(self): - vm_ids = map(lambda vm: vm.id, self.vms) + vm_ids = [vm.id for vm in self.vms] port_macs = [] - for index, net in enumerate(self.nets): + for _index, net in enumerate(self.nets): vm_mac_map = {port['device_id']: port['mac_address'] for port in self.ports[net['id']]} port_macs.append([vm_mac_map[vm_id] for vm_id in vm_ids]) return port_macs @@ -463,11 +480,13 @@ class PVPStageClient(BasicStageClient): if reusable_vm: self.vms.append(reusable_vm) else: + vnic_type = 'direct' if self.config.sriov else 'normal' + ports = [self._create_port(net, vnic_type) for net in self.nets] config_file = self.get_config_file(chain_index, self.config.generator_config.src_device.mac, - self.config.generator_config.dst_device.mac) - - ports = [self._create_port(net) for net in self.nets] + self.config.generator_config.dst_device.mac, + ports[0]['mac_address'], + ports[1]['mac_address']) self.created_ports.extend(ports) self.vms.append(self._create_server(name, ports, az, config_file)) self._ensure_vms_active() @@ -475,14 +494,10 @@ class PVPStageClient(BasicStageClient): class PVVPStageClient(BasicStageClient): - - def __init__(self, config, cred): - super(PVVPStageClient, self).__init__(config, cred) - def get_end_port_macs(self): port_macs = [] for index, net in enumerate(self.nets[:2]): - vm_ids = map(lambda vm: vm.id, self.vms[index::2]) + vm_ids = [vm.id for vm in self.vms[index::2]] vm_mac_map = {port['device_id']: port['mac_address'] for port in self.ports[net['id']]} port_macs.append([vm_mac_map[vm_id] for vm_id in vm_ids]) return port_macs @@ -530,11 +545,15 @@ class PVVPStageClient(BasicStageClient): if reusable_vm0 and reusable_vm1: self.vms.extend([reusable_vm0, reusable_vm1]) else: - vm0_port_net0 = self._create_port(vm0_nets[0]) - vm0_port_net2 = self._create_port(vm0_nets[1]) + edge_vnic_type = 'direct' if self.config.sriov else 'normal' + middle_vnic_type = 'direct' \ + if self.config.sriov and self.config.use_sriov_middle_net \ + else 'normal' + vm0_port_net0 = self._create_port(vm0_nets[0], edge_vnic_type) + vm0_port_net2 = self._create_port(vm0_nets[1], middle_vnic_type) - vm1_port_net2 = self._create_port(vm1_nets[1]) - vm1_port_net1 = self._create_port(vm1_nets[0]) + vm1_port_net2 = self._create_port(vm1_nets[1], middle_vnic_type) + vm1_port_net1 = self._create_port(vm1_nets[0], edge_vnic_type) self.created_ports.extend([vm0_port_net0, vm0_port_net2, @@ -546,10 +565,14 @@ class PVVPStageClient(BasicStageClient): # TG0 (net0) -> VM0 (net2) -> VM1 (net2) -> TG1 (net1) config_file0 = self.get_config_file(chain_index, self.config.generator_config.src_device.mac, - vm1_port_net2['mac_address']) + vm1_port_net2['mac_address'], + vm0_port_net0['mac_address'], + vm0_port_net2['mac_address']) config_file1 = self.get_config_file(chain_index, vm0_port_net2['mac_address'], - self.config.generator_config.dst_device.mac) + self.config.generator_config.dst_device.mac, + vm1_port_net2['mac_address'], + vm1_port_net1['mac_address']) self.vms.append(self._create_server(name0, [vm0_port_net0, vm0_port_net2],