def test_list_as_tuples(self, is_neutron): requests = objects.NetworkRequestList( objects=[objects.NetworkRequest(network_id='123'), objects.NetworkRequest(network_id='456')]) self.assertEqual( [('123', None, None, None), ('456', None, None, None)], requests.as_tuples())
def test_is_single_unspecified(self): requests = objects.NetworkRequestList( objects=[objects.NetworkRequest(network_id='123')]) self.assertFalse(requests.is_single_unspecified) requests = objects.NetworkRequestList( objects=[objects.NetworkRequest(), objects.NetworkRequest()]) self.assertFalse(requests.is_single_unspecified) requests = objects.NetworkRequestList( objects=[objects.NetworkRequest()]) self.assertTrue(requests.is_single_unspecified)
def test_basic(self): request = objects.NetworkRequest() request.network_id = '456' request.address = '1.2.3.4' request.port_id = FAKE_UUID self.assertFalse(request.auto_allocate) self.assertFalse(request.no_allocate)
def allocate_port_for_instance(self, context, instance, port_id, network_id=None, requested_ip=None, pci_list=None): """Allocate a port for the instance.""" if not network_id and not port_id: nets = self._get_available_networks(context, context.project_id) if len(nets) > 1: msg = _("Multiple possible networks found, use a Network " "ID to be more specific.") raise exception.NetworkAmbiguous(msg) requested_networks = objects.NetworkRequestList(objects=[ objects.NetworkRequest(network_id=network_id, address=requested_ip, port_id=port_id, pci_request_id=None) ]) return self.allocate_for_instance( context, instance, requested_networks=requested_networks, pci_list=pci_list)
def test_to_tuple_nova(self): request = objects.NetworkRequest(network_id='123', address='1.2.3.4', port_id=FAKE_UUID) with mock.patch('nova.utils.is_neutron', return_value=False): self.assertEqual(('123', '1.2.3.4'), request.to_tuple())
def _get_requested_networks(self, requested_networks): """Create a list of requested networks from the networks attribute.""" networks = [] network_uuids = [] for network in requested_networks: request = objects.NetworkRequest() try: try: request.port_id = network.get('port', None) except ValueError: msg = _("Bad port format: port uuid is " "not in proper format " "(%s)") % network.get('port') raise exc.HTTPBadRequest(explanation=msg) if request.port_id: request.network_id = None if not utils.is_neutron(): # port parameter is only for neutron v2.0 msg = _("Unknown argument : port") raise exc.HTTPBadRequest(explanation=msg) else: request.network_id = network['uuid'] if (not request.port_id and not uuidutils.is_uuid_like(request.network_id)): br_uuid = request.network_id.split('-', 1)[-1] if not uuidutils.is_uuid_like(br_uuid): msg = _("Bad networks format: network uuid is " "not in proper format " "(%s)") % request.network_id raise exc.HTTPBadRequest(explanation=msg) # fixed IP address is optional # if the fixed IP address is not provided then # it will use one of the available IP address from the network try: request.address = network.get('fixed_ip', None) except ValueError: msg = (_("Invalid fixed IP address (%s)") % network.get('fixed_ip')) raise exc.HTTPBadRequest(explanation=msg) # duplicate networks are allowed only for neutron v2.0 if (not utils.is_neutron() and request.network_id and request.network_id in network_uuids): expl = (_("Duplicate networks" " (%s) are not allowed") % request.network_id) raise exc.HTTPBadRequest(explanation=expl) network_uuids.append(request.network_id) networks.append(request) except KeyError as key: expl = _('Bad network format: missing %s') % key raise exc.HTTPBadRequest(explanation=expl) except TypeError: expl = _('Bad networks format') raise exc.HTTPBadRequest(explanation=expl) return objects.NetworkRequestList(objects=networks)
def test_to_tuple(self): request = objects.NetworkRequest( network_id='123', address='1.2.3.4', port_id=FAKE_UUID, ) self.assertEqual(('123', '1.2.3.4', FAKE_UUID, None), request.to_tuple())
def test_obj_make_compatible_pre_1_2(self): net_req = objects.NetworkRequest() net_req.tag = 'foo' data = lambda x: x['nova_object.data'] primitive = data(net_req.obj_to_primitive(target_version='1.2')) self.assertIn('tag', primitive) primitive = data(net_req.obj_to_primitive(target_version='1.1')) self.assertNotIn('tag', primitive)
def test_obj_make_compatible_pre_1_3(self): net_req = objects.NetworkRequest() net_req.arq_uuid = ARQ_UUID data = lambda x: x['nova_object.data'] primitive = data(net_req.obj_to_primitive(target_version='1.3')) self.assertIn('arq_uuid', primitive) primitive = data(net_req.obj_to_primitive(target_version='1.2')) self.assertNotIn('arq_uuid', primitive) self.assertNotIn('device_profile', primitive)
def test_auto_allocate(self): # no objects requests = objects.NetworkRequestList() self.assertFalse(requests.auto_allocate) # single object with network uuid requests = objects.NetworkRequestList( objects=[objects.NetworkRequest(network_id=FAKE_UUID)]) self.assertFalse(requests.auto_allocate) # multiple objects requests = objects.NetworkRequestList( objects=[objects.NetworkRequest(), objects.NetworkRequest()]) self.assertFalse(requests.auto_allocate) # single object, 'auto' case requests = objects.NetworkRequestList( objects=[objects.NetworkRequest( network_id=network_request.NETWORK_ID_AUTO)]) self.assertTrue(requests.auto_allocate)
def test_to_tuple(self): request = objects.NetworkRequest(network_id='123', address='1.2.3.4', port_id=FAKE_UUID, arq_uuid=ARQ_UUID, device_profile = DEV_PROFILE ) self.assertEqual( ('123', '1.2.3.4', FAKE_UUID, None, ARQ_UUID, DEV_PROFILE), request.to_tuple())
def test_routed_networks_filter_with_two_requested_nets( self, mock_get_aggs_network ): req_net1 = objects.NetworkRequest(network_id=uuids.net1) req_net2 = objects.NetworkRequest(network_id=uuids.net2) reqspec = objects.RequestSpec( requested_networks=objects.NetworkRequestList( objects=[req_net1, req_net2])) mock_get_aggs_network.side_effect = ([uuids.agg1, uuids.agg2], [uuids.agg3]) self.assertTrue(request_filter.routed_networks_filter( self.context, reqspec)) # require_aggregates() has a specific semantics here where multiple # aggregates provided in the same call have their UUIDs being joined. self.assertEqual([','.join([uuids.agg1, uuids.agg2]), uuids.agg3], reqspec.requested_destination.aggregates) mock_get_aggs_network.assert_has_calls([ mock.call(self.context, mock.ANY, mock.ANY, uuids.net1), mock.call(self.context, mock.ANY, mock.ANY, uuids.net2)])
def test_deallocate_for_instance_auto_allocate(self, mock_rpc_dealloc): # Tests that we pass requested_networks=None to the RPC API when # we're auto-allocating. instance = fake_instance.fake_instance_obj(self.context) req_net = objects.NetworkRequest( network_id=net_req_obj.NETWORK_ID_AUTO) requested_networks = objects.NetworkRequestList(objects=[req_net]) self.network_api.deallocate_for_instance( self.context, instance, requested_networks) mock_rpc_dealloc.assert_called_once_with(self.context, instance=instance, requested_networks=None)
def test_routed_networks_filter_with_requested_net(self, mock_get_aggs_network): req_net = objects.NetworkRequest(network_id=uuids.net1) reqspec = objects.RequestSpec( requested_networks=objects.NetworkRequestList(objects=[req_net])) mock_get_aggs_network.return_value = [uuids.agg1] self.assertTrue( request_filter.routed_networks_filter(self.context, reqspec)) self.assertEqual([uuids.agg1], reqspec.requested_destination.aggregates) mock_get_aggs_network.assert_called_once_with(self.context, mock.ANY, mock.ANY, uuids.net1)
def allocate_port_for_instance(self, context, instance, port_id, network_id=None, requested_ip=None): """Allocate a port for the instance.""" requested_networks = objects.NetworkRequestList(objects=[ objects.NetworkRequest(network_id=network_id, address=requested_ip, port_id=port_id, pci_request_id=None) ]) return self.allocate_for_instance( context, instance, requested_networks=requested_networks)
def _get_requested_networks(self, requested_networks): """Create a list of requested networks from the networks attribute.""" networks = [] network_uuids = [] for network in requested_networks: request = objects.NetworkRequest() try: # fixed IP address is optional # if the fixed IP address is not provided then # it will use one of the available IP address from the network request.address = network.get('fixed_ip', None) request.port_id = network.get('port', None) if request.port_id: request.network_id = None if not utils.is_neutron(): # port parameter is only for neutron v2.0 msg = _("Unknown argument: port") raise exc.HTTPBadRequest(explanation=msg) if request.address is not None: msg = _("Specified Fixed IP '%(addr)s' cannot be used " "with port '%(port)s': port already has " "a Fixed IP allocated.") % { "addr": request.address, "port": request.port_id} raise exc.HTTPBadRequest(explanation=msg) else: request.network_id = network['uuid'] self._validate_network_id( request.network_id, network_uuids) network_uuids.append(request.network_id) networks.append(request) except KeyError as key: expl = _('Bad network format: missing %s') % key raise exc.HTTPBadRequest(explanation=expl) except TypeError: expl = _('Bad networks format') raise exc.HTTPBadRequest(explanation=expl) return objects.NetworkRequestList(objects=networks)
def test_routed_networks_filter_with_requested_port_deferred( self, mock_show_port, mock_get_aggs_network ): req_net = objects.NetworkRequest(port_id=uuids.port1) reqspec = objects.RequestSpec( requested_networks=objects.NetworkRequestList(objects=[req_net])) # The port was created with a deferred allocation so for the moment, # it's not bound to a specific segment. mock_show_port.return_value = { 'port': { 'fixed_ips': [], 'network_id': uuids.net1}} mock_get_aggs_network.return_value = [uuids.agg1] self.assertTrue(request_filter.routed_networks_filter( self.context, reqspec)) self.assertEqual([uuids.agg1], reqspec.requested_destination.aggregates) mock_show_port.assert_called_once_with(self.context, uuids.port1) mock_get_aggs_network.assert_called_once_with( self.context, mock.ANY, mock.ANY, uuids.net1)
def test_routed_networks_filter_with_requested_port_immediate( self, mock_show_port, mock_get_aggs_subnet ): req_net = objects.NetworkRequest(port_id=uuids.port1) reqspec = objects.RequestSpec( requested_networks=objects.NetworkRequestList(objects=[req_net])) # Check whether the port was already bound to a segment mock_show_port.return_value = { 'port': { 'fixed_ips': [ { 'subnet_id': uuids.subnet1 }]}} mock_get_aggs_subnet.return_value = [uuids.agg1] self.assertTrue(request_filter.routed_networks_filter( self.context, reqspec)) self.assertEqual([uuids.agg1], reqspec.requested_destination.aggregates) mock_show_port.assert_called_once_with(self.context, uuids.port1) mock_get_aggs_subnet.assert_called_once_with( self.context, mock.ANY, mock.ANY, uuids.subnet1)
def allocate_for_instance(self, context, instance, **kwargs): """Allocate network resources for the instance. :param context: The request context. :param instance: nova.objects.instance.Instance object. :param requested_networks: optional value containing network_id, fixed_ip, and port_id :param security_groups: security groups to allocate for instance :param macs: None or a set of MAC addresses that the instance should use. macs is supplied by the hypervisor driver (contrast with requested_networks which is user supplied). NB: NeutronV2 currently assigns hypervisor supplied MAC addresses to arbitrary networks, which requires openflow switches to function correctly if more than one network is being used with the bare metal hypervisor (which is the only one known to limit MAC addresses). :param dhcp_options: None or a set of key/value pairs that should determine the DHCP BOOTP response, eg. for PXE booting an instance configured with the baremetal hypervisor. It is expected that these are already formatted for the neutron v2 api. See nova/virt/driver.py:dhcp_options_for_instance for an example. """ hypervisor_macs = kwargs.get('macs', None) available_macs = None if hypervisor_macs is not None: # Make a copy we can mutate: records macs that have not been used # to create a port on a network. If we find a mac with a # pre-allocated port we also remove it from this set. available_macs = set(hypervisor_macs) neutron = neutronv2.get_client(context) LOG.debug('allocate_for_instance()', instance=instance) if not instance.project_id: msg = _('empty project id for instance %s') raise exception.InvalidInput(reason=msg % instance.uuid) requested_networks = kwargs.get('requested_networks') dhcp_opts = kwargs.get('dhcp_options', None) ports = {} net_ids = [] ordered_networks = [] if requested_networks: for request in requested_networks: if request.port_id: try: port = neutron.show_port(request.port_id)['port'] except neutron_client_exc.PortNotFoundClient: raise exception.PortNotFound(port_id=request.port_id) if port['tenant_id'] != instance.project_id: raise exception.PortNotUsable(port_id=request.port_id, instance=instance.uuid) if (port.get('device_id') and port.get('device_id') != instance['uuid']): raise exception.PortInUse(port_id=request.port_id) if hypervisor_macs is not None: if port['mac_address'] not in hypervisor_macs: raise exception.PortNotUsable( port_id=request.port_id, instance=instance.uuid) else: # Don't try to use this MAC if we need to create a # port on the fly later. Identical MACs may be # configured by users into multiple ports so we # discard rather than popping. available_macs.discard(port['mac_address']) request.network_id = port['network_id'] ports[request.port_id] = port if request.network_id: net_ids.append(request.network_id) ordered_networks.append(request) nets = self._get_available_networks(context, instance.project_id, net_ids) if not nets: LOG.warn(_LW("No network configured!"), instance=instance) return network_model.NetworkInfo([]) # if this function is directly called without a requested_network param # or if it is indirectly called through allocate_port_for_instance() # with None params=(network_id=None, requested_ip=None, port_id=None, # pci_request_id=None): if (not requested_networks or requested_networks.is_single_unspecified): # bug/1267723 - if no network is requested and more # than one is available then raise NetworkAmbiguous Exception if len(nets) > 1: msg = _("Multiple possible networks found, use a Network " "ID to be more specific.") raise exception.NetworkAmbiguous(msg) ordered_networks.append( objects.NetworkRequest(network_id=nets[0]['id'])) db_req_networks = list() db_obj = huawei_instance_extra.HuaweiInstanceExtra( instance_uuid=instance.uuid) db_instance = db_obj.get_by_instance_uuid( context, instance_uuid=instance.uuid) if db_instance.request_network: db_req_networks = jsonutils.loads(db_instance.request_network) db_req_networks.append([nets[0]['id'], None, None]) db_obj.request_network = jsonutils.dumps(db_req_networks) db_obj.create(context) # NOTE(): check external net attach permission after the # check for ambiguity, there could be another # available net which is permitted bug/1364344 self._check_external_network_attach(context, nets) security_groups = kwargs.get('security_groups', []) security_group_ids = [] # TODO() Should optimize more to do direct query for security # group if len(security_groups) == 1 if len(security_groups): search_opts = {'tenant_id': instance.project_id} user_security_groups = neutron.list_security_groups( **search_opts).get('security_groups') for security_group in security_groups: name_match = None uuid_match = None for user_security_group in user_security_groups: if user_security_group['name'] == security_group: if name_match: raise exception.NoUniqueMatch( _("Multiple security groups found matching" " '%s'. Use an ID to be more specific.") % security_group) name_match = user_security_group['id'] if user_security_group['id'] == security_group: uuid_match = user_security_group['id'] # If a user names the security group the same as # another's security groups uuid, the name takes priority. if not name_match and not uuid_match: raise exception.SecurityGroupNotFound( security_group_id=security_group) elif name_match: security_group_ids.append(name_match) elif uuid_match: security_group_ids.append(uuid_match) touched_port_ids = [] created_port_ids = [] ports_in_requested_order = [] nets_in_requested_order = [] for request in ordered_networks: # Network lookup for available network_id network = None for net in nets: if net['id'] == request.network_id: network = net break # if network_id did not pass validate_networks() and not available # here then skip it safely not continuing with a None Network else: continue nets_in_requested_order.append(network) # If security groups are requested on an instance then the # network must has a subnet associated with it. Some plugins # implement the port-security extension which requires # 'port_security_enabled' to be True for security groups. # That is why True is returned if 'port_security_enabled' # is not found. if (security_groups and not (network['subnets'] and network.get('port_security_enabled', True))): # add for roll back self._delete_ports(neutron, instance, created_port_ids) raise exception.SecurityGroupCannotBeApplied() request.network_id = network['id'] zone = 'compute:%s' % instance.availability_zone port_req_body = { 'port': { 'device_id': instance.uuid, 'device_owner': zone } } try: self._populate_neutron_extension_values( context, instance, request.pci_request_id, port_req_body) # Requires admin creds to set port bindings port_client = (neutron if not self._has_port_binding_extension(context) else neutronv2.get_client(context, admin=True)) if request.port_id: port = ports[request.port_id] port_client.update_port(port['id'], port_req_body) touched_port_ids.append(port['id']) ports_in_requested_order.append(port['id']) else: created_port = self._create_port(port_client, instance, request.network_id, port_req_body, request.address, security_group_ids, available_macs, dhcp_opts) created_port_ids.append(created_port) ports_in_requested_order.append(created_port) except Exception: with excutils.save_and_reraise_exception(): for port_id in touched_port_ids: try: port_req_body = {'port': {'device_id': ''}} # Requires admin creds to set port bindings if self._has_port_binding_extension(context): port_req_body['port']['binding:host_id'] = None port_client = neutronv2.get_client(context, admin=True) else: port_client = neutron port_client.update_port(port_id, port_req_body) except Exception: msg = _LE("Failed to update port %s") LOG.exception(msg, port_id) self._delete_ports(neutron, instance, created_port_ids) pci_list = kwargs.get('pci_list', []) nw_info = self.get_instance_nw_info(context, instance, networks=nets_in_requested_order, port_ids=ports_in_requested_order, pci_list=pci_list) # NOTE(): Only return info about ports we created in this run. # In the initial allocation case, this will be everything we created, # and in later runs will only be what was created that time. Thus, # this only affects the attach case, not the original use for this # method. return network_model.NetworkInfo([ vif for vif in nw_info if vif['id'] in created_port_ids + touched_port_ids ])
def test_obj_make_compatible_pre_1_2(self): net_req = objects.NetworkRequest() net_req.tag = 'foo' primitive = net_req.obj_to_primitive(target_version='1.1') self.assertNotIn('tag', primitive)
def test_load(self): request = objects.NetworkRequest() self.assertIsNone(request.port_id) self.assertFalse(request.auto_allocate) self.assertFalse(request.no_allocate)
def test_basic(self): request = objects.NetworkRequest() request.network_id = '456' request.address = '1.2.3.4' request.port_id = FAKE_UUID
def test_load(self): request = objects.NetworkRequest() self.assertIsNone(request.port_id)