summaryrefslogtreecommitdiffstats
path: root/networking-odl/networking_odl/common
diff options
context:
space:
mode:
Diffstat (limited to 'networking-odl/networking_odl/common')
-rw-r--r--networking-odl/networking_odl/common/cache.py3
-rw-r--r--networking-odl/networking_odl/common/callback.py94
-rw-r--r--networking-odl/networking_odl/common/client.py6
-rw-r--r--networking-odl/networking_odl/common/config.py24
-rw-r--r--networking-odl/networking_odl/common/constants.py2
-rw-r--r--networking-odl/networking_odl/common/exceptions.py2
-rw-r--r--networking-odl/networking_odl/common/filters.py227
-rw-r--r--networking-odl/networking_odl/common/lightweight_testing.py5
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)