diff options
Diffstat (limited to 'networking-odl/networking_odl/common')
-rw-r--r-- | networking-odl/networking_odl/common/cache.py | 3 | ||||
-rw-r--r-- | networking-odl/networking_odl/common/callback.py | 94 | ||||
-rw-r--r-- | networking-odl/networking_odl/common/client.py | 6 | ||||
-rw-r--r-- | networking-odl/networking_odl/common/config.py | 24 | ||||
-rw-r--r-- | networking-odl/networking_odl/common/constants.py | 2 | ||||
-rw-r--r-- | networking-odl/networking_odl/common/exceptions.py | 2 | ||||
-rw-r--r-- | networking-odl/networking_odl/common/filters.py | 227 | ||||
-rw-r--r-- | networking-odl/networking_odl/common/lightweight_testing.py | 5 |
8 files changed, 218 insertions, 145 deletions
diff --git a/networking-odl/networking_odl/common/cache.py b/networking-odl/networking_odl/common/cache.py index 6c44cc3..8b5287e 100644 --- a/networking-odl/networking_odl/common/cache.py +++ b/networking-odl/networking_odl/common/cache.py @@ -45,9 +45,6 @@ class CacheEntry(collections.namedtuple('CacheEntry', ['timeout', 'values'])): def __eq__(self, other): return self is other - def __ne__(self, other): - return not self.__eq__(other) - class Cache(object): '''Generic mapping class used to cache mapping diff --git a/networking-odl/networking_odl/common/callback.py b/networking-odl/networking_odl/common/callback.py index d9d168b..fe09037 100644 --- a/networking-odl/networking_odl/common/callback.py +++ b/networking-odl/networking_odl/common/callback.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import collections - from oslo_log import log as logging from neutron.callbacks import events @@ -25,49 +23,79 @@ from networking_odl.common import constants as odl_const LOG = logging.getLogger(__name__) -ODLResource = collections.namedtuple('ODLResource', ('singular', 'plural')) -_RESOURCE_MAPPING = { - resources.SECURITY_GROUP: ODLResource(odl_const.ODL_SG, odl_const.ODL_SGS), - resources.SECURITY_GROUP_RULE: ODLResource(odl_const.ODL_SG_RULE, - odl_const.ODL_SG_RULES), -} -_OPERATION_MAPPING = { - events.AFTER_CREATE: odl_const.ODL_CREATE, - events.AFTER_UPDATE: odl_const.ODL_UPDATE, - events.AFTER_DELETE: odl_const.ODL_DELETE, -} - class OdlSecurityGroupsHandler(object): - def __init__(self, odl_driver): - self.odl_driver = odl_driver - self._subscribe() + def __init__(self, odl_client, event_type="AFTER"): + self.odl_client = odl_client + self.subscribe(event_type) - def _subscribe(self): - for event in (events.AFTER_CREATE, events.AFTER_DELETE): - registry.subscribe(self.sg_callback, resources.SECURITY_GROUP, - event) - registry.subscribe(self.sg_callback, resources.SECURITY_GROUP_RULE, - event) + def sg_callback(self, resource, event, trigger, **kwargs): - registry.subscribe(self.sg_callback, resources.SECURITY_GROUP, - events.AFTER_UPDATE) + res_key_mapping = { + odl_const.ODL_SGS: odl_const.ODL_SG, + odl_const.ODL_SG_RULES: odl_const.ODL_SG_RULE, + } + res_name_mapping = { + resources.SECURITY_GROUP: odl_const.ODL_SGS, + resources.SECURITY_GROUP_RULE: odl_const.ODL_SG_RULES, + } + ops_mapping = { + events.AFTER_CREATE: odl_const.ODL_CREATE, + events.AFTER_UPDATE: odl_const.ODL_UPDATE, + events.AFTER_DELETE: odl_const.ODL_DELETE, + events.PRECOMMIT_CREATE: odl_const.ODL_CREATE, + events.PRECOMMIT_UPDATE: odl_const.ODL_UPDATE, + events.PRECOMMIT_DELETE: odl_const.ODL_DELETE, + } - def sg_callback(self, resource, event, trigger, **kwargs): + # Loop up the ODL's counterpart resource label + # e.g. resources.SECURITY_GROUP -> odl_const.ODL_SGS + # Note: 1) url will use dashes instead of underscore; + # 2) when res is a list, append 's' to odl_res_key + # Ref: https://github.com/opendaylight/neutron/blob/master + # /northbound-api/src/main/java/org/opendaylight + # /neutron/northbound/api + # /NeutronSecurityGroupRequest.java#L33 res = kwargs.get(resource) res_id = kwargs.get("%s_id" % resource) - odl_res_type = _RESOURCE_MAPPING[resource] + odl_res_type = res_name_mapping[resource] + odl_res_key = res_key_mapping[odl_res_type] + odl_ops = ops_mapping[event] + odl_res_type_uri = odl_res_type.replace('_', '-') + + if type(res) is list: + odl_res_key += "s" - odl_ops = _OPERATION_MAPPING[event] - odl_res_dict = None if res is None else {odl_res_type.singular: res} + if res is None: + odl_res_dict = None + else: + odl_res_dict = {odl_res_key: res} LOG.debug("Calling sync_from_callback with ODL_OPS (%(odl_ops)s) " "ODL_RES_TYPE (%(odl_res_type)s) RES_ID (%(res_id)s) " - "ODL_RES_DICT (%(odl_res_dict)s) KWARGS (%(kwargs)s)", + "ODL_RES_KEY (%(odl_res_key)s) RES (%(res)s) " + "KWARGS (%(kwargs)s)", {'odl_ops': odl_ops, 'odl_res_type': odl_res_type, - 'res_id': res_id, 'odl_res_dict': odl_res_dict, + 'res_id': res_id, 'odl_res_key': odl_res_key, 'res': res, 'kwargs': kwargs}) - self.odl_driver.sync_from_callback(odl_ops, odl_res_type, - res_id, odl_res_dict) + self.odl_client.sync_from_callback(odl_ops, odl_res_type_uri, res_id, + odl_res_dict) + + def subscribe(self, event_type): + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP, + getattr(events, "%s_CREATE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP, + getattr(events, "%s_UPDATE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP, + getattr(events, "%s_DELETE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP_RULE, + getattr(events, "%s_CREATE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP_RULE, + getattr(events, "%s_DELETE" % event_type)) diff --git a/networking-odl/networking_odl/common/client.py b/networking-odl/networking_odl/common/client.py index 45349e9..537665d 100644 --- a/networking-odl/networking_odl/common/client.py +++ b/networking-odl/networking_odl/common/client.py @@ -27,9 +27,9 @@ cfg.CONF.import_group('ml2_odl', 'networking_odl.common.config') class OpenDaylightRestClient(object): @classmethod - def create_client(cls, url=None): + def create_client(cls): if cfg.CONF.ml2_odl.enable_lightweight_testing: - LOG.debug("ODL lightweight testing is enabled, " + LOG.debug("ODL lightweight testing is enabled, ", "returning a OpenDaylightLwtClient instance") """Have to import at here, otherwise we create a dependency loop""" @@ -37,7 +37,7 @@ class OpenDaylightRestClient(object): cls = lwt.OpenDaylightLwtClient return cls( - url or cfg.CONF.ml2_odl.url, + cfg.CONF.ml2_odl.url, cfg.CONF.ml2_odl.username, cfg.CONF.ml2_odl.password, cfg.CONF.ml2_odl.timeout) diff --git a/networking-odl/networking_odl/common/config.py b/networking-odl/networking_odl/common/config.py index c921242..0e38e2f 100644 --- a/networking-odl/networking_odl/common/config.py +++ b/networking-odl/networking_odl/common/config.py @@ -34,34 +34,12 @@ odl_opts = [ cfg.IntOpt('retry_count', default=5, help=_("(V2 driver) Number of times to retry a row " "before failing.")), - cfg.IntOpt('maintenance_interval', default=300, - help=_("(V2 driver) Journal maintenance operations interval " - "in seconds.")), - cfg.IntOpt('completed_rows_retention', default=600, - help=_("(V2 driver) Time to keep completed rows in seconds." - "Completed rows retention will be checked every " - "maintenance_interval by the cleanup thread." - "To disable completed rows deletion " - "value should be -1")), cfg.BoolOpt('enable_lightweight_testing', default=False, help=_('Test without real ODL.')), cfg.StrOpt('port_binding_controller', default='network-topology', - help=_('Name of the controller to be used for port binding.')), - cfg.IntOpt('processing_timeout', default='100', - help=_("(V2 driver) Time in seconds to wait before a " - "processing row is marked back to pending.")), - cfg.StrOpt('odl_hostconf_uri', - help=_("Path for ODL host configuration REST interface"), - default="/restconf/operational/neutron:neutron/hostconfigs"), - cfg.IntOpt('restconf_poll_interval', default=30, - help=_("Poll interval in seconds for getting ODL hostconfig")), - + help=_('Name of the controller to be used for port binding.')) ] cfg.CONF.register_opts(odl_opts, "ml2_odl") - - -def list_opts(): - return [('ml2_odl', odl_opts)] diff --git a/networking-odl/networking_odl/common/constants.py b/networking-odl/networking_odl/common/constants.py index 50c0117..9fed790 100644 --- a/networking-odl/networking_odl/common/constants.py +++ b/networking-odl/networking_odl/common/constants.py @@ -24,10 +24,8 @@ ODL_SGS = 'security_groups' ODL_SG_RULE = 'security_group_rule' ODL_SG_RULES = 'security_group_rules' ODL_ROUTER = 'router' -ODL_ROUTERS = 'routers' ODL_ROUTER_INTF = 'router_interface' ODL_FLOATINGIP = 'floatingip' -ODL_FLOATINGIPS = 'floatingips' ODL_LOADBALANCER = 'loadbalancer' ODL_LOADBALANCERS = 'loadbalancers' diff --git a/networking-odl/networking_odl/common/exceptions.py b/networking-odl/networking_odl/common/exceptions.py index f174c10..59956b1 100644 --- a/networking-odl/networking_odl/common/exceptions.py +++ b/networking-odl/networking_odl/common/exceptions.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutron_lib import exceptions as exc +from neutron.common import exceptions as exc class OpendaylightAuthError(exc.NeutronException): diff --git a/networking-odl/networking_odl/common/filters.py b/networking-odl/networking_odl/common/filters.py index fb42a0e..340fa4d 100644 --- a/networking-odl/networking_odl/common/filters.py +++ b/networking-odl/networking_odl/common/filters.py @@ -12,85 +12,158 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import abc +import six + from networking_odl.common import constants as odl_const from networking_odl.common import utils as odl_utils -def _filter_unmapped_null(resource_dict, unmapped_keys): - # NOTE(yamahata): bug work around - # https://bugs.eclipse.org/bugs/show_bug.cgi?id=475475 - # Null-value for an unmapped element causes next mapped - # collection to contain a null value - # JSON: { "unmappedField": null, "mappedCollection": [ "a" ] } - # - # Java Object: - # class Root { - # Collection<String> mappedCollection = new ArrayList<String>; - # } - # - # Result: - # Field B contains one element; null - # - # TODO(yamahata): update along side with neutron and ODL - # add when neutron adds more extensions - # delete when ODL neutron northbound supports it - # TODO(yamahata): do same thing for other resources - keys_to_del = [key for key in unmapped_keys - if resource_dict.get(key) is None] - if keys_to_del: - odl_utils.try_del(resource_dict, keys_to_del) - - -_NETWORK_UNMAPPED_KEYS = ['qos_policy_id'] -_PORT_UNMAPPED_KEYS = ['binding:profile', 'dns_name', - 'port_security_enabled', 'qos_policy_id'] - - -def _filter_network_create(network): - odl_utils.try_del(network, ['status', 'subnets']) - _filter_unmapped_null(network, _NETWORK_UNMAPPED_KEYS) - - -def _filter_network_update(network): - odl_utils.try_del(network, ['id', 'status', 'subnets', 'tenant_id']) - _filter_unmapped_null(network, _NETWORK_UNMAPPED_KEYS) - - -def _filter_subnet_update(subnet): - odl_utils.try_del(subnet, ['id', 'network_id', 'ip_version', 'cidr', - 'allocation_pools', 'tenant_id']) - - -def _filter_port_create(port): - """Filter out port attributes not required for a create.""" - odl_utils.try_del(port, ['status']) - _filter_unmapped_null(port, _PORT_UNMAPPED_KEYS) - - -def _filter_port_update(port): - """Filter out port attributes for an update operation.""" - odl_utils.try_del(port, ['network_id', 'id', 'status', 'mac_address', - 'tenant_id', 'fixed_ips']) - _filter_unmapped_null(port, _PORT_UNMAPPED_KEYS) - - -def _filter_router_update(router): - """Filter out attributes for an update operation.""" - odl_utils.try_del(router, ['id', 'tenant_id', 'status']) - - -_FILTER_MAP = { - (odl_const.ODL_NETWORK, odl_const.ODL_CREATE): _filter_network_create, - (odl_const.ODL_NETWORK, odl_const.ODL_UPDATE): _filter_network_update, - (odl_const.ODL_SUBNET, odl_const.ODL_UPDATE): _filter_subnet_update, - (odl_const.ODL_PORT, odl_const.ODL_CREATE): _filter_port_create, - (odl_const.ODL_PORT, odl_const.ODL_UPDATE): _filter_port_update, - (odl_const.ODL_ROUTER, odl_const.ODL_UPDATE): _filter_router_update, +@six.add_metaclass(abc.ABCMeta) +class ResourceFilterBase(object): + @staticmethod + @abc.abstractmethod + def filter_create_attributes(resource): + pass + + @staticmethod + @abc.abstractmethod + def filter_update_attributes(resource): + pass + + +class NetworkFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(network): + """Filter out network attributes not required for a create.""" + odl_utils.try_del(network, ['status', 'subnets']) + + @staticmethod + def filter_update_attributes(network): + """Filter out network attributes for an update operation.""" + odl_utils.try_del(network, ['id', 'status', 'subnets', 'tenant_id']) + + +class SubnetFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(subnet): + """Filter out subnet attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(subnet): + """Filter out subnet attributes for an update operation.""" + odl_utils.try_del(subnet, ['id', 'network_id', 'ip_version', 'cidr', + 'allocation_pools', 'tenant_id']) + + +class PortFilter(ResourceFilterBase): + @staticmethod + def _filter_unmapped_null(port): + # NOTE(yamahata): bug work around + # https://bugs.eclipse.org/bugs/show_bug.cgi?id=475475 + # Null-value for an unmapped element causes next mapped + # collection to contain a null value + # JSON: { "unmappedField": null, "mappedCollection": [ "a" ] } + # + # Java Object: + # class Root { + # Collection<String> mappedCollection = new ArrayList<String>; + # } + # + # Result: + # Field B contains one element; null + # + # TODO(yamahata): update along side with neutron and ODL + # add when neutron adds more extensions + # delete when ODL neutron northbound supports it + # TODO(yamahata): do same thing for other resources + unmapped_keys = ['dns_name', 'port_security_enabled', + 'binding:profile'] + keys_to_del = [key for key in unmapped_keys if port.get(key) is None] + if keys_to_del: + odl_utils.try_del(port, keys_to_del) + + @classmethod + def filter_create_attributes(cls, port): + """Filter out port attributes not required for a create.""" + cls._filter_unmapped_null(port) + odl_utils.try_del(port, ['status']) + + @classmethod + def filter_update_attributes(cls, port): + """Filter out port attributes for an update operation.""" + cls._filter_unmapped_null(port) + odl_utils.try_del(port, ['network_id', 'id', 'status', 'mac_address', + 'tenant_id', 'fixed_ips']) + + +class SecurityGroupFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(sg): + """Filter out security-group attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(sg): + """Filter out security-group attributes for an update operation.""" + pass + + +class SecurityGroupRuleFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(sg_rule): + """Filter out sg-rule attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(sg_rule): + """Filter out sg-rule attributes for an update operation.""" + pass + + +class RouterFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(router): + """Filter out attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(router): + """Filter out attributes for an update operation.""" + odl_utils.try_del(router, ['id', 'tenant_id', 'status']) + + +class FloatingIPFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(floatingip): + """Filter out attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(floatingip): + """Filter out attributes for an update operation.""" + pass + + +class RouterIntfFilter(ResourceFilterBase): + @staticmethod + def filter_add_attributes(routerintf): + """Filter out attributes not required for a create.""" + pass + + @staticmethod + def filter_remove_attributes(routerintf): + """Filter out attributes for an update operation.""" + pass + +FILTER_MAP = { + odl_const.ODL_NETWORK: NetworkFilter, + odl_const.ODL_SUBNET: SubnetFilter, + odl_const.ODL_PORT: PortFilter, + odl_const.ODL_ROUTER: RouterFilter, + odl_const.ODL_ROUTER_INTF: RouterIntfFilter, + odl_const.ODL_FLOATINGIP: FloatingIPFilter, + odl_const.ODL_SG: SecurityGroupFilter, + odl_const.ODL_SG_RULE: SecurityGroupRuleFilter, } - - -def filter_for_odl(object_type, operation, data): - """Filter out the attributed before sending the data to ODL""" - filter_key = (object_type, operation) - if filter_key in _FILTER_MAP: - _FILTER_MAP[filter_key](data) diff --git a/networking-odl/networking_odl/common/lightweight_testing.py b/networking-odl/networking_odl/common/lightweight_testing.py index 3d0cf2e..3f9c2bc 100644 --- a/networking-odl/networking_odl/common/lightweight_testing.py +++ b/networking-odl/networking_odl/common/lightweight_testing.py @@ -20,7 +20,6 @@ import six from oslo_log import log as logging from oslo_serialization import jsonutils -from networking_odl._i18n import _ from networking_odl.common import client from networking_odl.common import constants as odl_const @@ -69,7 +68,7 @@ class OpenDaylightLwtClient(client.OpenDaylightRestClient): """No ID in URL, elements in resource_list must have ID""" if resource_list is None: - raise ValueError(_("resource_list can not be None")) + raise ValueError("resource_list can not be None") for resource in resource_list: if resource['id'] in resource_dict: @@ -88,7 +87,7 @@ class OpenDaylightLwtClient(client.OpenDaylightRestClient): resource_id = cls._get_resource_id(urlpath) if resource_list is None: - raise ValueError(_("resource_list can not be None")) + raise ValueError("resource_list can not be None") if resource_id and len(resource_list) != 1: LOG.debug("Updating %s with multiple resources", urlpath) |