diff --git a/etc/neutron/plugins/ml2/ml2_conf.ini b/etc/neutron/plugins/ml2/ml2_conf.ini index f4c1d188772..6325e714eee 100644 --- a/etc/neutron/plugins/ml2/ml2_conf.ini +++ b/etc/neutron/plugins/ml2/ml2_conf.ini @@ -20,7 +20,6 @@ # Example: mechanism_drivers = cisco,logger # Example: mechanism_drivers = openvswitch,brocade # Example: mechanism_drivers = linuxbridge,brocade -# Example: mechanism_drivers = openvswitch,cisco_dfa # (ListOpt) Ordered list of extension driver entrypoints # to be loaded from the neutron.ml2.extension_drivers namespace. diff --git a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini index c2582e08358..1b69100e775 100644 --- a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini +++ b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini @@ -116,17 +116,3 @@ # encap=vlan-100 # cidr_exposed=10.10.40.2/16 # gateway_ip=10.10.40.1 - -[ml2_cisco_dfa] -# (StrOpt) IP address of Cisco DCNM (Data Center Network Manager). -# dcnm_ip = 1.1.1.1 -# -# (StrOpt) User login name for DCNM. -# dcnm_user = username -# -# (StrOpt) Login password for DCNM. -# dcnm_password = password -# -# (StrOpt) Gateway MAC address when forwarding mode in created config profile -# is proxy mode. -# gateway_mac = 00:01:02:03:04:05 diff --git a/neutron/db/migration/alembic_migrations/versions/469426cd2173_cisco_dfa_mech_driver.py b/neutron/db/migration/alembic_migrations/versions/469426cd2173_cisco_dfa_mech_driver.py deleted file mode 100644 index 7d10ee73c76..00000000000 --- a/neutron/db/migration/alembic_migrations/versions/469426cd2173_cisco_dfa_mech_driver.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2014 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. -# - -"""Cisco DFA Mechanism Driver - -Revision ID: 469426cd2173 -Revises: 32f3915891fd -Create Date: 2014-06-28 01:13:04.152945 - -""" - -# revision identifiers, used by Alembic. -revision = '469426cd2173' -down_revision = '32f3915891fd' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(active_plugins=None, options=None): - op.create_table( - 'cisco_dfa_config_profiles', - sa.Column('id', sa.String(36)), - sa.Column('name', sa.String(255)), - sa.Column('forwarding_mode', sa.String(32)), - sa.PrimaryKeyConstraint('id')) - - op.create_table( - 'cisco_dfa_config_profile_bindings', - sa.Column('network_id', sa.String(36)), - sa.Column('cfg_profile_id', sa.String(36)), - sa.ForeignKeyConstraint(['network_id'], ['networks.id'], - ondelete='CASCADE'), - sa.PrimaryKeyConstraint('network_id', 'cfg_profile_id')) - - op.create_table( - 'cisco_dfa_project_cache', - sa.Column('project_id', sa.String(36)), - sa.Column('project_name', sa.String(255)), - sa.PrimaryKeyConstraint('project_id')) - - -def downgrade(active_plugins=None, options=None): - op.drop_table('cisco_dfa_project_cache') - op.drop_table('cisco_dfa_config_profile_bindings') - op.drop_table('cisco_dfa_config_profiles') diff --git a/neutron/db/migration/alembic_migrations/versions/58fe87a01143_cisco_csr_routing.py b/neutron/db/migration/alembic_migrations/versions/58fe87a01143_cisco_csr_routing.py index 0ee5d6d9922..34ee0ee9ea5 100644 --- a/neutron/db/migration/alembic_migrations/versions/58fe87a01143_cisco_csr_routing.py +++ b/neutron/db/migration/alembic_migrations/versions/58fe87a01143_cisco_csr_routing.py @@ -16,14 +16,14 @@ """cisco_csr_routing Revision ID: 58fe87a01143 -Revises: 4eba2f05c2f4 +Revises: 32f3915891fd Create Date: 2014-08-18 17:14:12.506356 """ # revision identifiers, used by Alembic. revision = '58fe87a01143' -down_revision = '469426cd2173' +down_revision = '32f3915891fd' from alembic import op import sqlalchemy as sa diff --git a/neutron/db/migration/models/head.py b/neutron/db/migration/models/head.py index 645921d9756..d15d3df7967 100644 --- a/neutron/db/migration/models/head.py +++ b/neutron/db/migration/models/head.py @@ -60,7 +60,6 @@ from neutron.plugins.ml2.drivers.arista import db # noqa from neutron.plugins.ml2.drivers.brocade.db import ( # noqa models as ml2_brocade_models) from neutron.plugins.ml2.drivers.cisco.apic import apic_model # noqa -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_models_v2 # noqa from neutron.plugins.ml2.drivers.cisco.nexus import ( # noqa nexus_models_v2 as ml2_nexus_models_v2) from neutron.plugins.ml2.drivers import type_flat # noqa diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/__init__.py b/neutron/plugins/ml2/drivers/cisco/dfa/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/cfg_profile_db_v2.py b/neutron/plugins/ml2/drivers/cisco/dfa/cfg_profile_db_v2.py deleted file mode 100644 index 430e54cbcf5..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/cfg_profile_db_v2.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2014 Cisco Systems, 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.db import models_v2 -from neutron.plugins.ml2.drivers.cisco.dfa import constants as dfac -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_exceptions as dexc -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_models_v2 - - -def get_network_profile_binding(session, net_id): - """Retrieve network and config profile binding.""" - - try: - return (session.query(dfa_models_v2.ConfigProfileBinding). - filter_by(network_id=net_id).one()) - except (exc.NoResultFound, exc.MultipleResultsFound): - pass - - -def add_dfa_cfg_profile_binding(session, netid, cpid): - """Add new entry to the config profile binding database.""" - - try: - if cpid == dfac.DEFAULT_CFG_PROFILE_ID: - # The config profile is not provided when creating network. - # Use 'defaultNetworkL2Profile' as default config profile. - cfgp_name = 'defaultNetworkL2Profile' - cfgp_entry = (session.query(dfa_models_v2.ConfigProfile). - filter_by(name=cfgp_name).one()) - cpid = cfgp_entry.id - - binding = dfa_models_v2.ConfigProfileBinding(network_id=netid, - cfg_profile_id=cpid) - session.add(binding) - except (exc.NoResultFound, exc.MultipleResultsFound): - raise dexc.ConfigProfileNotFound(network_id=netid) - - -def get_network_entry(session, netid): - """Retrieve network information.""" - - try: - return (session.query(models_v2.Network). - filter_by(id=netid).one()) - except (exc.NoResultFound, exc.MultipleResultsFound): - raise dexc.NetworkNotFound(network_id=netid) - - -def get_config_profile_name(db_session, netid): - """Retrieve configuration profile for a network.""" - - try: - cfgpobj = dfa_models_v2.ConfigProfileBinding - cfgp = db_session.query(cfgpobj).filter_by(network_id=netid).one() - cfgid = cfgp.cfg_profile_id - except (exc.NoResultFound, exc.MultipleResultsFound): - raise dexc.ConfigProfileNotFound(network_id=netid) - try: - cfgp_entry = db_session.query( - dfa_models_v2.ConfigProfile).filter_by(id=cfgid).one() - except (exc.NoResultFound, exc.MultipleResultsFound): - raise dexc.ConfigProfileIdNotFound(profile_id=cfgid) - return cfgp_entry.name - - -def get_config_profile_fwd_mode(db_session, network_id): - """Retrieve configuration profile for a network.""" - - try: - cfgp = (db_session.query(dfa_models_v2.ConfigProfileBinding). - filter_by(network_id=network_id).one()) - cfgid = cfgp.cfg_profile_id - except (exc.NoResultFound, exc.MultipleResultsFound): - raise dexc.ConfigProfileNotFound(network_id=network_id) - - try: - cfgp_entry = db_session.query( - dfa_models_v2.ConfigProfile).filter_by(id=cfgid).one() - return cfgp_entry.forwarding_mode - except (exc.NoResultFound, exc.MultipleResultsFound): - raise dexc.ConfigProfileIdNotFound(profile_id=cfgid) - - -def delete_dfa_cfg_profile_binding(db_session, network_id): - """Delete an entry from the config profile binding database.""" - - try: - with db_session.begin(subtransactions=True): - entry = (db_session.query(dfa_models_v2.ConfigProfileBinding). - filter_by(network_id=network_id).one()) - db_session.delete(entry) - except (exc.NoResultFound, exc.MultipleResultsFound): - raise dexc.ConfigProfileNotFound(network_id=network_id) diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/cisco_dfa_rest.py b/neutron/plugins/ml2/drivers/cisco/dfa/cisco_dfa_rest.py deleted file mode 100644 index 1572be754b9..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/cisco_dfa_rest.py +++ /dev/null @@ -1,303 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 oslo.config import cfg -import requests - -from neutron.openstack.common import jsonutils -from neutron.openstack.common import log as logging -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_exceptions as dexc - -LOG = logging.getLogger(__name__) - - -class DFARESTClient(object): - """DFA client class that provides APIs to interact with DCNM.""" - - def __init__(self): - self._ip = cfg.CONF.ml2_cisco_dfa.dcnm_ip - self._user = cfg.CONF.ml2_cisco_dfa.dcnm_user - self._pwd = cfg.CONF.ml2_cisco_dfa.dcnm_password - if (not self._ip) or (not self._user) or (not self._pwd): - msg = _("[DFARESTClient] Input DCNM IP, user name or password" - "parameter is not specified") - raise ValueError(msg) - - # url timeout: 10 seconds - self._TIMEOUT_RESPONSE = 10 - - # urls - net_url = 'http://%s/' % self._ip - net_url += 'rest/auto-config/organizations/%s/partitions/%s/networks' - self._create_network_url = net_url - cfg_url = 'http://%s/rest/auto-config/profiles' % self._ip - self._cfg_profile_list_url = cfg_url - cfg_url += '/%s' - self._cfg_profile_get_url = cfg_url - self._org_url = 'http://%s/rest/auto-config/organizations' % self._ip - tmp_url = 'http://%s/rest/auto-config/organizations/' % self._ip - tmp_url += '%s/partitions' - self._create_part_url = tmp_url - self._del_org_url = self._org_url + '/%s' - self._del_part = self._org_url + '/%s/partitions/%s' - self._del_network_url = (self._org_url + - '/%s/partitions/%s/networks/segment/%s') - self._login_url = 'http://%s/rest/logon' % (self._ip) - self._logout_url = 'http://%s/rest/logout' % (self._ip) - self._exp_time = 100000 - self._resp_ok = 200 - - def _create_network(self, network_info): - """Send create network request to DCNM. - - :network_info: network parameters to be created on DCNM - """ - url = self._create_network_url % (network_info['partitionName'], - network_info['partitionName']) - payload = network_info - - LOG.info(_('url %(url)s payload %(payload)s'), - {'url': url, 'payload': payload}) - return (self._send_request('POST', url, payload, 'network')) - - def _config_profile_get(self, thisprofile): - """Get information of a config profile from DCNM. - - :thisprofile: network config profile in request - """ - url = self._cfg_profile_get_url % (thisprofile) - payload = {} - - res = self._send_request('GET', url, payload, 'config-profile') - return res.json() - - def _config_profile_list(self): - """Get list of supported config profile from DCNM.""" - url = self._cfg_profile_list_url - payload = {} - - res = self._send_request('GET', url, payload, 'config-profile') - return res.json() - - def _create_org(self, name, desc): - """Create organization on the DCNM. - - :name: Name of organization - :desc: Description of organization - """ - url = self._org_url - payload = { - "organizationName": name, - "description": name if len(desc) == 0 else desc, - "orchestrationSource": "Openstack Controller"} - - return (self._send_request('POST', url, payload, 'organization')) - - def _create_partition(self, org_name, part_name, desc): - """Send Create partition request to the DCNM. - - :org_name: name of organization - :part_name: name of partition - :desc: description of partition - """ - url = self._create_part_url % (org_name) - payload = { - "partitionName": part_name, - "description": part_name if len(desc) == 0 else desc, - "organizationName": org_name} - - return (self._send_request('POST', url, payload, 'partition')) - - def _delete_org(self, org_name): - """Send organization delete request to DCNM. - - :org_name: name of organization to be deleted - """ - url = self._del_org_url % (org_name) - self._send_request('DELETE', url, '', 'organization') - - def _delete_partition(self, org_name, partition_name): - """Send partition delete request to DCNM. - - :partition_name: name of partition to be deleted - """ - url = self._del_part % (org_name, partition_name) - self._send_request('DELETE', url, '', 'partition') - - def _delete_network(self, network_info): - """Send network delete request to DCNM. - - :partition_name: name of partition to be deleted - """ - org_name = network_info.get('organizationName', '') - part_name = network_info.get('partitionName', '') - segment_id = network_info['segmentId'] - url = self._del_network_url % (org_name, part_name, segment_id) - self._send_request('DELETE', url, '', 'network') - - def _login(self): - """Login request to DCNM.""" - url_login = self._login_url - expiration_time = self._exp_time - - payload = {'expirationTime': expiration_time} - self._req_headers = {'Accept': 'application/json', - 'Content-Type': 'application/json; charset=UTF-8'} - res = requests.post(url_login, - data=jsonutils.dumps(payload), - headers=self._req_headers, - auth=(self._user, self._pwd), - timeout=self._TIMEOUT_RESPONSE) - session_id = '' - if res and res.status_code == self._resp_ok: - session_id = res.json().get('Dcnm-Token') - self._req_headers.update({'Dcnm-Token': session_id}) - - def _logout(self): - """Logout request to DCNM.""" - url_logout = self._logout_url - requests.post(url_logout, - headers=self._req_headers, - timeout=self._TIMEOUT_RESPONSE) - - def _send_request(self, operation, url, payload, desc): - """Send request to DCNM.""" - res = None - try: - payload_json = None - if payload and payload != '': - payload_json = jsonutils.dumps(payload) - self._login() - desc_lookup = {'POST': ' creation', 'PUT': ' update', - 'DELETE': ' deletion', 'GET': ' get'} - - res = requests.request(operation, url, data=payload_json, - headers=self._req_headers, - timeout=self._TIMEOUT_RESPONSE) - desc += desc_lookup.get(operation, operation.lower()) - LOG.info(_("DCNM-send_request: %(desc)s %(url)s %(pld)s"), - {'desc': desc, 'url': url, 'pld': payload}) - - self._logout() - except (requests.HTTPError, requests.Timeout, - requests.ConnectionError) as e: - LOG.exception(_('Error during request')) - raise dexc.DFAClientRequestFailed(reason=e) - - return res - - def _check_for_supported_profile(self, thisprofile): - """Filter those profiles that are not currently supported.""" - return (thisprofile.endswith('Ipv4TfProfile') or - thisprofile.endswith('Ipv4EfProfile') or - 'defaultNetworkL2Profile' in thisprofile) - - def config_profile_list(self): - """Return config profile list from DCNM.""" - profile_list = [] - these_profiles = [] - these_profiles = self._config_profile_list() - profile_list = [q for p in these_profiles for q in - [p.get('profileName')] - if self._check_for_supported_profile(q)] - return profile_list - - def config_profile_fwding_mode_get(self, profile_name): - """Return forwarding mode of given config profile.""" - profile_params = self._config_profile_get(profile_name) - fwd_cli = 'fabric forwarding mode proxy-gateway' - if fwd_cli in profile_params['configCommands']: - return 'proxy-gateway' - else: - return 'anycast-gateway' - - def create_network(self, tenant_name, network, subnet): - """Create network on the DCNM. - - :tenant_name: name of tenant the network belongs to - :network: network parameters - :subnet: subnet parameters of the network - """ - network_info = {} - seg_id = str(network.provider__segmentation_id) - subnet_ip_mask = subnet.cidr.split('/') - gw_ip = subnet.gateway_ip - cfg_args = [ - "$segmentId=" + seg_id, - "$netMaskLength=" + subnet_ip_mask[1], - "$gatewayIpAddress=" + gw_ip, - "$networkName=" + network.name, - "$vlanId=0", - "$vrfName=" + tenant_name + ':' + tenant_name - ] - cfg_args = ';'.join(cfg_args) - - ip_range = ','.join(["%s-%s" % (p['start'], p['end']) for p in - subnet.allocation_pools]) - - dhcp_scopes = {'ipRange': ip_range, - 'subnet': subnet.cidr, - 'gateway': gw_ip} - - network_info = {"segmentId": seg_id, - "vlanId": "0", - "mobilityDomainId": "None", - "profileName": network.config_profile, - "networkName": network.name, - "configArg": cfg_args, - "organizationName": tenant_name, - "partitionName": tenant_name, - "description": network.name, - "dhcpScope": dhcp_scopes} - LOG.debug("Create %s network in DCNM." % network_info) - - self._create_network(network_info) - - def delete_network(self, tenant_name, network): - """Delete network on the DCNM. - - :tenant_name: name of tenant the network belongs to - :network: object that contains network parameters - """ - network_info = {} - seg_id = network.provider__segmentation_id - network_info = { - 'organizationName': tenant_name, - 'partitionName': tenant_name, - 'segmentId': seg_id, - } - LOG.debug("Delete %s network in DCNM." % network_info) - - self._delete_network(network_info) - - def delete_tenant(self, tenant_name): - """Delete tenant on the DCNM. - - :tenant_name: name of tenant to be deleted. - """ - self._delete_partition(tenant_name, tenant_name) - self._delete_org(tenant_name) - - def create_project(self, org_name, desc=None): - """Create project on the DCNM. - - :org_name: name of organization to be created - :desc: string that describes organization - """ - desc = desc or org_name - self._create_org(org_name, desc) - self._create_partition(org_name, org_name, desc) diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/config.py b/neutron/plugins/ml2/drivers/cisco/dfa/config.py deleted file mode 100644 index 82096ff33c7..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/config.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 oslo.config import cfg - - -ml2_cisco_dfa_opts = [ - cfg.StrOpt('dcnm_ip', default='0.0.0.0', - help=_("IP address of DCNM.")), - cfg.StrOpt('dcnm_user', default='user', - help=_("User login name for DCNM.")), - cfg.StrOpt('dcnm_password', default='password', - secret=True, - help=_("Login password for DCNM.")), - cfg.StrOpt('gateway_mac', default='00:00:DE:AD:BE:EF', - help=_("Gateway mac address when using proxy mode.")), -] - -cfg.CONF.register_opts(ml2_cisco_dfa_opts, "ml2_cisco_dfa") - - -class CiscoDFAConfig(object): - """Cisco DFA Mechanism Driver Configuration class.""" - - dfa_cfg = {} - - def __init__(self): - multi_parser = cfg.MultiConfigParser() - read_ok = multi_parser.read(cfg.CONF.config_file) - - if len(read_ok) != len(cfg.CONF.config_file): - raise cfg.Error(_("Failed to read config files %(file)s") % - {'file': cfg.CONF.config_file}) - - for parsed_file in multi_parser.parsed: - for parsed_item in parsed_file.keys(): - for key, value in parsed_file[parsed_item].items(): - if parsed_item == 'mech_driver_agent': - self.dfa_cfg[key] = value diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/constants.py b/neutron/plugins/ml2/drivers/cisco/dfa/constants.py deleted file mode 100644 index c051b61c22a..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/constants.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 uuid - - -CISCO_DFA_MECH_DRVR_NAME = 'cisco_dfa' -DEFAULT_CFG_PROFILE_ID = str(uuid.UUID(int=0)) -CONFIG_PROFILE_ID = 'dfa:cfg_profile_id' diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_exceptions.py b/neutron/plugins/ml2/drivers/cisco/dfa/dfa_exceptions.py deleted file mode 100644 index bef4e16d104..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_exceptions.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2014 Cisco Systems, 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. -# - -"""Exceptions used by DFA ML2 mechanism drivers.""" - -from neutron.common import exceptions - - -class NetworkNotFound(exceptions.NotFound): - """Network cannot be found.""" - - message = _("Network %(network_id)s could not be found.") - - -class ConfigProfileNotFound(exceptions.NotFound): - """Config Profile cannot be found.""" - - message = _("Config profile for network %(network_id)s" - " could not be found.") - - -class ConfigProfileFwdModeNotFound(exceptions.NotFound): - """Config Profile forwarding mode cannot be found.""" - - message = _("Forwarding Mode for network %(network_id)s" - " could not be found.") - - -class ConfigProfileIdNotFound(exceptions.NotFound): - """Config Profile ID cannot be found.""" - - message = _("Config Profile %(profile_id)s could not be found.") - - -class ConfigProfileNameNotFound(exceptions.NotFound): - """Config Profile name cannot be found.""" - - message = _("Config Profile %(name)s could not be found.") - - -class ProjectIdNotFound(exceptions.NotFound): - """Project ID cannot be found.""" - - message = _("Project ID %(project_id)s could not be found.") - - -class DFAClientRequestFailed(exceptions.ServiceUnavailable): - """Request to DCNM failed.""" - - message = _("Request to DCNM failed: %(reason)s.") diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_instance_api.py b/neutron/plugins/ml2/drivers/cisco/dfa/dfa_instance_api.py deleted file mode 100644 index a8f320baf2b..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_instance_api.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright 2014 Cisco Systems, 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. -# - -""" -This file provides a wrapper to novaclient API, for getting the instacne's -information such as display_name. -""" - -from keystoneclient.v2_0 import client as keyc - -from neutron.openstack.common import log as logging -from novaclient import exceptions as nexc -from novaclient.v1_1 import client as nova_client - - -LOG = logging.getLogger(__name__) - - -class DFAInstanceAPI(object): - """This class provides API to get information for a given instance.""" - - def __init__(self, cfg): - self._tenant_name = cfg.CONF.keystone_authtoken.admin_tenant_name - self._user_name = cfg.CONF.keystone_authtoken.admin_user - self._admin_password = cfg.CONF.keystone_authtoken.admin_password - self._TIMEOUT_RESPONSE = 10 - self._token = None - self._project_id = None - self._auth_url = None - self._token_id = None - self._token = None - self._novaclnt = None - self._url = cfg.CONF.nova_admin_auth_url - self._inst_info_cache = {} - - def _create_token(self): - """Create new token for using novaclient API.""" - ks = keyc.Client(username=self._user_name, - password=self._admin_password, - tenant_name=self._tenant_name, - auth_url=self._url) - result = ks.authenticate() - if result: - access = ks.auth_ref - token = access.get('token') - self._token_id = token['id'] - self._project_id = token['tenant'].get('id') - service_catalog = access.get('serviceCatalog') - for sc in service_catalog: - if sc['type'] == "compute" and sc['name'] == 'nova': - endpoints = sc['endpoints'] - for endp in endpoints: - self._auth_url = endp['adminURL'] - LOG.info(_('_create_token: token = %s'), token) - - # Create nova client. - self._novaclnt = self._create_nova_client() - - return token - - else: - # Failed request. - LOG.error(_('Failed to send token create request.')) - - def _create_nova_client(self): - """Creates nova client object.""" - try: - clnt = nova_client.Client(self._user_name, - self._token_id, - self._project_id, - self._auth_url, - insecure=False, - cacert=None) - clnt.client.auth_token = self._token_id - clnt.client.management_url = self._auth_url - return clnt - except nexc.Unauthorized: - thismsg = (_('Failed to get novaclient:Unauthorised ' - '%(proj)s %(user)s') % {'proj': self.project_id, - 'user': self._user_name}) - raise nexc.ClientException(thismsg) - - except nexc.AuthorizationFailure as err: - raise nexc.ClientException(_("Failed to get novaclient %s") % err) - - def _get_instances_for_project(self, project_id): - """Return all instances for a given project. - - :project_id: UUID of project (tenant) - """ - search_opts = {'marker': None, - 'all_tenants': True, - 'project_id': project_id} - self._create_token() - try: - servers = self._novaclnt.servers.list(True, search_opts) - LOG.debug('_get_instances_for_project: servers=%s' % servers) - return servers - except nexc.Unauthorized: - emsg = (_('Failed to get novaclient:Unauthorised ' - 'project_id=%(proj)s user=%(user)s'), - {'proj': self.project_id, 'name': self._user_name}) - LOG.exception(emsg) - raise nexc.ClientException(emsg) - except nexc.AuthorizationFailure as err: - emsg = _("Failed to get novaclient %s") - LOG.exception(emsg % err) - raise nexc.ClientException(emsg % err) - - def get_instance_for_uuid(self, uuid, project_id): - """Return instance name for given uuid of an instance and project. - - :uuid: Instance's UUID - :project_id: UUID of project (tenant) - """ - instance_name = None - instance_name = self._inst_info_cache.get((uuid, project_id)) - if instance_name: - return instance_name - instances = self._get_instances_for_project(project_id) - for inst in instances: - if inst.id.replace('-', '') == uuid: - LOG.debug('get_instance_for_uuid: name=%s' % inst.name) - instance_name = inst.name - self._inst_info_cache[(uuid, project_id)] = instance_name - return instance_name - return instance_name diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_mech_driver_rpc.py b/neutron/plugins/ml2/drivers/cisco/dfa/dfa_mech_driver_rpc.py deleted file mode 100644 index c5a99210e77..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_mech_driver_rpc.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 neutron.common import rpc as n_rpc -from neutron.common import topics - - -class RpcCallbacks(n_rpc.RpcCallback): - - RPC_API_VERSION = '1.1' - - def __init__(self, notifier): - self._nofifier = notifier - super(RpcCallbacks, self).__init__() - - -class MechDriversAgentNotifierApi(n_rpc.RpcProxy): - """Agent side of the cisco DFA mechanism driver rpc API. - - API version history: - 1.0 - Initial version. - """ - - BASE_RPC_API_VERSION = '1.0' - - def __init__(self, topic, agt_topic_tbl): - super(MechDriversAgentNotifierApi, self).__init__( - topic=topic, default_version=self.BASE_RPC_API_VERSION) - self.topic_dfa_update = topics.get_topic_name(topic, - agt_topic_tbl, - topics.UPDATE) - - def send_vm_info(self, context, vm_info): - self.fanout_cast(context, - self.make_msg('send_vm_info', vm_info=vm_info), - topic=self.topic_dfa_update) diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_models_v2.py b/neutron/plugins/ml2/drivers/cisco/dfa/dfa_models_v2.py deleted file mode 100644 index adecd8f138d..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/dfa_models_v2.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 neutron.db import model_base -import sqlalchemy as sa - - -class ConfigProfile(model_base.BASEV2): - """Cisco DFA network configuration profile. - - 'id' - UUID and is localy generated, - 'name' - profile name coming form DCNM. - """ - __tablename__ = 'cisco_dfa_config_profiles' - - id = sa.Column(sa.String(36), primary_key=True) - name = sa.Column(sa.String(255)) - forwarding_mode = sa.Column(sa.String(32)) - - -class ConfigProfileBinding(model_base.BASEV2): - """Represents a binding of Network to Config Profile. - - netwrok_id - Network UUID, - cfg_profile_id - UUID of config profile. - """ - __tablename__ = 'cisco_dfa_config_profile_bindings' - - network_id = sa.Column(sa.String(36), - sa.ForeignKey('networks.id', ondelete="CASCADE"), - primary_key=True) - cfg_profile_id = sa.Column(sa.String(36), primary_key=True) - - -class ProjectNameCache(model_base.BASEV2): - """Cache project name and project ID for Cisco DFA. - - project_id - project UUID, - project_name - project name. - """ - __tablename__ = 'cisco_dfa_project_cache' - - project_id = sa.Column(sa.String(36), - primary_key=True) - project_name = sa.Column(sa.String(255)) diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/mech_cisco_dfa.py b/neutron/plugins/ml2/drivers/cisco/dfa/mech_cisco_dfa.py deleted file mode 100644 index 909ad7e7446..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/mech_cisco_dfa.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) 2014 Cisco Systems -# 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. -# - - -""" -ML2 Mechanism Driver for Cisco DFA platforms. -""" - -import eventlet -from oslo.config import cfg - -from neutron.common import exceptions as n_exc -from neutron.common import rpc as n_rpc -from neutron.common import topics -from neutron.extensions import portbindings -from neutron.openstack.common import log as logging -from neutron.plugins.ml2.common import exceptions as ml2_exc -from neutron.plugins.ml2 import driver_api as api -from neutron.plugins.ml2.drivers.cisco.dfa import cfg_profile_db_v2 -from neutron.plugins.ml2.drivers.cisco.dfa import cisco_dfa_rest -from neutron.plugins.ml2.drivers.cisco.dfa import config -from neutron.plugins.ml2.drivers.cisco.dfa import constants as dfa_const -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_exceptions as dexc -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_instance_api -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_mech_driver_rpc as drpc -from neutron.plugins.ml2.drivers.cisco.dfa import project_events -from neutron.plugins.ml2.drivers.cisco.dfa import projects_cache_db_v2 - - -LOG = logging.getLogger(__name__) - - -class SubnetObj(object): - """Represents a subnet object. - - The information in the object will be used when creating a subnet on - the DCNM. - """ - def __init__(self, subnet): - self.allocation_pools = subnet['allocation_pools'] - self.host_routes = subnet['host_routes'] - self.cidr = subnet['cidr'] - self.id = subnet['id'] - self.name = subnet['name'] - self.enable_dhcp = subnet['enable_dhcp'] - self.network_id = subnet['network_id'] - self.tenant_id = subnet['tenant_id'] - self.dns_nameservers = subnet['dns_nameservers'] - self.gateway_ip = subnet['gateway_ip'] - self.ip_version = subnet['ip_version'] - self.shared = subnet['shared'] - - -class NetworkObj(object): - """Represents a network object. - - The information in this object will be used when creating a network on - the DCNM. - """ - def __init__(self, net, segid, cfgp=None): - self.provider__segmentation_id = segid - self.tenant_id = net['tenant_id'] - self.name = net['name'] - self.config_profile = cfgp - self.id = net['id'] - - -class CiscoDfaMechanismDriver(api.MechanismDriver): - """Cisco DFA ML2 Mechanism Driver.""" - - def initialize(self): - # Initialize the config - self._dfa_cfg = config.CiscoDFAConfig().dfa_cfg - - # Initialize DCNM client. - self._dcnm_client = cisco_dfa_rest.DFARESTClient() - - # Initialize project creation/deletion events object. - # This will be used to get notification from keystone when - # a tenant (i.e. project) is created or deleted. - self._keys = project_events.EventsHandler('keystone', - self._dcnm_client) - - # Spawn a task, to process notification queue for keystone events. - eventlet.spawn(self._process_keystone_events) - - # Initialize nova client wrapper. It will be used to get more - # information for an instance. - self._inst_api = dfa_instance_api.DFAInstanceAPI(cfg) - - # Initialize mechanism driver RPC. - self._setup_mechdrv_rpc() - - # Initialize project info object. - self.projects_cache_db_v2 = projects_cache_db_v2.ProjectsInfoCache() - - self._ctask_sleep_interval = 60 - - def _get_agent_topic(self): - """Read the mech_driver_agent section from the config file.""" - mech_drvr_rpc = self._dfa_cfg.get('mech_driver_rpc') - if mech_drvr_rpc is None: - return - self._agent_topic = '' - self._mech_drv_topic = '' - for val in mech_drvr_rpc: - if len(val) > 0: - if val.split(':')[0] != dfa_const.CISCO_DFA_MECH_DRVR_NAME: - continue - try: - self._mech_drv_topic = val.split(':')[1] - self._agent_topic = val.split(':')[2] - except IndexError: - emsg = _('No topics is defined for %s mechanism driver') - LOG.error(emsg % dfa_const.CISCO_DFA_MECH_DRVR_NAME) - return - - def _setup_mechdrv_rpc(self): - """Setup RPC for this mechanism driver.""" - self._get_agent_topic() - if not self._agent_topic or not self._mech_drv_topic: - LOG.debug('Mechanism Driver notifer is not initialized') - return - self.dfa_notifier = drpc.MechDriversAgentNotifierApi(topics.AGENT, - self._agent_topic) - self.endpoints = [drpc.RpcCallbacks(self.dfa_notifier)] - self.topic = self._mech_drv_topic - self.conn = n_rpc.create_connection(new=True) - self.conn.create_consumer(self.topic, self.endpoints, fanout=False) - self.conn.consume_in_threads() - - def _process_keystone_events(self): - """Task to process notification from keystone. - - The handler processes events such as creation and deletion of projects - sent by keystone. - """ - self._keys.event_handler() - - def create_network_postcommit(self, context): - # Check if the tenant is valid. - projid = context.current.get('tenant_id') - if not self._keys.is_valid_project(projid): - return - - # Check if network id exists in the config profile DB. If not, - # exception should be raised. - net_id = context.current.get('id') - res = cfg_profile_db_v2.get_network_profile_binding( - context._plugin_context.session, net_id) - if not res: - cfgp_id = context.current.get(dfa_const.CONFIG_PROFILE_ID) - msg = (_("Failed to create network. Config Profile id %s" - " does not exist.") % cfgp_id) - raise n_exc.BadRequest(resource='network', msg=msg) - - # Get the project name. If project name does not exist, an exception - # will be raised. - self.projects_cache_db_v2.get_project_name(projid) - - def delete_network_postcommit(self, context): - projid = context.current.get('tenant_id') - if not self._keys.is_valid_project(projid): - return - - segid = context.current.get('provider:segmentation_id') - tenant_name = context._plugin_context.tenant_name - net = NetworkObj(context.current, segid) - try: - self._dcnm_client.delete_network(tenant_name, net) - except dexc.DFAClientRequestFailed as ex: - emsg = _('Failed to create network %(net)s. Error:%(err)s.') - LOG.error(emsg % {'net': net.name, 'err': ex}) - raise ml2_exc.MechanismDriverError - - def create_subnet_postcommit(self, context): - projid = context.current.get('tenant_id') - if not self._keys.is_valid_project(projid): - return - - subnet = context.current - if subnet['name'] == 'private-subnet': - emsg = _("%s is default subnet and no need to create it in DCNM.") - LOG.info(emsg % subnet['name']) - return - - session = context._plugin_context.session - netid = context.current['network_id'] - network_entry = cfg_profile_db_v2.get_network_entry(session, netid) - tenant_name = context._plugin_context.tenant_name - segid = self.projects_cache_db_v2.get_network_segid(netid) - cfgp_name = cfg_profile_db_v2.get_config_profile_name(session, netid) - snet = SubnetObj(context.current) - net = NetworkObj(network_entry, int(segid), cfgp_name) - try: - self._dcnm_client.create_network(tenant_name, net, snet) - except dexc.DFAClientRequestFailed as ex: - emsg = _('Failed to create network %(net)s. Error:%(err)s.') - LOG.error(emsg % {'net': net.name, 'err': ex}) - raise ml2_exc.MechanismDriverError - - def update_port_postcommit(self, context): - projid = context.current.get('tenant_id') - if not self._keys.is_valid_project(projid): - return - - session = context._plugin_context.session - self.device_id = context.current.get('device_id').replace('-', '') - tenant_id = context.current.get('tenant_id') - netid = context.current.get('network_id') - self.inst_name = self._inst_api.get_instance_for_uuid(self.device_id, - tenant_id) - self.fwd_mode = cfg_profile_db_v2.get_config_profile_fwd_mode(session, - netid) - self.segid = self.projects_cache_db_v2.get_network_segid(netid) - self.mac = context.current.get('mac_address') - self.ip = (context.current.get('fixed_ips')[0]['ip_address'] - if context.current.get('fixed_ips') else None) - - vm_info = { - 'status': 'up', - 'ip': self.ip, - 'mac': self.mac, - 'segid': self.segid, - 'inst_name': self.inst_name, - 'inst_uuid': self.device_id, - 'host': context.current.get(portbindings.HOST_ID), - 'port_id': context.current.get('id'), - 'network_id': context.current.get('network_id'), - 'oui_type': 'cisco', - } - if self.inst_name: - self.dfa_notifier.send_vm_info(context._plugin_context, vm_info) - LOG.debug("update_port_postcommit : %s" % vm_info) - - def delete_port_postcommit(self, context): - session = context._plugin_context.session - self.device_id = context.current.get('device_id').replace('-', '') - tenant_id = context.current.get('tenant_id') - netid = context.current.get('network_id') - self.inst_name = self._inst_api.get_instance_for_uuid(self.device_id, - tenant_id) - self.fwd_mode = cfg_profile_db_v2.get_config_profile_fwd_mode(session, - netid) - self.segid = self.projects_cache_db_v2.get_network_segid(netid) - self.mac = context.current.get('mac_address') - self.ip = (context.current.get('fixed_ips')[0]['ip_address'] - if context.current.get('fixed_ips') else None) - - vm_info = { - 'status': 'down', - 'ip': self.ip, - 'mac': self.mac, - 'segid': self.segid, - 'inst_name': self.inst_name, - 'inst_uuid': self.device_id, - 'host': context.current.get(portbindings.HOST_ID), - 'port_id': context.current.get('id'), - 'network_id': context.current.get('network_id'), - 'oui_type': 'cisco', - } - if self.inst_name: - self.dfa_notifier.send_vm_info(context._plugin_context, vm_info) - LOG.debug("delete_port_postcommit : %s" % vm_info) diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/project_events.py b/neutron/plugins/ml2/drivers/cisco/dfa/project_events.py deleted file mode 100644 index 7ea1c10dbf3..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/project_events.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 keystoneclient.v3 import client -from oslo.config import cfg -from oslo import messaging - -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_exceptions as dexc -from neutron.plugins.ml2.drivers.cisco.dfa import projects_cache_db_v2 - - -LOG = logging.getLogger(__name__) - - -notif_params = { - 'keystone': { - 'admin_token': 'ADMIN', - 'admin_endpoint': 'http://localhost:%(admin_port)s/', - 'admin_port': '35357', - 'default_notification_level': 'INFO', - 'notification_topics': 'notifications', - 'control_exchange': 'openstack', - } -} - -proj_exceptions_list = [ - 'admin', 'service', 'invisible_to_admin', 'demo', 'alt_demo'] - - -class NotificationEndpoint(object): - def __init__(self, evnt_hndlr): - self._event_hndlr = evnt_hndlr - - def info(self, ctxt, publisher_id, event_type, payload, metadata): - self._event_hndlr.callback(event_type, payload) - - -class EventsHandler(projects_cache_db_v2.ProjectsInfoCache): - """This class defines methods to listen and process the project events.""" - - def __init__(self, ser_name, dcnm_client): - self._keystone = None - self._service = ser_name - self._notif_params = {} - self._set_notif_params() - self._dcnm_client = dcnm_client - self.events_handler = { - 'identity.project.created': self.project_create_event, - 'identity.project.deleted': self.project_delete_event, - 'identity.user.created': self.no_op_event, - 'identity.user.deleted': self.no_op_event, - } - - def no_op_event(self, keyc, project_id, dcnmc): - pass - - def project_create_event(self, keyc, project_id, dcnmc): - """Create a project on the DCNM. - - :param keyc: keystoneclient object - :param project_id: UUID of the project - :param dcnmc: DCNM client object - """ - proj = keyc.projects.get(project_id) - proj_name = proj.name - desc = proj.description - LOG.debug("project_create_event: %(proj)s %(proj_name)s %(desc)s." % - {'proj': proj, 'proj_name': proj_name, 'desc': desc}) - if proj_name not in proj_exceptions_list: - try: - dcnmc.create_project(proj_name, desc) - except dexc.DFAClientConnectionFailed as ex: - with excutils.save_and_reraise_exception(): - LOG.exception(_('Failed to create %(proj)s. ' - 'Error:%(err)s.'), - {'proj': proj_name, 'err': ex}) - proj_info = {'project_id': project_id, - 'project_name': proj_name} - self.create_projects_cache_db(proj_info) - - def project_delete_event(self, keyc, project_id, dcnmc): - """Delete a project on the DCNM. - - :param keyc: keystoneclient object - :param project_id: UUID of the project - :param dcnmc: DCNM client object - """ - try: - proj_info = self.delete_projects_cache_db(project_id) - LOG.debug("project_delete_event: proj_info: %s." % proj_info) - dcnmc.delete_tenant(proj_info.project_name) - except dexc.ProjectIdNotFound: - with excutils.save_and_reraise_exception(): - LOG.exception(_("Failed to delete %(id)s"), {'id': project_id}) - except dexc.DFAClientConnectionFailed: - with excutils.save_and_reraise_exception(): - LOG.exception(_("Failed to delete %(proj)s in DCNM."), - {'proj': proj_info.project_name}) - - def _set_notif_params(self): - """Read notification parameters from the config file.""" - self._notif_params.update(notif_params[self._service]) - temp_db = {} - cfgfile = cfg.find_config_files(self._service) - multi_parser = cfg.MultiConfigParser() - cfgr = multi_parser.read(cfgfile) - if len(cfgr) == 0: - LOG.error(_("Failed to read %s."), cfgfile) - return - for parsed_file in multi_parser.parsed: - for parsed_item in parsed_file.keys(): - for key, value in parsed_file[parsed_item].items(): - if key in self._notif_params: - val = notif_params[self._service].get(key) - if val != value[0]: - temp_db[key] = value[0] - - self._notif_params.update(temp_db) - self._token = self.get_notif_params().get('admin_token') - _endpoint = self.get_notif_params().get('admin_endpoint') - self._endpoint_url = _endpoint % self.get_notif_params() + 'v3/' - self._keystone = client.Client(token=self._token, - endpoint=self._endpoint_url) - - def callback(self, event_type, payload): - """Callback method for processing events in notification queue. - - :param event_type: event type in the notification queue such as - identity.project.created, identity.project.deleted. - :param payload: Contains information of an event - """ - try: - event = event_type - if event in self.events_handler: - project_id = payload['resource_info'] - self.events_handler[event](self._keystone, project_id, - self._dcnm_client) - except KeyError: - LOG.error(_('event_type %s does not have payload/resource_info ' - 'key'), event) - - def event_handler(self): - """Prepare connection and channels for listenning to the events.""" - topicname = self.get_notif_params().get('notification_topics') - transport = messaging.get_transport(cfg.CONF) - targets = [messaging.Target(topic=topicname)] - endpoints = [NotificationEndpoint(self)] - server = messaging.get_notification_listener(transport, targets, - endpoints) - server.start() - server.wait() - - def get_notif_params(self): - """Return notification parameters.""" - return self._notif_params - - def is_valid_project(self, project_id): - """Check the validity of project. - - :param project_id: UUID of project - :returns: True if project is valid. - """ - proj = self._keystone.projects.get(project_id) - proj_name = proj.name - if proj_name in proj_exceptions_list: - LOG.debug("Project %s is not created by user." % proj_name) - return False - return True diff --git a/neutron/plugins/ml2/drivers/cisco/dfa/projects_cache_db_v2.py b/neutron/plugins/ml2/drivers/cisco/dfa/projects_cache_db_v2.py deleted file mode 100644 index 0ab597f4fe3..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/dfa/projects_cache_db_v2.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 - -import neutron.db.api as db -from neutron.plugins.ml2 import db as ml2db -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_exceptions as dexc -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_models_v2 - - -class ProjectsInfoCache(object): - """Project DB API.""" - - def _get_project_entry(self, db_session, pid): - """Get a project entry from the table. - - :param db_session: database session object - :param pid: project ID - """ - try: - return db_session.query( - dfa_models_v2.ProjectNameCache).filter_by(project_id=pid).one() - except exc.NoResultFound: - raise dexc.ProjectIdNotFound(project_id=pid) - - def create_projects_cache_db(self, proj_info): - """Create an entry in the database. - - :param proj_info: dictionary that contains information of the project - """ - db_session = db.get_session() - with db_session.begin(subtransactions=True): - projid = proj_info["project_id"] - projname = proj_info["project_name"] - thisproj = dfa_models_v2.ProjectNameCache(project_id=projid, - project_name=projname) - db_session.add(thisproj) - return thisproj - - def delete_projects_cache_db(self, proj_id): - """Delete a project from the table. - - :param proj_id: UUID of the project - """ - db_session = db.get_session() - thisproj = None - with db_session.begin(subtransactions=True): - thisproj = self._get_project_entry(db_session, proj_id) - db_session.delete(thisproj) - return thisproj - - def get_project_name(self, proj_id): - """Returns project's name. - - :param proj_id: UUID of the project - """ - db_session = db.get_session() - with db_session.begin(subtransactions=True): - thisproj = self._get_project_entry(db_session, proj_id) - return thisproj.project_name - - def update_projects_cache_db(self, pid, proj_info): - """Update projects DB. - - :param pid: project ID - :param proj_info: dictionary that contains information of the project - """ - db_session = db.get_session() - with db_session.begin(subtransactions=True): - thisproj = self._get_project_entry(db_session, pid) - thisproj.update(proj_info) - - def get_network_segid(self, sid): - """Get network segmentation id. - - :param sid: requested segment id - """ - db_session = db.get_session() - seg_entry = ml2db.get_network_segments(db_session, sid) - return seg_entry[0]['segmentation_id'] diff --git a/neutron/tests/unit/ml2/drivers/cisco/dfa/__init__.py b/neutron/tests/unit/ml2/drivers/cisco/dfa/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/neutron/tests/unit/ml2/drivers/cisco/dfa/test_cisco_dfa_rest.py b/neutron/tests/unit/ml2/drivers/cisco/dfa/test_cisco_dfa_rest.py deleted file mode 100644 index 7e183cdd6b3..00000000000 --- a/neutron/tests/unit/ml2/drivers/cisco/dfa/test_cisco_dfa_rest.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 - -from neutron.plugins.ml2.drivers.cisco.dfa import cisco_dfa_rest as dc -from neutron.plugins.ml2.drivers.cisco.dfa import config # noqa -from neutron.tests import base - - -"""This file includes test cases for cisco_dfa_rest.py.""" - -FAKE_DCNM_IP = '1.1.1.1' -FAKE_DCNM_USERNAME = 'dcnmuser' -FAKE_DCNM_PASSWORD = 'dcnmpass' -org_url = 'http://%s/rest/auto-config/organizations' -part_url = 'http://%s/rest/auto-config/organizations/%s/partitions' -net_url = 'http://%s/rest/auto-config/organizations/%s/partitions/%s/networks' -del_net_url = ('http://%s/rest/auto-config/organizations/%s/partitions/%s/' - 'networks/segment/%s') - - -class TestNetwork(object): - provider__segmentation_id = 123456 - name = 'cisco_test_network' - config_profile = 'defaultL2ConfigProfile' - - -class TestCiscoDFAClient(base.BaseTestCase): - """Test cases for DFARESTClient.""" - - def setUp(self): - # Declare the test resource. - super(TestCiscoDFAClient, self).setUp() - - dcnm_cfg = {'dcnm_ip': FAKE_DCNM_IP, - 'dcnm_user': FAKE_DCNM_USERNAME, - 'dcnm_password': FAKE_DCNM_PASSWORD} - for k, v in dcnm_cfg.items(): - cfg.CONF.set_override(k, v, 'ml2_cisco_dfa') - - self.dcnm_client = dc.DFARESTClient() - mock.patch.object(self.dcnm_client, '_send_request').start() - self.testnetwork = TestNetwork() - - def test_create_org(self): - """Test create organization.""" - - org_name = 'Test_Project' - url = org_url % (cfg.CONF.ml2_cisco_dfa.dcnm_ip) - payload = {'organizationName': org_name, - 'description': org_name, - 'orchestrationSource': 'Openstack Controller'} - self.dcnm_client._create_org(org_name, org_name) - self.dcnm_client._send_request.assert_called_with('POST', url, - payload, - 'organization') - - def test_create_partition(self): - """Test create partition.""" - - org_name = 'Cisco' - part_name = 'Lab' - url = part_url % (cfg.CONF.ml2_cisco_dfa.dcnm_ip, org_name) - payload = {'partitionName': part_name, - 'description': org_name, - 'organizationName': org_name} - self.dcnm_client._create_partition(org_name, part_name, org_name) - self.dcnm_client._send_request.assert_called_with('POST', url, - payload, - 'partition') - - def test_create_project(self): - """Test create project.""" - - org_name = 'Cisco' - self.dcnm_client.create_project(org_name) - call_cnt = self.dcnm_client._send_request.call_count - self.assertEqual(2, call_cnt) - - def test_create_network(self): - """Test create network.""" - - network_info = {} - cfg_args = [] - seg_id = str(self.testnetwork.provider__segmentation_id) - config_profile = self.testnetwork.config_profile - network_name = self.testnetwork.name - tenant_name = 'Cisco' - url = net_url % (cfg.CONF.ml2_cisco_dfa.dcnm_ip, tenant_name, - tenant_name) - - cfg_args.append("$segmentId=" + seg_id) - cfg_args.append("$netMaskLength=16") - cfg_args.append("$gatewayIpAddress=30.31.32.1") - cfg_args.append("$networkName=" + network_name) - cfg_args.append("$vlanId=0") - cfg_args.append("$vrfName=%s:%s" % (tenant_name, tenant_name)) - cfg_args = ';'.join(cfg_args) - - dhcp_scopes = {'ipRange': '10.11.12.14-10.11.12.254', - 'subnet': '10.11.12.13', - 'gateway': '10.11.12.1'} - - network_info = {"segmentId": seg_id, - "vlanId": "0", - "mobilityDomainId": "None", - "profileName": config_profile, - "networkName": network_name, - "configArg": cfg_args, - "organizationName": tenant_name, - "partitionName": tenant_name, - "description": network_name, - "dhcpScope": dhcp_scopes} - - self.dcnm_client._create_network(network_info) - self.dcnm_client._send_request.assert_called_with('POST', url, - network_info, - 'network') - - def test_delete_network(self): - """Test delete network.""" - - seg_id = self.testnetwork.provider__segmentation_id - tenant_name = 'cisco' - url = del_net_url % (cfg.CONF.ml2_cisco_dfa.dcnm_ip, - tenant_name, tenant_name, seg_id) - self.dcnm_client.delete_network(tenant_name, self.testnetwork) - self.dcnm_client._send_request.assert_called_with('DELETE', url, - '', 'network') - - def test_delete_tenant(self): - """Test delete tenant.""" - - tenant_name = 'cisco' - self.dcnm_client.delete_tenant(tenant_name) - call_cnt = self.dcnm_client._send_request.call_count - self.assertEqual(2, call_cnt) diff --git a/neutron/tests/unit/ml2/drivers/cisco/dfa/test_mech_cisco_dfa.py b/neutron/tests/unit/ml2/drivers/cisco/dfa/test_mech_cisco_dfa.py deleted file mode 100644 index f23bc0e6f1f..00000000000 --- a/neutron/tests/unit/ml2/drivers/cisco/dfa/test_mech_cisco_dfa.py +++ /dev/null @@ -1,272 +0,0 @@ -# Copyright 2014 Cisco Systems, 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 n_exc -from neutron.plugins.ml2.drivers.cisco.dfa import cisco_dfa_rest -from neutron.plugins.ml2.drivers.cisco.dfa import config -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_exceptions as dexc -from neutron.plugins.ml2.drivers.cisco.dfa import dfa_instance_api -from neutron.plugins.ml2.drivers.cisco.dfa import mech_cisco_dfa -from neutron.plugins.ml2.drivers.cisco.dfa import project_events -from neutron.plugins.ml2.drivers.cisco.dfa import projects_cache_db_v2 -from neutron.tests import base - - -FAKE_NETWORK_NAME = 'test_dfa_network' -FAKE_NETWORK_ID = '949fdd05-a26a-4819-a829-9fc2285de6ff' -FAKE_CFG_PROF_ID = '8c30f360ffe948109c28ab56f69a82e1' -FAKE_SEG_ID = 12345 -FAKE_PROJECT_NAME = 'test_dfa_project' -FAKE_PROJECT_ID = 'aee5da7e699444889c662cf7ec1c8de7' -FAKE_CFG_PROFILE_NAME = 'defaultNetworkL2Profile' -FAKE_INSTANCE_NAME = 'test_dfa_instance' -FAKE_SUBNET_ID = '1a3c5ee1-cb92-4fd8-bff1-8312ac295d64' -FAKE_PORT_ID = 'ea0d92cf-d0cb-4ed2-bbcf-ed7c6aaea4cb' -FAKE_DEVICE_ID = '20305657-78b7-48f4-a7cd-1edf3edbfcad' -FAKE_SECURITY_GRP_ID = '4b5b387d-cf21-4594-b926-f5a5c602295f' -FAKE_MAC_ADDR = 'fa:16:3e:70:15:c4' -FAKE_IP_ADDR = '23.24.25.4' -FAKE_GW_ADDR = '23.24.25.1' -FAKE_DHCP_IP_RANGE_START = '23.24.25.2' -FAKE_DHCP_IP_RANGE_END = '23.24.25.254' -FAKE_HOST_ID = 'test_dfa_host' -FAKE_FWD_MODE = 'proxy-gateway' -FAKE_DCNM_USER = 'cisco' -FAKE_DCNM_PASS = 'password' -FAKE_DCNM_IP = '1.1.2.2' - - -class FakeNetworkContext(object): - """Network context for testing purposes only.""" - - def __init__(self, network): - self._network = network - self._session = None - - @property - def current(self): - return self._network - - @property - def original(self): - return self._network - - -class FakePortContext(object): - """Port context for testing purposes only.""" - - def __init__(self, plugin_context, port): - self._port = port - self._plugin_context = plugin_context - self._session = None - - @property - def current(self): - return self._port - - -class FakeSubnetContext(object): - """Subnet context for testing purposes only.""" - - def __init__(self, subnet): - self._subnet = subnet - - @property - def current(self): - return self._subnet - - -class TestCiscoDFAMechDriver(base.BaseTestCase): - """Test cases for cisco DFA mechanism driver.""" - - def setUp(self): - super(TestCiscoDFAMechDriver, self).setUp() - - dcnmpatcher = mock.patch(cisco_dfa_rest.__name__ + '.DFARESTClient') - self.mdcnm = dcnmpatcher.start() - - # Define retrun values for keystone project. - keys_patcher = mock.patch(project_events.__name__ + '.EventsHandler') - self.mkeys = keys_patcher.start() - - inst_api_patcher = mock.patch(dfa_instance_api.__name__ + - '.DFAInstanceAPI') - self.m_inst_api = inst_api_patcher.start() - - proj_patcher = mock.patch(projects_cache_db_v2.__name__ + - '.ProjectsInfoCache') - self.mock_proj = proj_patcher.start() - - dfa_cfg_patcher = mock.patch(config.__name__ + '.CiscoDFAConfig') - self.m_dfa_cfg = dfa_cfg_patcher.start() - ml2_cisco_dfa_opts = {'dcnm_password': FAKE_DCNM_PASS, - 'dcnm_user': FAKE_DCNM_USER, - 'dcnm_ip': FAKE_DCNM_IP} - for opt, val in ml2_cisco_dfa_opts.items(): - cfg.CONF.set_override(opt, val, 'ml2_cisco_dfa') - - self.dfa_mech_drvr = mech_cisco_dfa.CiscoDfaMechanismDriver() - self.dfa_mech_drvr.initialize() - self.dfa_mech_drvr._keys.is_valid_project.return_value = True - self.net_context = self._create_network_context() - self.proj_info = projects_cache_db_v2.ProjectsInfoCache() - - def _create_network_context(self): - net_info = {'name': FAKE_NETWORK_NAME, - 'tenant_id': FAKE_PROJECT_ID, - 'dfa:cfg_profile_id': FAKE_CFG_PROF_ID, - 'provider:segmentation_id': FAKE_SEG_ID, - 'id': FAKE_NETWORK_ID} - net_context = FakeNetworkContext(net_info) - net_context._plugin_context = mock.MagicMock() - net_context._session = net_context._plugin_context.session - return net_context - - def _create_subnet_context(self): - subnet_info = { - 'ipv6_ra_mode': None, - 'allocation_pools': [{'start': FAKE_DHCP_IP_RANGE_START, - 'end': FAKE_DHCP_IP_RANGE_END}], - 'host_routes': [], - 'ipv6_address_mode': None, - 'cidr': '23.24.25.0/24', - 'id': FAKE_SUBNET_ID, - 'name': u'', - 'enable_dhcp': True, - 'network_id': FAKE_NETWORK_ID, - 'tenant_id': FAKE_PROJECT_ID, - 'dns_nameservers': [], - 'gateway_ip': FAKE_GW_ADDR, - 'ip_version': 4, - 'shared': False} - subnet_context = FakeSubnetContext(subnet_info) - subnet_context._plugin_context = mock.MagicMock() - return subnet_context - - def _create_port_context(self): - port_info = { - 'status': 'ACTIVE', - 'binding:host_id': FAKE_HOST_ID, - 'allowed_address_pairs': [], - 'extra_dhcp_opts': [], - 'device_owner': u'compute:nova', - 'binding:profile': {}, - 'fixed_ips': [{'subnet_id': FAKE_SUBNET_ID, - 'ip_address': FAKE_IP_ADDR}], - 'id': FAKE_PORT_ID, - 'security_groups': [FAKE_SECURITY_GRP_ID], - 'device_id': FAKE_DEVICE_ID, - 'name': u'', - 'admin_state_up': True, - 'network_id': FAKE_NETWORK_ID, - 'tenant_id': FAKE_PROJECT_ID, - 'binding:vif_details': {u'port_filter': True, - u'ovs_hybrid_plug': True}, - 'binding:vnic_type': u'normal', - 'binding:vif_type': u'ovs', - 'mac_address': FAKE_MAC_ADDR} - port_context = FakePortContext(mock.MagicMock(), port_info) - port_context._plugin_context = mock.MagicMock() - port_context._session = port_context._plugin_context.session - return port_context - - def test_create_network_postcommit_no_profile(self): - query = self.net_context._session.query.return_value - query.filter_by.return_value.one.return_value = None - # Profile does not exist, catch the exception. - with testtools.ExpectedException(n_exc.BadRequest): - self.dfa_mech_drvr.create_network_postcommit(self.net_context) - - def test_create_network_postcommit_no_project(self): - self.proj_info.get_project_name.side_effect = ( - dexc.ProjectIdNotFound(project_id=FAKE_PROJECT_ID)) - # Project does not exist, catch the exception. - with testtools.ExpectedException(dexc.ProjectIdNotFound): - self.dfa_mech_drvr.create_network_postcommit(self.net_context) - - def test_delete_network_postcommit(self): - self.dfa_mech_drvr.delete_network_postcommit(self.net_context) - self.mdcnm.delete_network.return_value = None - self.assertTrue(self.dfa_mech_drvr._dcnm_client.delete_network.called) - - def test_create_subnet_postcommit(self): - subnet_ctxt = self._create_subnet_context() - proj_obj = self.dfa_mech_drvr.projects_cache_db_v2 - cfgp_mock = mock.MagicMock(return_value=FAKE_CFG_PROFILE_NAME) - self.dfa_mech_drvr.get_config_profile_name = cfgp_mock - mechdrvr_mock = mock.MagicMock(return_value=self.net_context.current) - self.dfa_mech_drvr.get_network_entry = mechdrvr_mock - proj_obj.get_network_segid.return_value = FAKE_SEG_ID - proj_obj.get_project_name.return_value = FAKE_PROJECT_NAME - self.dfa_mech_drvr.create_subnet_postcommit(subnet_ctxt) - self.assertTrue(self.dfa_mech_drvr._dcnm_client.create_network.called) - - def test_update_port_postcommit(self): - port_ctxt = self._create_port_context() - query = port_ctxt._session.query.return_value - query.filter_by.return_value.one.return_value.forwarding_mode = ( - FAKE_FWD_MODE) - vm_info = { - 'status': 'up', - 'ip': port_ctxt.current.get('fixed_ips')[0]['ip_address'], - 'mac': port_ctxt.current.get('mac_address'), - 'segid': FAKE_SEG_ID, - 'inst_name': FAKE_INSTANCE_NAME, - 'inst_uuid': port_ctxt.current.get('device_id').replace('-', ''), - 'host': FAKE_HOST_ID, - 'port_id': port_ctxt.current.get('id'), - 'network_id': port_ctxt.current.get('network_id'), - 'oui_type': 'cisco', - } - self.proj_info.get_network_segid.return_value = FAKE_SEG_ID - mechdrvr_mock = self.dfa_mech_drvr._inst_api.get_instance_for_uuid - mechdrvr_mock.return_value = FAKE_INSTANCE_NAME - self.dfa_mech_drvr.dfa_notifier = mock.MagicMock() - self.dfa_mech_drvr.update_port_postcommit(port_ctxt) - self.assertTrue(self.dfa_mech_drvr.dfa_notifier.send_vm_info.called) - self.dfa_mech_drvr.dfa_notifier.send_vm_info.assert_called_with( - port_ctxt._plugin_context, vm_info) - - def test_delete_port_postcommit(self): - port_ctxt = self._create_port_context() - query = port_ctxt._session.query.return_value - query.filter_by.return_value.one.return_value.forwarding_mode = ( - FAKE_FWD_MODE) - vm_info = { - 'status': 'down', - 'ip': port_ctxt.current.get('fixed_ips')[0]['ip_address'], - 'mac': port_ctxt.current.get('mac_address'), - 'segid': FAKE_SEG_ID, - 'inst_name': FAKE_INSTANCE_NAME, - 'inst_uuid': port_ctxt.current.get('device_id').replace('-', ''), - 'host': FAKE_HOST_ID, - 'port_id': port_ctxt.current.get('id'), - 'network_id': port_ctxt.current.get('network_id'), - 'oui_type': 'cisco', - } - self.proj_info.get_network_segid.return_value = FAKE_SEG_ID - instapi_mock = self.dfa_mech_drvr._inst_api.get_instance_for_uuid - instapi_mock.return_value = FAKE_INSTANCE_NAME - self.dfa_mech_drvr.dfa_notifier = mock.MagicMock() - self.dfa_mech_drvr.delete_port_postcommit(port_ctxt) - self.assertTrue(self.dfa_mech_drvr.dfa_notifier.send_vm_info.called) - self.dfa_mech_drvr.dfa_notifier.send_vm_info.assert_called_with( - port_ctxt._plugin_context, vm_info) diff --git a/setup.cfg b/setup.cfg index 056698ab45b..d3aa4329dce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -170,7 +170,6 @@ neutron.ml2.mechanism_drivers = arista = neutron.plugins.ml2.drivers.arista.mechanism_arista:AristaDriver cisco_nexus = neutron.plugins.ml2.drivers.cisco.nexus.mech_cisco_nexus:CiscoNexusMechanismDriver cisco_apic = neutron.plugins.ml2.drivers.cisco.apic.mechanism_apic:APICMechanismDriver - cisco_dfa = neutron.plugins.ml2.drivers.cisco.dfa.mech_cisco_dfa:CiscoDfaMechanismDriver l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver bigswitch = neutron.plugins.ml2.drivers.mech_bigswitch.driver:BigSwitchMechanismDriver ofagent = neutron.plugins.ml2.drivers.mech_ofagent:OfagentMechanismDriver