diff --git a/doc/source/devref/contribute.rst b/doc/source/devref/contribute.rst index 5bc068c0c40..726da0fe561 100644 --- a/doc/source/devref/contribute.rst +++ b/doc/source/devref/contribute.rst @@ -401,7 +401,7 @@ The following chart captures the following aspects: +-------------------------------+-----------------------+-----------+------------------+---------+--------------+ | networking-hyperv_ | | | | | | +-------------------------------+-----------------------+-----------+------------------+---------+--------------+ -| networking-metaplugin_ | | | | | | +| networking-metaplugin_ | core | no | no | [C] | Kilo | +-------------------------------+-----------------------+-----------+------------------+---------+--------------+ | networking-midonet_ | | | | | | +-------------------------------+-----------------------+-----------+------------------+---------+--------------+ @@ -440,6 +440,11 @@ Arista .. _networking-metaplugin: +Metaplugin +---------- + +* Git: https://github.com/ntt-sic/networking-metaplugin + .. _networking-midonet: .. _networking-mlnx: diff --git a/neutron/plugins/metaplugin/README b/neutron/plugins/metaplugin/README index 8dbc47756ad..e2140b1e1c0 100644 --- a/neutron/plugins/metaplugin/README +++ b/neutron/plugins/metaplugin/README @@ -1,92 +1,6 @@ -# -- Background - -This plugin supports multiple plugin at same time. This plugin is for L3 connectivility -between networks which are realized by different plugins.This plugin adds new attributes 'flavor:network' and 'flavor:router". -flavor:network corresponds to specific l2 plugin ( flavor-plugin mapping could be configurable by plugin_list config. -flavor:router corresponds to specific l3 plugin ( flavor-plugin mapping could be configurable by l3_plugin_list config. Note that Metaplugin can provide l3 functionaliteis for l2 plugin which didn't support l3 extension yet. -This plugin also support extensions. We can map extension to plugin by using extension_map config. - -[database] -# This line MUST be changed to actually run the plugin. -# Example: -# connection = mysql://root:nova@127.0.0.1:3306/ovs_neutron -# Replace 127.0.0.1 above with the IP address of the database used by the -# main neutron server. (Leave it as is if the database runs on this host.) -connection = mysql://root:password@localhost/neutron_metaplugin?charset=utf8 - -# Database reconnection retry times - in event connectivity is lost -# set to -1 implgies an infinite retry count -# max_retries = 10 -# Database reconnection interval in seconds - in event connectivity is lost -retry_interval = 2 - -[meta] -## This is list of flavor:neutron_plugins -# extension method is used in the order of this list -plugin_list= 'openvswitch:neutron.plugins.openvswitch.ovs_neutron_plugin.OVSneutronPluginV2,linuxbridge:neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' -# plugin for l3 -l3_plugin_list= 'openvswitch:neutron.plugins.openvswitch.ovs_neutron_plugin.OVSneutronPluginV2,linuxbridge:neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' - -# Default value of flavor -default_flavor = 'openvswitch' -# Default value for l3 -default_l3_flavor = 'openvswitch' - -# supported extensions -supported_extension_aliases = 'providernet' -# specific method map for each flavor to extensions -extension_map = 'get_port_stats:nvp' - -# -- BridgeDriver Configration -# In order to use metaplugin, you should use MetaDriver. Following configation is needed. - -[DEFAULT] -# Meta Plugin -# Mapping between flavor and driver -meta_flavor_driver_mappings = openvswitch:neutron.agent.linux.interface.OVSInterfaceDriver, linuxbridge:neutron.agent.linux.interface.BridgeInterfaceDriver -# interface driver for MetaPlugin -interface_driver = neutron.agent.linux.interface.MetaInterfaceDriver - -[proxy] -auth_url = http://10.0.0.1:35357/v2.0 -auth_region = RegionOne -admin_tenant_name = service -admin_user = neutron -admin_password = password - - -# -- Agent -Agents for Metaplugin are in neutron/plugins/metaplugin/agent -linuxbridge_neutron_agent and ovs_neutron_agent is available. - -# -- Extensions - -- flavor -MetaPlugin supports flavor and provider net extension. -Metaplugin select plugin_list using flavor. -One plugin may use multiple flavor value. If the plugin support flavor, it may provide -multiple flavor of network. - -- Attribute extension -Each plugin can use attribute extension such as provider_net, if you specify that in supported_extension_aliases. - -- providernet -Vlan ID range of each plugin should be different, since Metaplugin dose not manage that. - -#- limitations - -Basically, All plugin should inherit NeutronDbPluginV2. -Metaplugin assumes all plugin share same Database especially for IPAM part in NeutronV2 API. -You can use another plugin if you use ProxyPluginV2, which proxies request to the another neutron server. - -Example flavor configration for ProxyPluginV2 - -meta_flavor_driver_mappings = "openvswitch:neutron.agent.linux.interface.OVSInterfaceDriver,proxy:neutron.plugins.metaplugin.proxy_neutron_plugin.ProxyPluginV2" - -- Limited L3 support -In folsom version, l3 is an extension. There is no way to extend exntension attributes. -so you can set flavor:router value but you can't get flavor:router value in API output. -L3 agent dont's support flavor:router. - - +# NOTE +The main source codes of Metaplugin is now in https://github.com/ntt-sic/networking-metaplugin. +They were moved from Neutron tree to there according to core-vendor-decomposition. +Defining config and DB are still here according to the decomposition policy. +Codes of 'flavor' extension and interface driver used by *-agent remain in Neutron tree too. diff --git a/neutron/plugins/metaplugin/meta_db_v2.py b/neutron/plugins/metaplugin/meta_db_v2.py deleted file mode 100644 index 939442eb9e8..00000000000 --- a/neutron/plugins/metaplugin/meta_db_v2.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2012, Nachi Ueno, NTT MCL, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy.orm import exc - -from neutron.plugins.metaplugin import meta_models_v2 - - -def get_flavor_by_network(session, net_id): - try: - binding = (session.query(meta_models_v2.NetworkFlavor). - filter_by(network_id=net_id). - one()) - except exc.NoResultFound: - return None - return binding.flavor - - -def add_network_flavor_binding(session, flavor, net_id): - binding = meta_models_v2.NetworkFlavor(flavor=flavor, network_id=net_id) - session.add(binding) - return binding - - -def get_flavor_by_router(session, router_id): - try: - binding = (session.query(meta_models_v2.RouterFlavor). - filter_by(router_id=router_id). - one()) - except exc.NoResultFound: - return None - return binding.flavor - - -def add_router_flavor_binding(session, flavor, router_id): - binding = meta_models_v2.RouterFlavor(flavor=flavor, router_id=router_id) - session.add(binding) - return binding diff --git a/neutron/plugins/metaplugin/meta_neutron_plugin.py b/neutron/plugins/metaplugin/meta_neutron_plugin.py index 35233f88934..9ded159d45f 100644 --- a/neutron/plugins/metaplugin/meta_neutron_plugin.py +++ b/neutron/plugins/metaplugin/meta_neutron_plugin.py @@ -13,402 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg -from oslo_utils import importutils - -from neutron.common import exceptions as exc -from neutron.common import topics -from neutron import context as neutron_context -from neutron.db import db_base_plugin_v2 -from neutron.db import external_net_db -from neutron.db import extraroute_db -from neutron.db import l3_db -from neutron.db import models_v2 -from neutron.extensions import flavor as ext_flavor -from neutron.i18n import _LE -from neutron.openstack.common import log as logging -from neutron.plugins.metaplugin.common import config # noqa -from neutron.plugins.metaplugin import meta_db_v2 -from neutron.plugins.metaplugin import meta_models_v2 +from metaplugin.plugin import meta_neutron_plugin -LOG = logging.getLogger(__name__) - - -# Hooks used to select records which belong a target plugin. -def _meta_network_model_hook(context, original_model, query): - return query.outerjoin(meta_models_v2.NetworkFlavor, - meta_models_v2.NetworkFlavor.network_id == - models_v2.Network.id) - - -def _meta_port_model_hook(context, original_model, query): - return query.join(meta_models_v2.NetworkFlavor, - meta_models_v2.NetworkFlavor.network_id == - models_v2.Port.network_id) - - -def _meta_flavor_filter_hook(query, filters): - if ext_flavor.FLAVOR_NETWORK in filters: - return query.filter(meta_models_v2.NetworkFlavor.flavor == - filters[ext_flavor.FLAVOR_NETWORK][0]) - return query - - -# Metaplugin Exceptions -class FlavorNotFound(exc.NotFound): - message = _("Flavor %(flavor)s could not be found") - - -class FaildToAddFlavorBinding(exc.NeutronException): - message = _("Failed to add flavor binding") - - -class MetaPluginV2(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - extraroute_db.ExtraRoute_db_mixin): - - def __init__(self, configfile=None): - super(MetaPluginV2, self).__init__() - LOG.debug("Start initializing metaplugin") - self.supported_extension_aliases = ['flavor', 'external-net'] - if cfg.CONF.META.supported_extension_aliases: - cfg_aliases = cfg.CONF.META.supported_extension_aliases.split(',') - self.supported_extension_aliases += cfg_aliases - - # Ignore config option overapping - def _is_opt_registered(opts, opt): - if opt.dest in opts: - return True - else: - return False - - cfg._is_opt_registered = _is_opt_registered - - self.plugins = {} - - plugin_list = [plugin_set.split(':') - for plugin_set - in cfg.CONF.META.plugin_list.split(',')] - self.rpc_flavor = cfg.CONF.META.rpc_flavor - topic_save = topics.PLUGIN - topic_fake = topic_save + '-metaplugin' - for flavor, plugin_provider in plugin_list: - # Rename topic used by a plugin other than rpc_flavor during - # loading the plugin instance if rpc_flavor is specified. - # This enforces the plugin specified by rpc_flavor is only - # consumer of 'q-plugin'. It is a bit tricky but there is no - # bad effect. - if self.rpc_flavor and self.rpc_flavor != flavor: - topics.PLUGIN = topic_fake - self.plugins[flavor] = self._load_plugin(plugin_provider) - topics.PLUGIN = topic_save - - self.l3_plugins = {} - if cfg.CONF.META.l3_plugin_list: - l3_plugin_list = [plugin_set.split(':') - for plugin_set - in cfg.CONF.META.l3_plugin_list.split(',')] - for flavor, plugin_provider in l3_plugin_list: - if flavor in self.plugins: - self.l3_plugins[flavor] = self.plugins[flavor] - else: - # For l3 only plugin - self.l3_plugins[flavor] = self._load_plugin( - plugin_provider) - - self.default_flavor = cfg.CONF.META.default_flavor - if self.default_flavor not in self.plugins: - raise exc.Invalid(_('default_flavor %s is not plugin list') % - self.default_flavor) - - if self.l3_plugins: - self.default_l3_flavor = cfg.CONF.META.default_l3_flavor - if self.default_l3_flavor not in self.l3_plugins: - raise exc.Invalid(_('default_l3_flavor %s is not plugin list') - % self.default_l3_flavor) - self.supported_extension_aliases += ['router', 'ext-gw-mode', - 'extraroute'] - - if self.rpc_flavor and self.rpc_flavor not in self.plugins: - raise exc.Invalid(_('rpc_flavor %s is not plugin list') % - self.rpc_flavor) - - self.extension_map = {} - if not cfg.CONF.META.extension_map == '': - extension_list = [method_set.split(':') - for method_set - in cfg.CONF.META.extension_map.split(',')] - for method_name, flavor in extension_list: - self.extension_map[method_name] = flavor - - # Register hooks. - # The hooks are applied for each target plugin instance when - # calling the base class to get networks/ports so that only records - # which belong to the plugin are selected. - #NOTE: Doing registration here (within __init__()) is to avoid - # registration when merely importing this file. This is only - # for running whole unit tests. - db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook( - models_v2.Network, - 'metaplugin_net', - _meta_network_model_hook, - None, - _meta_flavor_filter_hook) - db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook( - models_v2.Port, - 'metaplugin_port', - _meta_port_model_hook, - None, - _meta_flavor_filter_hook) - - def _load_plugin(self, plugin_provider): - LOG.debug("Plugin location: %s", plugin_provider) - plugin_klass = importutils.import_class(plugin_provider) - return plugin_klass() - - def _get_plugin(self, flavor): - if flavor not in self.plugins: - raise FlavorNotFound(flavor=flavor) - return self.plugins[flavor] - - def _get_l3_plugin(self, flavor): - if flavor not in self.l3_plugins: - raise FlavorNotFound(flavor=flavor) - return self.l3_plugins[flavor] - - def __getattr__(self, key): - # At first, try to pickup extension command from extension_map - - if key in self.extension_map: - flavor = self.extension_map[key] - plugin = self._get_plugin(flavor) - if plugin and hasattr(plugin, key): - return getattr(plugin, key) - - # Second, try to match extension method in order of plugin list - for flavor, plugin in self.plugins.items(): - if hasattr(plugin, key): - return getattr(plugin, key) - - # if no plugin support the method, then raise - raise AttributeError() - - def _extend_network_dict(self, context, network): - flavor = self._get_flavor_by_network_id(context, network['id']) - network[ext_flavor.FLAVOR_NETWORK] = flavor - - def start_rpc_listeners(self): - return self.plugins[self.rpc_flavor].start_rpc_listeners() - - def rpc_workers_supported(self): - #NOTE: If a plugin which supports multiple RPC workers is desired - # to handle RPC, rpc_flavor must be specified. - return (self.rpc_flavor and - self.plugins[self.rpc_flavor].rpc_workers_supported()) - - def create_network(self, context, network): - n = network['network'] - flavor = n.get(ext_flavor.FLAVOR_NETWORK) - if str(flavor) not in self.plugins: - flavor = self.default_flavor - plugin = self._get_plugin(flavor) - net = plugin.create_network(context, network) - LOG.debug("Created network: %(net_id)s with flavor " - "%(flavor)s", {'net_id': net['id'], 'flavor': flavor}) - try: - meta_db_v2.add_network_flavor_binding(context.session, - flavor, str(net['id'])) - except Exception: - LOG.exception(_LE('Failed to add flavor bindings')) - plugin.delete_network(context, net['id']) - raise FaildToAddFlavorBinding() - - LOG.debug("Created network: %s", net['id']) - self._extend_network_dict(context, net) - return net - - def update_network(self, context, id, network): - flavor = meta_db_v2.get_flavor_by_network(context.session, id) - plugin = self._get_plugin(flavor) - return plugin.update_network(context, id, network) - - def delete_network(self, context, id): - flavor = meta_db_v2.get_flavor_by_network(context.session, id) - plugin = self._get_plugin(flavor) - return plugin.delete_network(context, id) - - def get_network(self, context, id, fields=None): - flavor = meta_db_v2.get_flavor_by_network(context.session, id) - plugin = self._get_plugin(flavor) - net = plugin.get_network(context, id, fields) - net['id'] = id - if not fields or ext_flavor.FLAVOR_NETWORK in fields: - self._extend_network_dict(context, net) - if fields and 'id' not in fields: - del net['id'] - return net - - def get_networks(self, context, filters=None, fields=None): - nets = [] - for flavor, plugin in self.plugins.items(): - if (filters and ext_flavor.FLAVOR_NETWORK in filters and - flavor not in filters[ext_flavor.FLAVOR_NETWORK]): - continue - if filters: - #NOTE: copy each time since a target plugin may modify - # plugin_filters. - plugin_filters = filters.copy() - else: - plugin_filters = {} - plugin_filters[ext_flavor.FLAVOR_NETWORK] = [flavor] - plugin_nets = plugin.get_networks(context, plugin_filters, fields) - for net in plugin_nets: - if not fields or ext_flavor.FLAVOR_NETWORK in fields: - net[ext_flavor.FLAVOR_NETWORK] = flavor - nets.append(net) - return nets - - def _get_flavor_by_network_id(self, context, network_id): - return meta_db_v2.get_flavor_by_network(context.session, network_id) - - def _get_flavor_by_router_id(self, context, router_id): - return meta_db_v2.get_flavor_by_router(context.session, router_id) - - def _get_plugin_by_network_id(self, context, network_id): - flavor = self._get_flavor_by_network_id(context, network_id) - return self._get_plugin(flavor) - - def create_port(self, context, port): - p = port['port'] - if 'network_id' not in p: - raise exc.NotFound() - plugin = self._get_plugin_by_network_id(context, p['network_id']) - return plugin.create_port(context, port) - - def update_port(self, context, id, port): - port_in_db = self._get_port(context, id) - plugin = self._get_plugin_by_network_id(context, - port_in_db['network_id']) - return plugin.update_port(context, id, port) - - def delete_port(self, context, id, l3_port_check=True): - port_in_db = self._get_port(context, id) - plugin = self._get_plugin_by_network_id(context, - port_in_db['network_id']) - return plugin.delete_port(context, id, l3_port_check) - - # This is necessary since there is a case that - # NeutronManager.get_plugin()._make_port_dict is called. - def _make_port_dict(self, port): - context = neutron_context.get_admin_context() - plugin = self._get_plugin_by_network_id(context, - port['network_id']) - return plugin._make_port_dict(port) - - def get_port(self, context, id, fields=None): - port_in_db = self._get_port(context, id) - plugin = self._get_plugin_by_network_id(context, - port_in_db['network_id']) - return plugin.get_port(context, id, fields) - - def get_ports(self, context, filters=None, fields=None): - all_ports = [] - for flavor, plugin in self.plugins.items(): - if filters: - #NOTE: copy each time since a target plugin may modify - # plugin_filters. - plugin_filters = filters.copy() - else: - plugin_filters = {} - plugin_filters[ext_flavor.FLAVOR_NETWORK] = [flavor] - ports = plugin.get_ports(context, plugin_filters, fields) - all_ports += ports - return all_ports - - def create_subnet(self, context, subnet): - s = subnet['subnet'] - if 'network_id' not in s: - raise exc.NotFound() - plugin = self._get_plugin_by_network_id(context, - s['network_id']) - return plugin.create_subnet(context, subnet) - - def update_subnet(self, context, id, subnet): - s = self.get_subnet(context, id) - plugin = self._get_plugin_by_network_id(context, - s['network_id']) - return plugin.update_subnet(context, id, subnet) - - def delete_subnet(self, context, id): - s = self.get_subnet(context, id) - plugin = self._get_plugin_by_network_id(context, - s['network_id']) - return plugin.delete_subnet(context, id) - - def _extend_router_dict(self, context, router): - flavor = self._get_flavor_by_router_id(context, router['id']) - router[ext_flavor.FLAVOR_ROUTER] = flavor - - def create_router(self, context, router): - r = router['router'] - flavor = r.get(ext_flavor.FLAVOR_ROUTER) - if str(flavor) not in self.l3_plugins: - flavor = self.default_l3_flavor - plugin = self._get_l3_plugin(flavor) - r_in_db = plugin.create_router(context, router) - LOG.debug("Created router: %(router_id)s with flavor " - "%(flavor)s", - {'router_id': r_in_db['id'], 'flavor': flavor}) - try: - meta_db_v2.add_router_flavor_binding(context.session, - flavor, str(r_in_db['id'])) - except Exception: - LOG.exception(_LE('Failed to add flavor bindings')) - plugin.delete_router(context, r_in_db['id']) - raise FaildToAddFlavorBinding() - - LOG.debug("Created router: %s", r_in_db['id']) - self._extend_router_dict(context, r_in_db) - return r_in_db - - def update_router(self, context, id, router): - flavor = meta_db_v2.get_flavor_by_router(context.session, id) - plugin = self._get_l3_plugin(flavor) - return plugin.update_router(context, id, router) - - def delete_router(self, context, id): - flavor = meta_db_v2.get_flavor_by_router(context.session, id) - plugin = self._get_l3_plugin(flavor) - return plugin.delete_router(context, id) - - def get_router(self, context, id, fields=None): - flavor = meta_db_v2.get_flavor_by_router(context.session, id) - plugin = self._get_l3_plugin(flavor) - router = plugin.get_router(context, id, fields) - if not fields or ext_flavor.FLAVOR_ROUTER in fields: - self._extend_router_dict(context, router) - return router - - def get_routers_with_flavor(self, context, filters=None, - fields=None): - collection = self._model_query(context, l3_db.Router) - r_model = meta_models_v2.RouterFlavor - collection = collection.join(r_model, - l3_db.Router.id == r_model.router_id) - if filters: - for key, value in filters.iteritems(): - if key == ext_flavor.FLAVOR_ROUTER: - column = meta_models_v2.RouterFlavor.flavor - else: - column = getattr(l3_db.Router, key, None) - if column: - collection = collection.filter(column.in_(value)) - return [self._make_router_dict(c, fields) for c in collection] - - def get_routers(self, context, filters=None, fields=None): - routers = self.get_routers_with_flavor(context, filters, - None) - return [self.get_router(context, router['id'], - fields) - for router in routers] +MetaPluginV2 = meta_neutron_plugin.MetaPluginV2 diff --git a/neutron/plugins/metaplugin/proxy_neutron_plugin.py b/neutron/plugins/metaplugin/proxy_neutron_plugin.py index 3b9dca39e10..353dee242ac 100644 --- a/neutron/plugins/metaplugin/proxy_neutron_plugin.py +++ b/neutron/plugins/metaplugin/proxy_neutron_plugin.py @@ -13,123 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - -from neutron.db import db_base_plugin_v2 -from neutron.db import external_net_db -from neutron.db import l3_db -from neutron.i18n import _LE, _LW -from neutron.openstack.common import log as logging -from neutronclient.common import exceptions -from neutronclient.v2_0 import client +from metaplugin.plugin import proxy_neutron_plugin -LOG = logging.getLogger(__name__) - - -class ProxyPluginV2(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - l3_db.L3_NAT_db_mixin): - supported_extension_aliases = ["external-net", "router"] - - def __init__(self, configfile=None): - super(ProxyPluginV2, self).__init__() - self.neutron = client.Client( - username=cfg.CONF.PROXY.admin_user, - password=cfg.CONF.PROXY.admin_password, - tenant_name=cfg.CONF.PROXY.admin_tenant_name, - auth_url=cfg.CONF.PROXY.auth_url, - auth_strategy=cfg.CONF.PROXY.auth_strategy, - region_name=cfg.CONF.PROXY.auth_region - ) - - def _get_client(self): - return self.neutron - - def create_subnet(self, context, subnet): - subnet_remote = self._get_client().create_subnet(subnet) - subnet['subnet']['id'] = subnet_remote['id'] - tenant_id = self._get_tenant_id_for_create(context, subnet['subnet']) - subnet['subnet']['tenant_id'] = tenant_id - try: - subnet_in_db = super(ProxyPluginV2, self).create_subnet( - context, subnet) - except Exception: - self._get_client().delete_subnet(subnet_remote['id']) - return subnet_in_db - - def update_subnet(self, context, id, subnet): - subnet_in_db = super(ProxyPluginV2, self).update_subnet( - context, id, subnet) - try: - self._get_client().update_subnet(id, subnet) - except Exception as e: - LOG.error(_LE("Update subnet failed: %s"), e) - return subnet_in_db - - def delete_subnet(self, context, id): - try: - self._get_client().delete_subnet(id) - except exceptions.NotFound: - LOG.warn(_LW("Subnet in remote have already deleted")) - return super(ProxyPluginV2, self).delete_subnet(context, id) - - def create_network(self, context, network): - network_remote = self._get_client().create_network(network) - network['network']['id'] = network_remote['id'] - tenant_id = self._get_tenant_id_for_create(context, network['network']) - network['network']['tenant_id'] = tenant_id - try: - network_in_db = super(ProxyPluginV2, self).create_network( - context, network) - except Exception: - self._get_client().delete_network(network_remote['id']) - return network_in_db - - def update_network(self, context, id, network): - network_in_db = super(ProxyPluginV2, self).update_network( - context, id, network) - try: - self._get_client().update_network(id, network) - except Exception as e: - LOG.error(_LE("Update network failed: %s"), e) - return network_in_db - - def delete_network(self, context, id): - try: - self._get_client().delete_network(id) - except exceptions.NetworkNotFoundClient: - LOG.warn(_LW("Network in remote have already deleted")) - return super(ProxyPluginV2, self).delete_network(context, id) - - def create_port(self, context, port): - port_remote = self._get_client().create_port(port) - port['port']['id'] = port_remote['id'] - tenant_id = self._get_tenant_id_for_create(context, port['port']) - port['port']['tenant_id'] = tenant_id - try: - port_in_db = super(ProxyPluginV2, self).create_port( - context, port) - except Exception: - self._get_client().delete_port(port_remote['id']) - return port_in_db - - def update_port(self, context, id, port): - port_in_db = super(ProxyPluginV2, self).update_port( - context, id, port) - try: - self._get_client().update_port(id, port) - except Exception as e: - LOG.error(_LE("Update port failed: %s"), e) - return port_in_db - - def delete_port(self, context, id, l3_port_check=True): - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - self.disassociate_floatingips(context, id) - - try: - self._get_client().delete_port(id) - except exceptions.PortNotFoundClient: - LOG.warn(_LW("Port in remote have already deleted")) - return super(ProxyPluginV2, self).delete_port(context, id) +ProxyPluginV2 = proxy_neutron_plugin.ProxyPluginV2 diff --git a/neutron/tests/unit/metaplugin/__init__.py b/neutron/tests/unit/metaplugin/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/neutron/tests/unit/metaplugin/fake_plugin.py b/neutron/tests/unit/metaplugin/fake_plugin.py deleted file mode 100644 index 0fac458091f..00000000000 --- a/neutron/tests/unit/metaplugin/fake_plugin.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2012, Nachi Ueno, NTT MCL, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutron.db import db_base_plugin_v2 -from neutron.db import external_net_db -from neutron.db import l3_gwmode_db - - -class Fake1(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - l3_gwmode_db.L3_NAT_db_mixin): - supported_extension_aliases = ['external-net', 'router'] - - def fake_func(self): - return 'fake1' - - def create_network(self, context, network): - session = context.session - with session.begin(subtransactions=True): - net = super(Fake1, self).create_network(context, network) - self._process_l3_create(context, net, network['network']) - return net - - def update_network(self, context, id, network): - session = context.session - with session.begin(subtransactions=True): - net = super(Fake1, self).update_network(context, id, - network) - self._process_l3_update(context, net, network['network']) - return net - - def delete_network(self, context, id): - session = context.session - with session.begin(subtransactions=True): - self._process_l3_delete(context, id) - return super(Fake1, self).delete_network(context, id) - - def create_port(self, context, port): - port = super(Fake1, self).create_port(context, port) - return port - - def create_subnet(self, context, subnet): - subnet = super(Fake1, self).create_subnet(context, subnet) - return subnet - - def update_port(self, context, id, port): - port = super(Fake1, self).update_port(context, id, port) - return port - - def delete_port(self, context, id, l3_port_check=True): - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - self.disassociate_floatingips(context, id) - return super(Fake1, self).delete_port(context, id) - - -class Fake2(Fake1): - def fake_func(self): - return 'fake2' - - def fake_func2(self): - return 'fake2' - - def start_rpc_listeners(self): - # return value is only used to confirm this method was called. - return 'OK' diff --git a/neutron/tests/unit/metaplugin/test_basic.py b/neutron/tests/unit/metaplugin/test_basic.py deleted file mode 100644 index 5f204695766..00000000000 --- a/neutron/tests/unit/metaplugin/test_basic.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2012 OpenStack Foundation. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from neutron.tests.unit.metaplugin import test_metaplugin -from neutron.tests.unit import test_db_plugin as test_plugin -from neutron.tests.unit import test_l3_plugin - - -class MetaPluginV2DBTestCase(test_plugin.NeutronDbPluginV2TestCase): - - _plugin_name = ('neutron.plugins.metaplugin.' - 'meta_neutron_plugin.MetaPluginV2') - - def setUp(self, plugin=None, ext_mgr=None, - service_plugins=None): - # NOTE(salv-orlando): The plugin keyword argument is ignored, - # as this class will always invoke super with self._plugin_name. - # These keyword parameters ensure setUp methods always have the - # same signature. - test_metaplugin.setup_metaplugin_conf() - ext_mgr = ext_mgr or test_l3_plugin.L3TestExtensionManager() - self.addCleanup(test_metaplugin.unregister_meta_hooks) - super(MetaPluginV2DBTestCase, self).setUp( - plugin=self._plugin_name, ext_mgr=ext_mgr, - service_plugins=service_plugins) - - -class TestMetaBasicGet(test_plugin.TestBasicGet, - MetaPluginV2DBTestCase): - pass - - -class TestMetaV2HTTPResponse(test_plugin.TestV2HTTPResponse, - MetaPluginV2DBTestCase): - pass - - -class TestMetaPortsV2(test_plugin.TestPortsV2, - MetaPluginV2DBTestCase): - pass - - -class TestMetaNetworksV2(test_plugin.TestNetworksV2, - MetaPluginV2DBTestCase): - pass - - -class TestMetaSubnetsV2(test_plugin.TestSubnetsV2, - MetaPluginV2DBTestCase): - #TODO(nati) This test fails if we run all test, but It success just one - def test_update_subnet_route(self): - pass - - def test_update_subnet_dns_to_None(self): - pass - - def test_update_subnet_route_to_None(self): - pass - - def test_update_subnet_dns(self): - pass - - -class TestMetaL3NatDBTestCase(test_l3_plugin.L3NatDBIntTestCase, - MetaPluginV2DBTestCase): - pass diff --git a/neutron/tests/unit/metaplugin/test_metaplugin.py b/neutron/tests/unit/metaplugin/test_metaplugin.py deleted file mode 100644 index 682864b9a54..00000000000 --- a/neutron/tests/unit/metaplugin/test_metaplugin.py +++ /dev/null @@ -1,405 +0,0 @@ -# Copyright 2012, Nachi Ueno, NTT MCL, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# 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 mock -from oslo_config import cfg -import testtools - -from neutron.common import exceptions as exc -from neutron.common import topics -from neutron import context -from neutron.db import db_base_plugin_v2 -from neutron.db import models_v2 -from neutron.extensions import flavor as ext_flavor -from neutron.openstack.common import uuidutils -from neutron.plugins.metaplugin import meta_neutron_plugin -from neutron.tests.unit import testlib_api -from neutron.tests.unit import testlib_plugin - -CONF_FILE = "" -META_PATH = "neutron.plugins.metaplugin" -FAKE_PATH = "neutron.tests.unit.metaplugin" -PROXY_PATH = "%s.proxy_neutron_plugin.ProxyPluginV2" % META_PATH -PLUGIN_LIST = """ -fake1:%s.fake_plugin.Fake1,fake2:%s.fake_plugin.Fake2,proxy:%s -""".strip() % (FAKE_PATH, FAKE_PATH, PROXY_PATH) -L3_PLUGIN_LIST = """ -fake1:%s.fake_plugin.Fake1,fake2:%s.fake_plugin.Fake2 -""".strip() % (FAKE_PATH, FAKE_PATH) - - -def setup_metaplugin_conf(has_l3=True): - cfg.CONF.set_override('auth_url', 'http://localhost:35357/v2.0', - 'PROXY') - cfg.CONF.set_override('auth_region', 'RegionOne', 'PROXY') - cfg.CONF.set_override('admin_user', 'neutron', 'PROXY') - cfg.CONF.set_override('admin_password', 'password', 'PROXY') - cfg.CONF.set_override('admin_tenant_name', 'service', 'PROXY') - cfg.CONF.set_override('plugin_list', PLUGIN_LIST, 'META') - if has_l3: - cfg.CONF.set_override('l3_plugin_list', L3_PLUGIN_LIST, 'META') - else: - cfg.CONF.set_override('l3_plugin_list', "", 'META') - cfg.CONF.set_override('default_flavor', 'fake2', 'META') - cfg.CONF.set_override('default_l3_flavor', 'fake1', 'META') - cfg.CONF.set_override('base_mac', "12:34:56:78:90:ab") - #TODO(nati) remove this after subnet quota change is merged - cfg.CONF.set_override('max_dns_nameservers', 10) - - -# Hooks registered by metaplugin must not exist for other plugins UT. -# So hooks must be unregistered (overwrite to None in fact). -def unregister_meta_hooks(): - db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook( - models_v2.Network, 'metaplugin_net', None, None, None) - db_base_plugin_v2.NeutronDbPluginV2.register_model_query_hook( - models_v2.Port, 'metaplugin_port', None, None, None) - - -class MetaNeutronPluginV2Test(testlib_api.SqlTestCase, - testlib_plugin.PluginSetupHelper): - """Class conisting of MetaNeutronPluginV2 unit tests.""" - - has_l3 = True - - def setUp(self): - super(MetaNeutronPluginV2Test, self).setUp() - self.fake_tenant_id = uuidutils.generate_uuid() - self.context = context.get_admin_context() - - self.addCleanup(unregister_meta_hooks) - - setup_metaplugin_conf(self.has_l3) - - self.client_cls_p = mock.patch('neutronclient.v2_0.client.Client') - client_cls = self.client_cls_p.start() - self.client_inst = mock.Mock() - client_cls.return_value = self.client_inst - self.client_inst.create_network.return_value = \ - {'id': 'fake_id'} - self.client_inst.create_port.return_value = \ - {'id': 'fake_id'} - self.client_inst.create_subnet.return_value = \ - {'id': 'fake_id'} - self.client_inst.update_network.return_value = \ - {'id': 'fake_id'} - self.client_inst.update_port.return_value = \ - {'id': 'fake_id'} - self.client_inst.update_subnet.return_value = \ - {'id': 'fake_id'} - self.client_inst.delete_network.return_value = True - self.client_inst.delete_port.return_value = True - self.client_inst.delete_subnet.return_value = True - plugin = (meta_neutron_plugin.MetaPluginV2.__module__ + '.' - + meta_neutron_plugin.MetaPluginV2.__name__) - self.setup_coreplugin(plugin) - self.plugin = meta_neutron_plugin.MetaPluginV2(configfile=None) - - def _fake_network(self, flavor): - data = {'network': {'name': flavor, - 'admin_state_up': True, - 'shared': False, - 'router:external': [], - 'tenant_id': self.fake_tenant_id, - ext_flavor.FLAVOR_NETWORK: flavor}} - return data - - def _fake_port(self, net_id): - return {'port': {'name': net_id, - 'network_id': net_id, - 'admin_state_up': True, - 'device_id': 'bad_device_id', - 'device_owner': 'bad_device_owner', - 'admin_state_up': True, - 'host_routes': [], - 'fixed_ips': [], - 'mac_address': self.plugin._generate_mac(), - 'tenant_id': self.fake_tenant_id}} - - def _fake_subnet(self, net_id): - allocation_pools = [{'start': '10.0.0.2', - 'end': '10.0.0.254'}] - return {'subnet': {'name': net_id, - 'network_id': net_id, - 'gateway_ip': '10.0.0.1', - 'dns_nameservers': ['10.0.0.2'], - 'host_routes': [], - 'cidr': '10.0.0.0/24', - 'allocation_pools': allocation_pools, - 'enable_dhcp': True, - 'ip_version': 4}} - - def _fake_router(self, flavor): - data = {'router': {'name': flavor, 'admin_state_up': True, - 'tenant_id': self.fake_tenant_id, - ext_flavor.FLAVOR_ROUTER: flavor, - 'external_gateway_info': None}} - return data - - def test_create_delete_network(self): - network1 = self._fake_network('fake1') - ret1 = self.plugin.create_network(self.context, network1) - self.assertEqual('fake1', ret1[ext_flavor.FLAVOR_NETWORK]) - - network2 = self._fake_network('fake2') - ret2 = self.plugin.create_network(self.context, network2) - self.assertEqual('fake2', ret2[ext_flavor.FLAVOR_NETWORK]) - - network3 = self._fake_network('proxy') - ret3 = self.plugin.create_network(self.context, network3) - self.assertEqual('proxy', ret3[ext_flavor.FLAVOR_NETWORK]) - - db_ret1 = self.plugin.get_network(self.context, ret1['id']) - self.assertEqual('fake1', db_ret1['name']) - - db_ret2 = self.plugin.get_network(self.context, ret2['id']) - self.assertEqual('fake2', db_ret2['name']) - - db_ret3 = self.plugin.get_network(self.context, ret3['id']) - self.assertEqual('proxy', db_ret3['name']) - - db_ret4 = self.plugin.get_networks(self.context) - self.assertEqual(3, len(db_ret4)) - - db_ret5 = self.plugin.get_networks( - self.context, - {ext_flavor.FLAVOR_NETWORK: ['fake1']}) - self.assertEqual(1, len(db_ret5)) - self.assertEqual('fake1', db_ret5[0]['name']) - self.plugin.delete_network(self.context, ret1['id']) - self.plugin.delete_network(self.context, ret2['id']) - self.plugin.delete_network(self.context, ret3['id']) - - def test_create_delete_port(self): - network1 = self._fake_network('fake1') - network_ret1 = self.plugin.create_network(self.context, network1) - network2 = self._fake_network('fake2') - network_ret2 = self.plugin.create_network(self.context, network2) - network3 = self._fake_network('proxy') - network_ret3 = self.plugin.create_network(self.context, network3) - - port1 = self._fake_port(network_ret1['id']) - port2 = self._fake_port(network_ret2['id']) - port3 = self._fake_port(network_ret3['id']) - - port1_ret = self.plugin.create_port(self.context, port1) - port2_ret = self.plugin.create_port(self.context, port2) - port3_ret = self.plugin.create_port(self.context, port3) - ports_all = self.plugin.get_ports(self.context) - - self.assertEqual(network_ret1['id'], port1_ret['network_id']) - self.assertEqual(network_ret2['id'], port2_ret['network_id']) - self.assertEqual(network_ret3['id'], port3_ret['network_id']) - self.assertEqual(3, len(ports_all)) - - port1_dict = self.plugin._make_port_dict(port1_ret) - port2_dict = self.plugin._make_port_dict(port2_ret) - port3_dict = self.plugin._make_port_dict(port3_ret) - - self.assertEqual(port1_dict, port1_ret) - self.assertEqual(port2_dict, port2_ret) - self.assertEqual(port3_dict, port3_ret) - - port1['port']['admin_state_up'] = False - port2['port']['admin_state_up'] = False - port3['port']['admin_state_up'] = False - self.plugin.update_port(self.context, port1_ret['id'], port1) - self.plugin.update_port(self.context, port2_ret['id'], port2) - self.plugin.update_port(self.context, port3_ret['id'], port3) - port_in_db1 = self.plugin.get_port(self.context, port1_ret['id']) - port_in_db2 = self.plugin.get_port(self.context, port2_ret['id']) - port_in_db3 = self.plugin.get_port(self.context, port3_ret['id']) - self.assertEqual(False, port_in_db1['admin_state_up']) - self.assertEqual(False, port_in_db2['admin_state_up']) - self.assertEqual(False, port_in_db3['admin_state_up']) - - self.plugin.delete_port(self.context, port1_ret['id']) - self.plugin.delete_port(self.context, port2_ret['id']) - self.plugin.delete_port(self.context, port3_ret['id']) - - self.plugin.delete_network(self.context, network_ret1['id']) - self.plugin.delete_network(self.context, network_ret2['id']) - self.plugin.delete_network(self.context, network_ret3['id']) - - def test_create_delete_subnet(self): - # for this test we need to enable overlapping ips - cfg.CONF.set_default('allow_overlapping_ips', True) - network1 = self._fake_network('fake1') - network_ret1 = self.plugin.create_network(self.context, network1) - network2 = self._fake_network('fake2') - network_ret2 = self.plugin.create_network(self.context, network2) - network3 = self._fake_network('proxy') - network_ret3 = self.plugin.create_network(self.context, network3) - - subnet1 = self._fake_subnet(network_ret1['id']) - subnet2 = self._fake_subnet(network_ret2['id']) - subnet3 = self._fake_subnet(network_ret3['id']) - - subnet1_ret = self.plugin.create_subnet(self.context, subnet1) - subnet2_ret = self.plugin.create_subnet(self.context, subnet2) - subnet3_ret = self.plugin.create_subnet(self.context, subnet3) - self.assertEqual(network_ret1['id'], subnet1_ret['network_id']) - self.assertEqual(network_ret2['id'], subnet2_ret['network_id']) - self.assertEqual(network_ret3['id'], subnet3_ret['network_id']) - - subnet_in_db1 = self.plugin.get_subnet(self.context, subnet1_ret['id']) - subnet_in_db2 = self.plugin.get_subnet(self.context, subnet2_ret['id']) - subnet_in_db3 = self.plugin.get_subnet(self.context, subnet3_ret['id']) - - subnet1['subnet']['allocation_pools'].pop() - subnet2['subnet']['allocation_pools'].pop() - subnet3['subnet']['allocation_pools'].pop() - - self.plugin.update_subnet(self.context, - subnet1_ret['id'], subnet1) - self.plugin.update_subnet(self.context, - subnet2_ret['id'], subnet2) - self.plugin.update_subnet(self.context, - subnet3_ret['id'], subnet3) - subnet_in_db1 = self.plugin.get_subnet(self.context, subnet1_ret['id']) - subnet_in_db2 = self.plugin.get_subnet(self.context, subnet2_ret['id']) - subnet_in_db3 = self.plugin.get_subnet(self.context, subnet3_ret['id']) - - self.assertEqual(4, subnet_in_db1['ip_version']) - self.assertEqual(4, subnet_in_db2['ip_version']) - self.assertEqual(4, subnet_in_db3['ip_version']) - - self.plugin.delete_subnet(self.context, subnet1_ret['id']) - self.plugin.delete_subnet(self.context, subnet2_ret['id']) - self.plugin.delete_subnet(self.context, subnet3_ret['id']) - - self.plugin.delete_network(self.context, network_ret1['id']) - self.plugin.delete_network(self.context, network_ret2['id']) - self.plugin.delete_network(self.context, network_ret3['id']) - - def test_create_delete_router(self): - router1 = self._fake_router('fake1') - router_ret1 = self.plugin.create_router(self.context, router1) - router2 = self._fake_router('fake2') - router_ret2 = self.plugin.create_router(self.context, router2) - - self.assertEqual('fake1', router_ret1[ext_flavor.FLAVOR_ROUTER]) - self.assertEqual('fake2', router_ret2[ext_flavor.FLAVOR_ROUTER]) - - router_in_db1 = self.plugin.get_router(self.context, router_ret1['id']) - router_in_db2 = self.plugin.get_router(self.context, router_ret2['id']) - - self.assertEqual('fake1', router_in_db1[ext_flavor.FLAVOR_ROUTER]) - self.assertEqual('fake2', router_in_db2[ext_flavor.FLAVOR_ROUTER]) - - self.plugin.delete_router(self.context, router_ret1['id']) - self.plugin.delete_router(self.context, router_ret2['id']) - with testtools.ExpectedException(meta_neutron_plugin.FlavorNotFound): - self.plugin.get_router(self.context, router_ret1['id']) - - def test_extension_method(self): - """Test if plugin methods are accessible from self.plugin - - This test compensates for the nondeterministic ordering of - self.plugin's plugins dictionary. Fake Plugin 1 and Fake Plugin 2 - both have a function called fake_func and the order of - self.plugin.plugins will determine which fake_func is called. - """ - fake1 = self.plugin.plugins.keys().index('fake1') - fake2 = self.plugin.plugins.keys().index('fake2') - fake1_before_fake2 = fake1 < fake2 - - fake_func_return = 'fake1' if fake1_before_fake2 else 'fake2' - - self.assertEqual(fake_func_return, self.plugin.fake_func()) - self.assertEqual('fake2', self.plugin.fake_func2()) - - def test_extension_not_implemented_method(self): - try: - self.plugin.not_implemented() - except AttributeError: - return - except Exception: - self.fail("AttributeError Error is not raised") - - self.fail("No Error is not raised") - - def test_create_network_flavor_fail(self): - with mock.patch('neutron.plugins.metaplugin.meta_db_v2.' - 'add_network_flavor_binding', - side_effect=Exception): - network = self._fake_network('fake1') - self.assertRaises(meta_neutron_plugin.FaildToAddFlavorBinding, - self.plugin.create_network, - self.context, - network) - count = self.plugin.get_networks_count(self.context) - self.assertEqual(count, 0) - - def test_create_router_flavor_fail(self): - with mock.patch('neutron.plugins.metaplugin.meta_db_v2.' - 'add_router_flavor_binding', - side_effect=Exception): - router = self._fake_router('fake1') - self.assertRaises(meta_neutron_plugin.FaildToAddFlavorBinding, - self.plugin.create_router, - self.context, - router) - count = self.plugin.get_routers_count(self.context) - self.assertEqual(count, 0) - - -class MetaNeutronPluginV2TestWithoutL3(MetaNeutronPluginV2Test): - """Tests without l3_plugin_list configration.""" - - has_l3 = False - - def test_supported_extension_aliases(self): - self.assertEqual(self.plugin.supported_extension_aliases, - ['flavor', 'external-net']) - - def test_create_delete_router(self): - self.skipTest("Test case without router") - - def test_create_router_flavor_fail(self): - self.skipTest("Test case without router") - - -class MetaNeutronPluginV2TestRpcFlavor(testlib_api.SqlTestCase): - """Tests for rpc_flavor.""" - - def setUp(self): - super(MetaNeutronPluginV2TestRpcFlavor, self).setUp() - self.addCleanup(unregister_meta_hooks) - - def test_rpc_flavor(self): - setup_metaplugin_conf() - cfg.CONF.set_override('rpc_flavor', 'fake1', 'META') - self.plugin = meta_neutron_plugin.MetaPluginV2() - self.assertEqual(topics.PLUGIN, 'q-plugin') - ret = self.plugin.rpc_workers_supported() - self.assertFalse(ret) - - def test_invalid_rpc_flavor(self): - setup_metaplugin_conf() - cfg.CONF.set_override('rpc_flavor', 'fake-fake', 'META') - self.assertRaises(exc.Invalid, - meta_neutron_plugin.MetaPluginV2) - self.assertEqual(topics.PLUGIN, 'q-plugin') - - def test_rpc_flavor_multiple_rpc_workers(self): - setup_metaplugin_conf() - cfg.CONF.set_override('rpc_flavor', 'fake2', 'META') - self.plugin = meta_neutron_plugin.MetaPluginV2() - self.assertEqual(topics.PLUGIN, 'q-plugin') - ret = self.plugin.rpc_workers_supported() - self.assertTrue(ret) - ret = self.plugin.start_rpc_listeners() - self.assertEqual('OK', ret)