def test_do_not_notify_in_undo_cmd_mgr(self, notifier): """Wraps a call to notify in CommandManager's rollback()""" cfg.CONF.set_override('environment_capabilities', 'security_groups,ip_billing', 'QUARK') ipaddress = get_fake_fixed_address() ipaddress.allocated_at = datetime.datetime.utcnow() try: with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def f(): raise Exception @cmd_mgr.undo def f_undo(*args, **kwargs): billing.notify(self.context, billing.IP_ADD, ipaddress, *args, **kwargs) f() except Exception: pass # notifier should NOT have been called self.assertFalse(notifier.called) cfg.CONF.clear_override('environment_capabilities', 'QUARK')
def test_command_manager_undo(self, func_undo_notifier): """Test that undo is called when the do function raises""" try: with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def f(**kwargs): raise Exception @cmd_mgr.undo def f_undo(*args, **kwargs): func_undo(**kwargs) f() except Exception: pass self.assertTrue(func_undo_notifier.called)
def test_rollback_is_passed_to_undo(self): """Tests that the undo function has rollback set to True""" self.is_rollback_undo = False try: with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def f(**kwargs): raise Exception @cmd_mgr.undo def f_undo(*args, **kwargs): self.is_rollback_undo = func_undo(**kwargs) f() except Exception: pass self.assertTrue(self.is_rollback_undo)
def test_rollback_is_passed_to_do(self): """Tests that the do function has rollback set to False""" self.is_rollback_do = True try: with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def f(**kwargs): return func_do(**kwargs) @cmd_mgr.undo def f_undo(*args, **kwargs): func_undo(**kwargs) self.is_rollback_do = f() except Exception: pass self.assertFalse(self.is_rollback_do)
def test_command_manager_no_undo(self, func_do_notifier, func_undo_notifier): """Test that undo is not called when everything is good""" try: with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def f(**kwargs): func_do(**kwargs) @cmd_mgr.undo def f_undo(*args, **kwargs): func_undo(**kwargs) f() except Exception: pass self.assertTrue(func_do_notifier.called) self.assertFalse(func_undo_notifier.called)
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up", "use_forbidden_mac_range"] utils.filter_body(context, port_attrs, admin_only=admin_only) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) use_forbidden_mac_range = utils.pop_param(port_attrs, "use_forbidden_mac_range", False) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") if "device_id" not in port_attrs: port_attrs['device_id'] = "" device_id = port_attrs['device_id'] net_id = port_attrs["network_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context, None, None, None, False, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) _raise_if_unauthorized(context.tenant_id, net) # NOTE (Perkins): If a device_id is given, try to prevent multiple ports # from being created for a device already attached to the network if device_id: existing_ports = db_api.port_find(context, network_id=net_id, device_id=device_id, scope=db_api.ONE) if existing_ports: raise exceptions.BadRequest( resource="port", msg="This device is already connected to the " "requested network via another port") # Try to fail early on quotas and save ourselves some db overhead if fixed_ips: quota.QUOTAS.limit_check(context, context.tenant_id, fixed_ips_per_port=len(fixed_ips)) if not STRATEGY.is_parent_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check( context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"]) net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"]) # TODO(anyone): security groups are not currently supported on port create, # nor on isolated networks today. Please see RM8615 security_groups = utils.pop_param(port_attrs, "security_groups") if security_groups is not None: raise q_exc.SecurityGroupsNotImplemented() group_ids, security_groups = _make_security_group_list(context, security_groups) quota.QUOTAS.limit_check(context, context.tenant_id, security_groups_per_port=len(group_ids)) addresses = [] backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac): fixed_ip_kwargs = {} if fixed_ips: if STRATEGY.is_parent_network(net_id) and not context.is_admin: raise exceptions.NotAuthorized() ips, subnets = split_and_validate_requested_subnets(context, net_id, segment_id, fixed_ips) fixed_ip_kwargs["ip_addresses"] = ips fixed_ip_kwargs["subnets"] = subnets ipam_driver.allocate_ip_address( context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac, **fixed_ip_kwargs) @cmd_mgr.undo def _allocate_ips_undo(addr): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address(context, address) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=False): mac = ipam_driver.allocate_mac_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address, use_forbidden_mac_range=use_forbidden_mac_range) return mac @cmd_mgr.undo def _allocate_mac_undo(mac): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address(context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id): backend_port = net_driver.create_port(context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port): LOG.info("Rolling back backend port...") try: net_driver.delete_port(context, backend_port["uuid"]) except Exception: LOG.exception( "Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create( context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception( "Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=use_forbidden_mac_range) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] admin_only = [ "mac_address", "device_owner", "bridge", "admin_state_up", "use_forbidden_mac_range", "network_plugin", "instance_node_id" ] utils.filter_body(context, port_attrs, admin_only=admin_only) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) use_forbidden_mac_range = utils.pop_param(port_attrs, "use_forbidden_mac_range", False) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") if "device_id" not in port_attrs: port_attrs['device_id'] = "" device_id = port_attrs['device_id'] # NOTE(morgabra) This should be instance.node from nova, only needed # for ironic_driver. if "instance_node_id" not in port_attrs: port_attrs['instance_node_id'] = "" instance_node_id = port_attrs['instance_node_id'] net_id = port_attrs["network_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context=context, limit=None, sorts=['id'], marker=None, page_reverse=False, fields=None, id=net_id, scope=db_api.ONE) if not net: raise n_exc.NetworkNotFound(net_id=net_id) _raise_if_unauthorized(context, net) # NOTE (Perkins): If a device_id is given, try to prevent multiple ports # from being created for a device already attached to the network if device_id: existing_ports = db_api.port_find(context, network_id=net_id, device_id=device_id, scope=db_api.ONE) if existing_ports: raise n_exc.BadRequest( resource="port", msg="This device is already connected to the " "requested network via another port") # Try to fail early on quotas and save ourselves some db overhead if fixed_ips: quota.QUOTAS.limit_check(context, context.tenant_id, fixed_ips_per_port=len(fixed_ips)) if not STRATEGY.is_provider_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check(context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) network_plugin = utils.pop_param(port_attrs, "network_plugin") if not network_plugin: network_plugin = net["network_plugin"] port_attrs["network_plugin"] = network_plugin ipam_driver = _get_ipam_driver(net, port=port_attrs) net_driver = _get_net_driver(net, port=port_attrs) # NOTE(morgabra) It's possible that we select a driver different than # the one specified by the network. However, we still might need to use # this for some operations, so we also fetch it and pass it along to # the backend driver we are actually using. base_net_driver = _get_net_driver(net) # TODO(anyone): security groups are not currently supported on port create. # Please see JIRA:NCP-801 security_groups = utils.pop_param(port_attrs, "security_groups") if security_groups is not None: raise q_exc.SecurityGroupsNotImplemented() group_ids, security_groups = _make_security_group_list( context, security_groups) quota.QUOTAS.limit_check(context, context.tenant_id, security_groups_per_port=len(group_ids)) addresses = [] backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac, **kwargs): if fixed_ips: if (STRATEGY.is_provider_network(net_id) and not context.is_admin): raise n_exc.NotAuthorized() ips, subnets = split_and_validate_requested_subnets( context, net_id, segment_id, fixed_ips) kwargs["ip_addresses"] = ips kwargs["subnets"] = subnets ipam_driver.allocate_ip_address(context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac, **kwargs) @cmd_mgr.undo def _allocate_ips_undo(addr, **kwargs): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address( context, address, **kwargs) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=False, **kwargs): mac = ipam_driver.allocate_mac_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address, use_forbidden_mac_range=use_forbidden_mac_range, **kwargs) return mac @cmd_mgr.undo def _allocate_mac_undo(mac, **kwargs): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address( context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id, **kwargs): backend_port = net_driver.create_port( context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id, instance_node_id=instance_node_id, mac_address=mac, addresses=addresses, base_net_driver=base_net_driver) _filter_backend_port(backend_port) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port, **kwargs): LOG.info("Rolling back backend port...") try: backend_port_uuid = None if backend_port: backend_port_uuid = backend_port.get("uuid") net_driver.delete_port(context, backend_port_uuid) except Exception: LOG.exception("Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac, **kwargs): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create(context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port, **kwargs): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception("Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=use_forbidden_mac_range) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up"] utils.filter_body(context, port_attrs, admin_only=admin_only) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") net_id = port_attrs["network_id"] device_id = port_attrs["device_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) # NOTE (Perkins): If a device_id is given, try to prevent multiple ports # from being created for a device already attached to the network if device_id: existing_ports = db_api.port_find(context, network_id=net_id, device_id=device_id, scope=db_api.ONE) if existing_ports: raise exceptions.BadRequest( resource="port", msg="This device is already connected to the " "requested network via another port") if not STRATEGY.is_parent_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check(context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"]) net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"]) group_ids, security_groups = v.make_security_group_list( context, port["port"].pop("security_groups", None)) addresses = [] mac = None backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac): if fixed_ips: for fixed_ip in fixed_ips: subnet_id = fixed_ip.get("subnet_id") ip_address = fixed_ip.get("ip_address") if not (subnet_id and ip_address): raise exceptions.BadRequest( resource="fixed_ips", msg="subnet_id and ip_address required") ipam_driver.allocate_ip_address( context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, ip_address=ip_address, subnets=[subnet_id], mac_address=mac) else: ipam_driver.allocate_ip_address(context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac) @cmd_mgr.undo def _allocate_ips_undo(addr): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address(context, address) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address): mac = ipam_driver.allocate_mac_address(context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address) return mac @cmd_mgr.undo def _allocate_mac_undo(mac): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address( context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id): backend_port = net_driver.create_port(context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port): LOG.info("Rolling back backend port...") try: net_driver.delete_port(context, backend_port["uuid"]) except Exception: LOG.exception("Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create(context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception("Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)