diff --git a/neutron/plugins/bigswitch/README b/neutron/plugins/bigswitch/README deleted file mode 100644 index 43f157d12..000000000 --- a/neutron/plugins/bigswitch/README +++ /dev/null @@ -1,14 +0,0 @@ -# Neuron REST Proxy Plug-in for Big Switch and FloodLight Controllers - -This module provides a generic neutron plugin 'NeutronRestProxy' that -translates neutron function calls to authenticated REST requests (JSON supported) -to a set of redundant external network controllers. - -It also keeps a local persistent store of neutron state that has been -setup using that API. - -Currently the FloodLight Openflow Controller or the Big Switch Networks Controller -can be configured as external network controllers for this plugin. - -For more details on this plugin, please refer to the following link: -http://www.openflowhub.org/display/floodlightcontroller/Neutron+REST+Proxy+Plugin diff --git a/neutron/plugins/bigswitch/__init__.py b/neutron/plugins/bigswitch/__init__.py deleted file mode 100644 index 2a2421616..000000000 --- a/neutron/plugins/bigswitch/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 Big Switch Networks, 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. -# diff --git a/neutron/plugins/bigswitch/agent/__init__.py b/neutron/plugins/bigswitch/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/bigswitch/agent/restproxy_agent.py b/neutron/plugins/bigswitch/agent/restproxy_agent.py deleted file mode 100644 index 97aa7d0e3..000000000 --- a/neutron/plugins/bigswitch/agent/restproxy_agent.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright 2014 Big Switch Networks, Inc. -# All Rights Reserved. -# -# Copyright 2011 VMware, 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. -# @author: Kevin Benton, kevin.benton@bigswitch.com - -import sys -import time - -import eventlet -eventlet.monkey_patch() - -from oslo.config import cfg - -from neutron.agent.linux import ovs_lib -from neutron.agent.linux import utils -from neutron.agent import rpc as agent_rpc -from neutron.agent import securitygroups_rpc as sg_rpc -from neutron.common import config -from neutron.common import rpc_compat -from neutron.common import topics -from neutron import context as q_context -from neutron.extensions import securitygroup as ext_sg -from neutron.openstack.common import excutils -from neutron.openstack.common import log -from neutron.plugins.bigswitch import config as pl_config - -LOG = log.getLogger(__name__) - - -class IVSBridge(ovs_lib.OVSBridge): - ''' - This class does not provide parity with OVS using IVS. - It's only the bare minimum necessary to use IVS with this agent. - ''' - def run_vsctl(self, args, check_error=False): - full_args = ["ivs-ctl"] + args - try: - return utils.execute(full_args, root_helper=self.root_helper) - except Exception as e: - with excutils.save_and_reraise_exception() as ctxt: - LOG.error(_("Unable to execute %(cmd)s. " - "Exception: %(exception)s"), - {'cmd': full_args, 'exception': e}) - if not check_error: - ctxt.reraise = False - - def get_vif_port_set(self): - port_names = self.get_port_name_list() - edge_ports = set(port_names) - return edge_ports - - def get_vif_port_by_id(self, port_id): - # IVS in nova uses hybrid method with last 14 chars of UUID - name = 'qvo%s' % port_id[:14] - if name in self.get_vif_port_set(): - return name - return False - - -class PluginApi(agent_rpc.PluginApi, - sg_rpc.SecurityGroupServerRpcApiMixin): - pass - - -class SecurityGroupAgent(sg_rpc.SecurityGroupAgentRpcMixin): - def __init__(self, context, plugin_rpc, root_helper): - self.context = context - self.plugin_rpc = plugin_rpc - self.root_helper = root_helper - self.init_firewall() - - -class RestProxyAgent(rpc_compat.RpcCallback, - sg_rpc.SecurityGroupAgentRpcCallbackMixin): - - RPC_API_VERSION = '1.1' - - def __init__(self, integ_br, polling_interval, root_helper, vs='ovs'): - super(RestProxyAgent, self).__init__() - self.polling_interval = polling_interval - self._setup_rpc() - self.sg_agent = SecurityGroupAgent(self.context, - self.plugin_rpc, - root_helper) - if vs == 'ivs': - self.int_br = IVSBridge(integ_br, root_helper) - else: - self.int_br = ovs_lib.OVSBridge(integ_br, root_helper) - - def _setup_rpc(self): - self.topic = topics.AGENT - self.plugin_rpc = PluginApi(topics.PLUGIN) - self.context = q_context.get_admin_context_without_session() - self.endpoints = [self] - consumers = [[topics.PORT, topics.UPDATE], - [topics.SECURITY_GROUP, topics.UPDATE]] - self.connection = agent_rpc.create_consumers(self.endpoints, - self.topic, - consumers) - - def port_update(self, context, **kwargs): - LOG.debug(_("Port update received")) - port = kwargs.get('port') - vif_port = self.int_br.get_vif_port_by_id(port['id']) - if not vif_port: - LOG.debug(_("Port %s is not present on this host."), port['id']) - return - - LOG.debug(_("Port %s found. Refreshing firewall."), port['id']) - if ext_sg.SECURITYGROUPS in port: - self.sg_agent.refresh_firewall() - - def _update_ports(self, registered_ports): - ports = self.int_br.get_vif_port_set() - if ports == registered_ports: - return - added = ports - registered_ports - removed = registered_ports - ports - return {'current': ports, - 'added': added, - 'removed': removed} - - def _process_devices_filter(self, port_info): - if 'added' in port_info: - self.sg_agent.prepare_devices_filter(port_info['added']) - if 'removed' in port_info: - self.sg_agent.remove_devices_filter(port_info['removed']) - - def daemon_loop(self): - ports = set() - - while True: - start = time.time() - try: - port_info = self._update_ports(ports) - if port_info: - LOG.debug(_("Agent loop has new device")) - self._process_devices_filter(port_info) - ports = port_info['current'] - except Exception: - LOG.exception(_("Error in agent event loop")) - - elapsed = max(time.time() - start, 0) - if (elapsed < self.polling_interval): - time.sleep(self.polling_interval - elapsed) - else: - LOG.debug(_("Loop iteration exceeded interval " - "(%(polling_interval)s vs. %(elapsed)s)!"), - {'polling_interval': self.polling_interval, - 'elapsed': elapsed}) - - -def main(): - config.init(sys.argv[1:]) - config.setup_logging(cfg.CONF) - pl_config.register_config() - - integ_br = cfg.CONF.RESTPROXYAGENT.integration_bridge - polling_interval = cfg.CONF.RESTPROXYAGENT.polling_interval - root_helper = cfg.CONF.AGENT.root_helper - bsnagent = RestProxyAgent(integ_br, polling_interval, root_helper, - cfg.CONF.RESTPROXYAGENT.virtual_switch_type) - bsnagent.daemon_loop() - sys.exit(0) - -if __name__ == "__main__": - main() diff --git a/neutron/plugins/bigswitch/config.py b/neutron/plugins/bigswitch/config.py deleted file mode 100644 index 4646319c9..000000000 --- a/neutron/plugins/bigswitch/config.py +++ /dev/null @@ -1,123 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2014 Big Switch Networks, 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. -# -# @author: Mandeep Dhami, Big Switch Networks, Inc. -# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc. -# @author: Kevin Benton, Big Switch Networks, Inc. - -""" -This module manages configuration options -""" - -from oslo.config import cfg - -from neutron.agent.common import config as agconfig -from neutron.common import utils -from neutron.extensions import portbindings - -restproxy_opts = [ - cfg.ListOpt('servers', default=['localhost:8800'], - help=_("A comma separated list of Big Switch or Floodlight " - "servers and port numbers. The plugin proxies the " - "requests to the Big Switch/Floodlight server, " - "which performs the networking configuration. Only one" - "server is needed per deployment, but you may wish to" - "deploy multiple servers to support failover.")), - cfg.StrOpt('server_auth', secret=True, - help=_("The username and password for authenticating against " - " the Big Switch or Floodlight controller.")), - cfg.BoolOpt('server_ssl', default=True, - help=_("If True, Use SSL when connecting to the Big Switch or " - "Floodlight controller.")), - cfg.BoolOpt('ssl_sticky', default=True, - help=_("Trust and store the first certificate received for " - "each controller address and use it to validate future " - "connections to that address.")), - cfg.BoolOpt('no_ssl_validation', default=False, - help=_("Disables SSL certificate validation for controllers")), - cfg.BoolOpt('cache_connections', default=True, - help=_("Re-use HTTP/HTTPS connections to the controller.")), - cfg.StrOpt('ssl_cert_directory', - default='/etc/neutron/plugins/bigswitch/ssl', - help=_("Directory containing ca_certs and host_certs " - "certificate directories.")), - cfg.BoolOpt('sync_data', default=False, - help=_("Sync data on connect")), - cfg.BoolOpt('auto_sync_on_failure', default=True, - help=_("If neutron fails to create a resource because " - "the backend controller doesn't know of a dependency, " - "the plugin automatically triggers a full data " - "synchronization to the controller.")), - cfg.IntOpt('consistency_interval', default=60, - help=_("Time between verifications that the backend controller " - "database is consistent with Neutron. (0 to disable)")), - cfg.IntOpt('server_timeout', default=10, - help=_("Maximum number of seconds to wait for proxy request " - "to connect and complete.")), - cfg.IntOpt('thread_pool_size', default=4, - help=_("Maximum number of threads to spawn to handle large " - "volumes of port creations.")), - cfg.StrOpt('neutron_id', default='neutron-' + utils.get_hostname(), - deprecated_name='quantum_id', - help=_("User defined identifier for this Neutron deployment")), - cfg.BoolOpt('add_meta_server_route', default=True, - help=_("Flag to decide if a route to the metadata server " - "should be injected into the VM")), -] -router_opts = [ - cfg.MultiStrOpt('tenant_default_router_rule', default=['*:any:any:permit'], - help=_("The default router rules installed in new tenant " - "routers. Repeat the config option for each rule. " - "Format is :::" - " Use an * to specify default for all tenants.")), - cfg.IntOpt('max_router_rules', default=200, - help=_("Maximum number of router rules")), -] -nova_opts = [ - cfg.StrOpt('vif_type', default='ovs', - help=_("Virtual interface type to configure on " - "Nova compute nodes")), -] - -# Each VIF Type can have a list of nova host IDs that are fixed to that type -for i in portbindings.VIF_TYPES: - opt = cfg.ListOpt('node_override_vif_' + i, default=[], - help=_("Nova compute nodes to manually set VIF " - "type to %s") % i) - nova_opts.append(opt) - -# Add the vif types for reference later -nova_opts.append(cfg.ListOpt('vif_types', - default=portbindings.VIF_TYPES, - help=_('List of allowed vif_type values.'))) - -agent_opts = [ - cfg.StrOpt('integration_bridge', default='br-int', - help=_('Name of integration bridge on compute ' - 'nodes used for security group insertion.')), - cfg.IntOpt('polling_interval', default=5, - help=_('Seconds between agent checks for port changes')), - cfg.StrOpt('virtual_switch_type', default='ovs', - help=_('Virtual switch type.')) -] - - -def register_config(): - cfg.CONF.register_opts(restproxy_opts, "RESTPROXY") - cfg.CONF.register_opts(router_opts, "ROUTER") - cfg.CONF.register_opts(nova_opts, "NOVA") - cfg.CONF.register_opts(agent_opts, "RESTPROXYAGENT") - agconfig.register_root_helper(cfg.CONF) diff --git a/neutron/plugins/bigswitch/db/__init__.py b/neutron/plugins/bigswitch/db/__init__.py deleted file mode 100644 index c05daecf8..000000000 --- a/neutron/plugins/bigswitch/db/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Big Switch Networks, 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. -# -# @author: Kevin Benton, Big Switch Networks, Inc. diff --git a/neutron/plugins/bigswitch/db/consistency_db.py b/neutron/plugins/bigswitch/db/consistency_db.py deleted file mode 100644 index cd89a2690..000000000 --- a/neutron/plugins/bigswitch/db/consistency_db.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2014, Big Switch Networks -# 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 sqlalchemy as sa - -from neutron.db import api as db -from neutron.db import model_base -from neutron.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - -''' -A simple table to store the latest consistency hash -received from a server in case neutron gets restarted. -''' - - -class ConsistencyHash(model_base.BASEV2): - ''' - For now we only support one global state so the - hash_id will always be '1' - ''' - __tablename__ = 'consistencyhashes' - hash_id = sa.Column(sa.String(255), - primary_key=True) - hash = sa.Column(sa.String(255), nullable=False) - - -def get_consistency_hash(hash_id='1'): - session = db.get_session() - with session.begin(subtransactions=True): - query = session.query(ConsistencyHash) - res = query.filter_by(hash_id=hash_id).first() - if not res: - return False - return res.hash - - -def put_consistency_hash(hash, hash_id='1'): - session = db.get_session() - with session.begin(subtransactions=True): - conhash = ConsistencyHash(hash_id=hash_id, hash=hash) - session.merge(conhash) - LOG.debug(_("Consistency hash for group %(hash_id)s updated " - "to %(hash)s"), {'hash_id': hash_id, 'hash': hash}) diff --git a/neutron/plugins/bigswitch/db/porttracker_db.py b/neutron/plugins/bigswitch/db/porttracker_db.py deleted file mode 100644 index 7966c7c7d..000000000 --- a/neutron/plugins/bigswitch/db/porttracker_db.py +++ /dev/null @@ -1,53 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013, Big Switch Networks -# 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.api.v2 import attributes -from neutron.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - - -def get_port_hostid(context, port_id): - # REVISIT(kevinbenton): this is a workaround to avoid portbindings_db - # relational table generation until one of the functions is called. - from neutron.db import portbindings_db - with context.session.begin(subtransactions=True): - query = context.session.query(portbindings_db.PortBindingPort) - res = query.filter_by(port_id=port_id).first() - if not res: - return False - return res.host - - -def put_port_hostid(context, port_id, host): - # REVISIT(kevinbenton): this is a workaround to avoid portbindings_db - # relational table generation until one of the functions is called. - from neutron.db import portbindings_db - if not attributes.is_attr_set(host): - LOG.warning(_("No host_id in port request to track port location.")) - return - if port_id == '': - LOG.warning(_("Received an empty port ID for host_id '%s'"), host) - return - if host == '': - LOG.debug(_("Received an empty host_id for port '%s'"), port_id) - return - LOG.debug(_("Logging port %(port)s on host_id %(host)s"), - {'port': port_id, 'host': host}) - with context.session.begin(subtransactions=True): - location = portbindings_db.PortBindingPort(port_id=port_id, host=host) - context.session.merge(location) diff --git a/neutron/plugins/bigswitch/extensions/__init__.py b/neutron/plugins/bigswitch/extensions/__init__.py deleted file mode 100644 index c05daecf8..000000000 --- a/neutron/plugins/bigswitch/extensions/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Big Switch Networks, 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. -# -# @author: Kevin Benton, Big Switch Networks, Inc. diff --git a/neutron/plugins/bigswitch/extensions/routerrule.py b/neutron/plugins/bigswitch/extensions/routerrule.py deleted file mode 100644 index 2563d113d..000000000 --- a/neutron/plugins/bigswitch/extensions/routerrule.py +++ /dev/null @@ -1,144 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Big Switch Networks, 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. -# -# @author: Kevin Benton, Big Switch Networks, Inc. - -from neutron.api.v2 import attributes as attr -from neutron.common import exceptions as qexception -from neutron.openstack.common import log as logging - - -LOG = logging.getLogger(__name__) - - -# Router Rules Exceptions -class InvalidRouterRules(qexception.InvalidInput): - message = _("Invalid format for router rules: %(rule)s, %(reason)s") - - -class RulesExhausted(qexception.BadRequest): - message = _("Unable to complete rules update for %(router_id)s. " - "The number of rules exceeds the maximum %(quota)s.") - - -def convert_to_valid_router_rules(data): - """ - Validates and converts router rules to the appropriate data structure - Example argument = [{'source': 'any', 'destination': 'any', - 'action':'deny'}, - {'source': '1.1.1.1/32', 'destination': 'external', - 'action':'permit', - 'nexthops': ['1.1.1.254', '1.1.1.253']} - ] - """ - V4ANY = '0.0.0.0/0' - CIDRALL = ['any', 'external'] - if not isinstance(data, list): - emsg = _("Invalid data format for router rule: '%s'") % data - LOG.debug(emsg) - raise qexception.InvalidInput(error_message=emsg) - _validate_uniquerules(data) - rules = [] - expected_keys = ['source', 'destination', 'action'] - for rule in data: - rule['nexthops'] = rule.get('nexthops', []) - if not isinstance(rule['nexthops'], list): - rule['nexthops'] = rule['nexthops'].split('+') - - src = V4ANY if rule['source'] in CIDRALL else rule['source'] - dst = V4ANY if rule['destination'] in CIDRALL else rule['destination'] - - errors = [attr._verify_dict_keys(expected_keys, rule, False), - attr._validate_subnet(dst), - attr._validate_subnet(src), - _validate_nexthops(rule['nexthops']), - _validate_action(rule['action'])] - errors = [m for m in errors if m] - if errors: - LOG.debug(errors) - raise qexception.InvalidInput(error_message=errors) - rules.append(rule) - return rules - - -def _validate_nexthops(nexthops): - seen = [] - for ip in nexthops: - msg = attr._validate_ip_address(ip) - if ip in seen: - msg = _("Duplicate nexthop in rule '%s'") % ip - seen.append(ip) - if msg: - return msg - - -def _validate_action(action): - if action not in ['permit', 'deny']: - return _("Action must be either permit or deny." - " '%s' was provided") % action - - -def _validate_uniquerules(rules): - pairs = [] - for r in rules: - if 'source' not in r or 'destination' not in r: - continue - pairs.append((r['source'], r['destination'])) - - if len(set(pairs)) != len(pairs): - error = _("Duplicate router rules (src,dst) found '%s'") % pairs - LOG.debug(error) - raise qexception.InvalidInput(error_message=error) - - -class Routerrule(object): - - @classmethod - def get_name(cls): - return "Neutron Router Rule" - - @classmethod - def get_alias(cls): - return "router_rules" - - @classmethod - def get_description(cls): - return "Router rule configuration for L3 router" - - @classmethod - def get_namespace(cls): - return "http://docs.openstack.org/ext/neutron/routerrules/api/v1.0" - - @classmethod - def get_updated(cls): - return "2013-05-23T10:00:00-00:00" - - def get_extended_resources(self, version): - if version == "2.0": - return EXTENDED_ATTRIBUTES_2_0 - else: - return {} - -# Attribute Map -EXTENDED_ATTRIBUTES_2_0 = { - 'routers': { - 'router_rules': {'allow_post': False, 'allow_put': True, - 'convert_to': convert_to_valid_router_rules, - 'is_visible': True, - 'default': attr.ATTR_NOT_SPECIFIED}, - } -} diff --git a/neutron/plugins/bigswitch/plugin.py b/neutron/plugins/bigswitch/plugin.py deleted file mode 100644 index c13c45b65..000000000 --- a/neutron/plugins/bigswitch/plugin.py +++ /dev/null @@ -1,1115 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2012 Big Switch Networks, 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. -# -# @author: Mandeep Dhami, Big Switch Networks, Inc. -# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc. - -""" -Neutron REST Proxy Plug-in for Big Switch and FloodLight Controllers. - -NeutronRestProxy provides a generic neutron plugin that translates all plugin -function calls to equivalent authenticated REST calls to a set of redundant -external network controllers. It also keeps persistent store for all neutron -state to allow for re-sync of the external controller(s), if required. - -The local state on the plugin also allows for local response and fast-fail -semantics where it can be determined based on the local persistent store. - -Network controller specific code is decoupled from this plugin and expected -to reside on the controller itself (via the REST interface). - -This allows for: - - independent authentication and redundancy schemes between neutron and the - network controller - - independent upgrade/development cycles between neutron and the controller - as it limits the proxy code upgrade requirement to neutron release cycle - and the controller specific code upgrade requirement to controller code - - ability to sync the controller with neutron for independent recovery/reset - -External REST API used by proxy is the same API as defined for neutron (JSON -subset) with some additional parameters (gateway on network-create and macaddr -on port-attach) on an additional PUT to do a bulk dump of all persistent data. -""" - -import copy -import httplib -import re - -import eventlet -from oslo.config import cfg -from sqlalchemy.orm import exc as sqlexc - -from neutron.agent import securitygroups_rpc as sg_rpc -from neutron.api import extensions as neutron_extensions -from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api -from neutron.common import constants as const -from neutron.common import exceptions -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.common import utils -from neutron import context as qcontext -from neutron.db import agents_db -from neutron.db import agentschedulers_db -from neutron.db import allowedaddresspairs_db as addr_pair_db -from neutron.db import api as db -from neutron.db import db_base_plugin_v2 -from neutron.db import dhcp_rpc_base -from neutron.db import external_net_db -from neutron.db import extradhcpopt_db -from neutron.db import l3_db -from neutron.db import models_v2 -from neutron.db import securitygroups_db as sg_db -from neutron.db import securitygroups_rpc_base as sg_rpc_base -from neutron.extensions import allowedaddresspairs as addr_pair -from neutron.extensions import external_net -from neutron.extensions import extra_dhcp_opt as edo_ext -from neutron.extensions import l3 -from neutron.extensions import portbindings -from neutron import manager -from neutron.openstack.common import excutils -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.bigswitch import config as pl_config -from neutron.plugins.bigswitch.db import porttracker_db -from neutron.plugins.bigswitch import extensions -from neutron.plugins.bigswitch import routerrule_db -from neutron.plugins.bigswitch import servermanager -from neutron.plugins.bigswitch import version - -LOG = logging.getLogger(__name__) - -SYNTAX_ERROR_MESSAGE = _('Syntax error in server config file, aborting plugin') -METADATA_SERVER_IP = '169.254.169.254' - - -class AgentNotifierApi(rpc_compat.RpcProxy, - sg_rpc.SecurityGroupAgentRpcApiMixin): - - BASE_RPC_API_VERSION = '1.1' - - def __init__(self, topic): - super(AgentNotifierApi, self).__init__( - topic=topic, default_version=self.BASE_RPC_API_VERSION) - self.topic_port_update = topics.get_topic_name( - topic, topics.PORT, topics.UPDATE) - - def port_update(self, context, port): - self.fanout_cast(context, - self.make_msg('port_update', - port=port), - topic=self.topic_port_update) - - -class RestProxyCallbacks(rpc_compat.RpcCallback, - sg_rpc_base.SecurityGroupServerRpcCallbackMixin, - dhcp_rpc_base.DhcpRpcCallbackMixin): - - RPC_API_VERSION = '1.1' - - def get_port_from_device(self, device): - port_id = re.sub(r"^tap", "", device) - port = self.get_port_and_sgs(port_id) - if port: - port['device'] = device - return port - - def get_port_and_sgs(self, port_id): - """Get port from database with security group info.""" - - LOG.debug(_("get_port_and_sgs() called for port_id %s"), port_id) - session = db.get_session() - sg_binding_port = sg_db.SecurityGroupPortBinding.port_id - - with session.begin(subtransactions=True): - query = session.query( - models_v2.Port, - sg_db.SecurityGroupPortBinding.security_group_id - ) - query = query.outerjoin(sg_db.SecurityGroupPortBinding, - models_v2.Port.id == sg_binding_port) - query = query.filter(models_v2.Port.id.startswith(port_id)) - port_and_sgs = query.all() - if not port_and_sgs: - return - port = port_and_sgs[0][0] - plugin = manager.NeutronManager.get_plugin() - port_dict = plugin._make_port_dict(port) - port_dict['security_groups'] = [ - sg_id for port_, sg_id in port_and_sgs if sg_id] - port_dict['security_group_rules'] = [] - port_dict['security_group_source_groups'] = [] - port_dict['fixed_ips'] = [ip['ip_address'] - for ip in port['fixed_ips']] - return port_dict - - -class NeutronRestProxyV2Base(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - routerrule_db.RouterRule_db_mixin): - - supported_extension_aliases = ["binding"] - servers = None - - def _get_all_data(self, get_ports=True, get_floating_ips=True, - get_routers=True): - admin_context = qcontext.get_admin_context() - networks = [] - # this method is used by the ML2 driver so it can't directly invoke - # the self.get_(ports|networks) methods - plugin = manager.NeutronManager.get_plugin() - all_networks = plugin.get_networks(admin_context) or [] - for net in all_networks: - mapped_network = self._get_mapped_network_with_subnets(net) - flips_n_ports = mapped_network - if get_floating_ips: - flips_n_ports = self._get_network_with_floatingips( - mapped_network) - - if get_ports: - ports = [] - net_filter = {'network_id': [net.get('id')]} - net_ports = plugin.get_ports(admin_context, - filters=net_filter) or [] - for port in net_ports: - mapped_port = self._map_state_and_status(port) - mapped_port['attachment'] = { - 'id': port.get('device_id'), - 'mac': port.get('mac_address'), - } - mapped_port = self._extend_port_dict_binding(admin_context, - mapped_port) - ports.append(mapped_port) - flips_n_ports['ports'] = ports - - if flips_n_ports: - networks.append(flips_n_ports) - - data = {'networks': networks} - - if get_routers: - routers = [] - all_routers = self.get_routers(admin_context) or [] - for router in all_routers: - interfaces = [] - mapped_router = self._map_state_and_status(router) - router_filter = { - 'device_owner': [const.DEVICE_OWNER_ROUTER_INTF], - 'device_id': [router.get('id')] - } - router_ports = self.get_ports(admin_context, - filters=router_filter) or [] - for port in router_ports: - net_id = port.get('network_id') - subnet_id = port['fixed_ips'][0]['subnet_id'] - intf_details = self._get_router_intf_details(admin_context, - net_id, - subnet_id) - interfaces.append(intf_details) - mapped_router['interfaces'] = interfaces - - routers.append(mapped_router) - - data.update({'routers': routers}) - return data - - def _send_all_data(self, send_ports=True, send_floating_ips=True, - send_routers=True, timeout=None, - triggered_by_tenant=None): - """Pushes all data to network ctrl (networks/ports, ports/attachments). - - This gives the controller an option to re-sync it's persistent store - with neutron's current view of that data. - """ - data = self._get_all_data(send_ports, send_floating_ips, send_routers) - data['triggered_by_tenant'] = triggered_by_tenant - errstr = _("Unable to update remote topology: %s") - return self.servers.rest_action('PUT', servermanager.TOPOLOGY_PATH, - data, errstr, timeout=timeout) - - def _get_network_with_floatingips(self, network, context=None): - if context is None: - context = qcontext.get_admin_context() - - net_id = network['id'] - net_filter = {'floating_network_id': [net_id]} - fl_ips = self.get_floatingips(context, - filters=net_filter) or [] - network['floatingips'] = fl_ips - - return network - - def _get_all_subnets_json_for_network(self, net_id, context=None): - if context is None: - context = qcontext.get_admin_context() - # start a sub-transaction to avoid breaking parent transactions - with context.session.begin(subtransactions=True): - subnets = self._get_subnets_by_network(context, - net_id) - subnets_details = [] - if subnets: - for subnet in subnets: - subnet_dict = self._make_subnet_dict(subnet) - mapped_subnet = self._map_state_and_status(subnet_dict) - subnets_details.append(mapped_subnet) - - return subnets_details - - def _get_mapped_network_with_subnets(self, network, context=None): - # if context is not provided, admin context is used - if context is None: - context = qcontext.get_admin_context() - network = self._map_state_and_status(network) - subnets = self._get_all_subnets_json_for_network(network['id'], - context) - network['subnets'] = subnets - for subnet in (subnets or []): - if subnet['gateway_ip']: - # FIX: For backward compatibility with wire protocol - network['gateway'] = subnet['gateway_ip'] - break - else: - network['gateway'] = '' - network[external_net.EXTERNAL] = self._network_is_external( - context, network['id']) - # include ML2 segmentation types - network['segmentation_types'] = getattr(self, "segmentation_types", "") - return network - - def _send_create_network(self, network, context=None): - tenant_id = network['tenant_id'] - mapped_network = self._get_mapped_network_with_subnets(network, - context) - self.servers.rest_create_network(tenant_id, mapped_network) - - def _send_update_network(self, network, context=None): - net_id = network['id'] - tenant_id = network['tenant_id'] - mapped_network = self._get_mapped_network_with_subnets(network, - context) - net_fl_ips = self._get_network_with_floatingips(mapped_network, - context) - self.servers.rest_update_network(tenant_id, net_id, net_fl_ips) - - def _send_delete_network(self, network, context=None): - net_id = network['id'] - tenant_id = network['tenant_id'] - self.servers.rest_delete_network(tenant_id, net_id) - - def _map_state_and_status(self, resource): - resource = copy.copy(resource) - - resource['state'] = ('UP' if resource.pop('admin_state_up', - True) else 'DOWN') - resource.pop('status', None) - - return resource - - def _warn_on_state_status(self, resource): - if resource.get('admin_state_up', True) is False: - LOG.warning(_("Setting admin_state_up=False is not supported " - "in this plugin version. Ignoring setting for " - "resource: %s"), resource) - - if 'status' in resource: - if resource['status'] != const.NET_STATUS_ACTIVE: - LOG.warning(_("Operational status is internally set by the " - "plugin. Ignoring setting status=%s."), - resource['status']) - - def _get_router_intf_details(self, context, intf_id, subnet_id): - - # we will use the network id as interface's id - net_id = intf_id - network = self.get_network(context, net_id) - subnet = self.get_subnet(context, subnet_id) - mapped_network = self._get_mapped_network_with_subnets(network) - mapped_subnet = self._map_state_and_status(subnet) - - data = { - 'id': intf_id, - "network": mapped_network, - "subnet": mapped_subnet - } - - return data - - def _extend_port_dict_binding(self, context, port): - cfg_vif_type = cfg.CONF.NOVA.vif_type.lower() - if not cfg_vif_type in (portbindings.VIF_TYPE_OVS, - portbindings.VIF_TYPE_IVS): - LOG.warning(_("Unrecognized vif_type in configuration " - "[%s]. Defaulting to ovs."), - cfg_vif_type) - cfg_vif_type = portbindings.VIF_TYPE_OVS - # In ML2, the host_id is already populated - if portbindings.HOST_ID in port: - hostid = port[portbindings.HOST_ID] - else: - hostid = porttracker_db.get_port_hostid(context, port['id']) - if hostid: - port[portbindings.HOST_ID] = hostid - override = self._check_hostvif_override(hostid) - if override: - cfg_vif_type = override - port[portbindings.VIF_TYPE] = cfg_vif_type - - port[portbindings.VIF_DETAILS] = { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases, - portbindings.OVS_HYBRID_PLUG: True - } - return port - - def _check_hostvif_override(self, hostid): - for v in cfg.CONF.NOVA.vif_types: - if hostid in getattr(cfg.CONF.NOVA, "node_override_vif_" + v, []): - return v - return False - - def _get_port_net_tenantid(self, context, port): - net = super(NeutronRestProxyV2Base, - self).get_network(context, port["network_id"]) - return net['tenant_id'] - - def async_port_create(self, tenant_id, net_id, port): - try: - self.servers.rest_create_port(tenant_id, net_id, port) - except servermanager.RemoteRestError as e: - # 404 should never be received on a port create unless - # there are inconsistencies between the data in neutron - # and the data in the backend. - # Run a sync to get it consistent. - if (cfg.CONF.RESTPROXY.auto_sync_on_failure and - e.status == httplib.NOT_FOUND and - servermanager.NXNETWORK in e.reason): - LOG.error(_("Iconsistency with backend controller " - "triggering full synchronization.")) - # args depend on if we are operating in ML2 driver - # or as the full plugin - topoargs = self.servers.get_topo_function_args - self._send_all_data( - send_ports=topoargs['get_ports'], - send_floating_ips=topoargs['get_floating_ips'], - send_routers=topoargs['get_routers'], - triggered_by_tenant=tenant_id - ) - # If the full sync worked, the port will be created - # on the controller so it can be safely marked as active - else: - # Any errors that don't result in a successful auto-sync - # require that the port be placed into the error state. - LOG.error( - _("NeutronRestProxyV2: Unable to create port: %s"), e) - try: - self._set_port_status(port['id'], const.PORT_STATUS_ERROR) - except exceptions.PortNotFound: - # If port is already gone from DB and there was an error - # creating on the backend, everything is already consistent - pass - return - new_status = (const.PORT_STATUS_ACTIVE if port['state'] == 'UP' - else const.PORT_STATUS_DOWN) - try: - self._set_port_status(port['id'], new_status) - except exceptions.PortNotFound: - # This port was deleted before the create made it to the controller - # so it now needs to be deleted since the normal delete request - # would have deleted an non-existent port. - self.servers.rest_delete_port(tenant_id, net_id, port['id']) - - # NOTE(kevinbenton): workaround for eventlet/mysql deadlock - @utils.synchronized('bsn-port-barrier') - def _set_port_status(self, port_id, status): - session = db.get_session() - try: - port = session.query(models_v2.Port).filter_by(id=port_id).one() - port['status'] = status - session.flush() - except sqlexc.NoResultFound: - raise exceptions.PortNotFound(port_id=port_id) - - -class NeutronRestProxyV2(NeutronRestProxyV2Base, - addr_pair_db.AllowedAddressPairsMixin, - extradhcpopt_db.ExtraDhcpOptMixin, - agentschedulers_db.DhcpAgentSchedulerDbMixin, - sg_rpc_base.SecurityGroupServerRpcMixin): - - _supported_extension_aliases = ["external-net", "router", "binding", - "router_rules", "extra_dhcp_opt", "quotas", - "dhcp_agent_scheduler", "agent", - "security-group", "allowed-address-pairs"] - - @property - def supported_extension_aliases(self): - if not hasattr(self, '_aliases'): - aliases = self._supported_extension_aliases[:] - sg_rpc.disable_security_group_extension_by_config(aliases) - self._aliases = aliases - return self._aliases - - def __init__(self): - super(NeutronRestProxyV2, self).__init__() - LOG.info(_('NeutronRestProxy: Starting plugin. Version=%s'), - version.version_string_with_vcs()) - pl_config.register_config() - self.evpool = eventlet.GreenPool(cfg.CONF.RESTPROXY.thread_pool_size) - - # Include the Big Switch Extensions path in the api_extensions - neutron_extensions.append_api_extensions_path(extensions.__path__) - - self.add_meta_server_route = cfg.CONF.RESTPROXY.add_meta_server_route - - # init network ctrl connections - self.servers = servermanager.ServerPool() - self.servers.get_topo_function = self._get_all_data - self.servers.get_topo_function_args = {'get_ports': True, - 'get_floating_ips': True, - 'get_routers': True} - - self.network_scheduler = importutils.import_object( - cfg.CONF.network_scheduler_driver - ) - - # setup rpc for security and DHCP agents - self._setup_rpc() - - if cfg.CONF.RESTPROXY.sync_data: - self._send_all_data() - - LOG.debug(_("NeutronRestProxyV2: initialization done")) - - def _setup_rpc(self): - self.conn = rpc_compat.create_connection(new=True) - self.topic = topics.PLUGIN - self.notifier = AgentNotifierApi(topics.AGENT) - # init dhcp agent support - self._dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI() - self.agent_notifiers[const.AGENT_TYPE_DHCP] = ( - self._dhcp_agent_notifier - ) - self.endpoints = [RestProxyCallbacks(), - agents_db.AgentExtRpcCallback()] - self.conn.create_consumer(self.topic, self.endpoints, - fanout=False) - # Consume from all consumers in threads - self.conn.consume_in_threads() - - def create_network(self, context, network): - """Create a network. - - Network represents an L2 network segment which can have a set of - subnets and ports associated with it. - - :param context: neutron api request context - :param network: dictionary describing the network - - :returns: a sequence of mappings with the following signature: - { - "id": UUID representing the network. - "name": Human-readable name identifying the network. - "tenant_id": Owner of network. NOTE: only admin user can specify - a tenant_id other than its own. - "admin_state_up": Sets admin state of network. - if down, network does not forward packets. - "status": Indicates whether network is currently operational - (values are "ACTIVE", "DOWN", "BUILD", and "ERROR") - "subnets": Subnets associated with this network. - } - - :raises: RemoteRestError - """ - LOG.debug(_("NeutronRestProxyV2: create_network() called")) - - self._warn_on_state_status(network['network']) - - with context.session.begin(subtransactions=True): - self._ensure_default_security_group( - context, - network['network']["tenant_id"] - ) - # create network in DB - new_net = super(NeutronRestProxyV2, self).create_network(context, - network) - self._process_l3_create(context, new_net, network['network']) - # create network on the network controller - self._send_create_network(new_net, context) - - # return created network - return new_net - - def update_network(self, context, net_id, network): - """Updates the properties of a particular Virtual Network. - - :param context: neutron api request context - :param net_id: uuid of the network to update - :param network: dictionary describing the updates - - :returns: a sequence of mappings with the following signature: - { - "id": UUID representing the network. - "name": Human-readable name identifying the network. - "tenant_id": Owner of network. NOTE: only admin user can - specify a tenant_id other than its own. - "admin_state_up": Sets admin state of network. - if down, network does not forward packets. - "status": Indicates whether network is currently operational - (values are "ACTIVE", "DOWN", "BUILD", and "ERROR") - "subnets": Subnets associated with this network. - } - - :raises: exceptions.NetworkNotFound - :raises: RemoteRestError - """ - LOG.debug(_("NeutronRestProxyV2.update_network() called")) - - self._warn_on_state_status(network['network']) - - session = context.session - with session.begin(subtransactions=True): - new_net = super(NeutronRestProxyV2, self).update_network( - context, net_id, network) - self._process_l3_update(context, new_net, network['network']) - - # update network on network controller - self._send_update_network(new_net, context) - return new_net - - # NOTE(kevinbenton): workaround for eventlet/mysql deadlock - @utils.synchronized('bsn-port-barrier') - def delete_network(self, context, net_id): - """Delete a network. - :param context: neutron api request context - :param id: UUID representing the network to delete. - - :returns: None - - :raises: exceptions.NetworkInUse - :raises: exceptions.NetworkNotFound - :raises: RemoteRestError - """ - LOG.debug(_("NeutronRestProxyV2: delete_network() called")) - - # Validate args - orig_net = super(NeutronRestProxyV2, self).get_network(context, net_id) - with context.session.begin(subtransactions=True): - self._process_l3_delete(context, net_id) - ret_val = super(NeutronRestProxyV2, self).delete_network(context, - net_id) - self._send_delete_network(orig_net, context) - return ret_val - - def create_port(self, context, port): - """Create a port, which is a connection point of a device - (e.g., a VM NIC) to attach to a L2 Neutron network. - :param context: neutron api request context - :param port: dictionary describing the port - - :returns: - { - "id": uuid represeting the port. - "network_id": uuid of network. - "tenant_id": tenant_id - "mac_address": mac address to use on this port. - "admin_state_up": Sets admin state of port. if down, port - does not forward packets. - "status": dicates whether port is currently operational - (limit values to "ACTIVE", "DOWN", "BUILD", and "ERROR") - "fixed_ips": list of subnet ID"s and IP addresses to be used on - this port - "device_id": identifies the device (e.g., virtual server) using - this port. - } - - :raises: exceptions.NetworkNotFound - :raises: exceptions.StateInvalid - :raises: RemoteRestError - """ - LOG.debug(_("NeutronRestProxyV2: create_port() called")) - - # Update DB in new session so exceptions rollback changes - with context.session.begin(subtransactions=True): - self._ensure_default_security_group_on_port(context, port) - sgids = self._get_security_groups_on_port(context, port) - # non-router port status is set to pending. it is then updated - # after the async rest call completes. router ports are synchronous - if port['port']['device_owner'] == l3_db.DEVICE_OWNER_ROUTER_INTF: - port['port']['status'] = const.PORT_STATUS_ACTIVE - else: - port['port']['status'] = const.PORT_STATUS_BUILD - dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, []) - new_port = super(NeutronRestProxyV2, self).create_port(context, - port) - self._process_port_create_security_group(context, new_port, sgids) - if (portbindings.HOST_ID in port['port'] - and 'id' in new_port): - host_id = port['port'][portbindings.HOST_ID] - porttracker_db.put_port_hostid(context, new_port['id'], - host_id) - new_port[addr_pair.ADDRESS_PAIRS] = ( - self._process_create_allowed_address_pairs( - context, new_port, - port['port'].get(addr_pair.ADDRESS_PAIRS))) - self._process_port_create_extra_dhcp_opts(context, new_port, - dhcp_opts) - new_port = self._extend_port_dict_binding(context, new_port) - net = super(NeutronRestProxyV2, - self).get_network(context, new_port["network_id"]) - if self.add_meta_server_route: - if new_port['device_owner'] == const.DEVICE_OWNER_DHCP: - destination = METADATA_SERVER_IP + '/32' - self._add_host_route(context, destination, new_port) - - # create on network ctrl - mapped_port = self._map_state_and_status(new_port) - # ports have to be created synchronously when creating a router - # port since adding router interfaces is a multi-call process - if mapped_port['device_owner'] == l3_db.DEVICE_OWNER_ROUTER_INTF: - self.servers.rest_create_port(net["tenant_id"], - new_port["network_id"], - mapped_port) - else: - self.evpool.spawn_n(self.async_port_create, net["tenant_id"], - new_port["network_id"], mapped_port) - self.notify_security_groups_member_updated(context, new_port) - return new_port - - def get_port(self, context, id, fields=None): - with context.session.begin(subtransactions=True): - port = super(NeutronRestProxyV2, self).get_port(context, id, - fields) - self._extend_port_dict_binding(context, port) - return self._fields(port, fields) - - def get_ports(self, context, filters=None, fields=None): - with context.session.begin(subtransactions=True): - ports = super(NeutronRestProxyV2, self).get_ports(context, filters, - fields) - for port in ports: - self._extend_port_dict_binding(context, port) - return [self._fields(port, fields) for port in ports] - - def update_port(self, context, port_id, port): - """Update values of a port. - - :param context: neutron api request context - :param id: UUID representing the port to update. - :param port: dictionary with keys indicating fields to update. - - :returns: a mapping sequence with the following signature: - { - "id": uuid represeting the port. - "network_id": uuid of network. - "tenant_id": tenant_id - "mac_address": mac address to use on this port. - "admin_state_up": sets admin state of port. if down, port - does not forward packets. - "status": dicates whether port is currently operational - (limit values to "ACTIVE", "DOWN", "BUILD", and "ERROR") - "fixed_ips": list of subnet ID's and IP addresses to be used on - this port - "device_id": identifies the device (e.g., virtual server) using - this port. - } - - :raises: exceptions.StateInvalid - :raises: exceptions.PortNotFound - :raises: RemoteRestError - """ - LOG.debug(_("NeutronRestProxyV2: update_port() called")) - - self._warn_on_state_status(port['port']) - - # Validate Args - orig_port = super(NeutronRestProxyV2, self).get_port(context, port_id) - with context.session.begin(subtransactions=True): - # Update DB - new_port = super(NeutronRestProxyV2, - self).update_port(context, port_id, port) - ctrl_update_required = False - if addr_pair.ADDRESS_PAIRS in port['port']: - ctrl_update_required |= ( - self.update_address_pairs_on_port(context, port_id, port, - orig_port, new_port)) - self._update_extra_dhcp_opts_on_port(context, port_id, port, - new_port) - old_host_id = porttracker_db.get_port_hostid(context, - orig_port['id']) - if (portbindings.HOST_ID in port['port'] - and 'id' in new_port): - host_id = port['port'][portbindings.HOST_ID] - porttracker_db.put_port_hostid(context, new_port['id'], - host_id) - if old_host_id != host_id: - ctrl_update_required = True - - if (new_port.get("device_id") != orig_port.get("device_id") and - orig_port.get("device_id")): - ctrl_update_required = True - - if ctrl_update_required: - # tenant_id must come from network in case network is shared - net_tenant_id = self._get_port_net_tenantid(context, new_port) - new_port = self._extend_port_dict_binding(context, new_port) - mapped_port = self._map_state_and_status(new_port) - self.servers.rest_update_port(net_tenant_id, - new_port["network_id"], - mapped_port) - agent_update_required = self.update_security_group_on_port( - context, port_id, port, orig_port, new_port) - agent_update_required |= self.is_security_group_member_updated( - context, orig_port, new_port) - - # return new_port - return new_port - - # NOTE(kevinbenton): workaround for eventlet/mysql deadlock - @utils.synchronized('bsn-port-barrier') - def delete_port(self, context, port_id, l3_port_check=True): - """Delete a port. - :param context: neutron api request context - :param id: UUID representing the port to delete. - - :raises: exceptions.PortInUse - :raises: exceptions.PortNotFound - :raises: exceptions.NetworkNotFound - :raises: RemoteRestError - """ - LOG.debug(_("NeutronRestProxyV2: delete_port() called")) - - # if needed, check to see if this is a port owned by - # and l3-router. If so, we should prevent deletion. - if l3_port_check: - self.prevent_l3_port_deletion(context, port_id) - with context.session.begin(subtransactions=True): - self.disassociate_floatingips(context, port_id) - self._delete_port_security_group_bindings(context, port_id) - port = super(NeutronRestProxyV2, self).get_port(context, port_id) - # Tenant ID must come from network in case the network is shared - tenid = self._get_port_net_tenantid(context, port) - self._delete_port(context, port_id) - self.servers.rest_delete_port(tenid, port['network_id'], port_id) - - def create_subnet(self, context, subnet): - LOG.debug(_("NeutronRestProxyV2: create_subnet() called")) - - self._warn_on_state_status(subnet['subnet']) - - with context.session.begin(subtransactions=True): - # create subnet in DB - new_subnet = super(NeutronRestProxyV2, - self).create_subnet(context, subnet) - net_id = new_subnet['network_id'] - orig_net = super(NeutronRestProxyV2, - self).get_network(context, net_id) - # update network on network controller - self._send_update_network(orig_net, context) - return new_subnet - - def update_subnet(self, context, id, subnet): - LOG.debug(_("NeutronRestProxyV2: update_subnet() called")) - - self._warn_on_state_status(subnet['subnet']) - - with context.session.begin(subtransactions=True): - # update subnet in DB - new_subnet = super(NeutronRestProxyV2, - self).update_subnet(context, id, subnet) - net_id = new_subnet['network_id'] - orig_net = super(NeutronRestProxyV2, - self).get_network(context, net_id) - # update network on network controller - self._send_update_network(orig_net, context) - return new_subnet - - # NOTE(kevinbenton): workaround for eventlet/mysql deadlock - @utils.synchronized('bsn-port-barrier') - def delete_subnet(self, context, id): - LOG.debug(_("NeutronRestProxyV2: delete_subnet() called")) - orig_subnet = super(NeutronRestProxyV2, self).get_subnet(context, id) - net_id = orig_subnet['network_id'] - with context.session.begin(subtransactions=True): - # delete subnet in DB - super(NeutronRestProxyV2, self).delete_subnet(context, id) - orig_net = super(NeutronRestProxyV2, self).get_network(context, - net_id) - # update network on network controller - exception will rollback - self._send_update_network(orig_net, context) - - def _get_tenant_default_router_rules(self, tenant): - rules = cfg.CONF.ROUTER.tenant_default_router_rule - defaultset = [] - tenantset = [] - for rule in rules: - items = rule.split(':') - if len(items) == 5: - (tenantid, source, destination, action, nexthops) = items - elif len(items) == 4: - (tenantid, source, destination, action) = items - nexthops = '' - else: - continue - parsedrule = {'source': source, - 'destination': destination, 'action': action, - 'nexthops': nexthops.split(',')} - if parsedrule['nexthops'][0] == '': - parsedrule['nexthops'] = [] - if tenantid == '*': - defaultset.append(parsedrule) - if tenantid == tenant: - tenantset.append(parsedrule) - if tenantset: - return tenantset - return defaultset - - def create_router(self, context, router): - LOG.debug(_("NeutronRestProxyV2: create_router() called")) - - self._warn_on_state_status(router['router']) - - tenant_id = self._get_tenant_id_for_create(context, router["router"]) - - # set default router rules - rules = self._get_tenant_default_router_rules(tenant_id) - router['router']['router_rules'] = rules - - with context.session.begin(subtransactions=True): - # create router in DB - new_router = super(NeutronRestProxyV2, self).create_router(context, - router) - mapped_router = self._map_state_and_status(new_router) - self.servers.rest_create_router(tenant_id, mapped_router) - - # return created router - return new_router - - def update_router(self, context, router_id, router): - - LOG.debug(_("NeutronRestProxyV2.update_router() called")) - - self._warn_on_state_status(router['router']) - - orig_router = super(NeutronRestProxyV2, self).get_router(context, - router_id) - tenant_id = orig_router["tenant_id"] - with context.session.begin(subtransactions=True): - new_router = super(NeutronRestProxyV2, - self).update_router(context, router_id, router) - router = self._map_state_and_status(new_router) - - # update router on network controller - self.servers.rest_update_router(tenant_id, router, router_id) - - # return updated router - return new_router - - # NOTE(kevinbenton): workaround for eventlet/mysql deadlock. - # delete_router ends up calling _delete_port instead of delete_port. - @utils.synchronized('bsn-port-barrier') - def delete_router(self, context, router_id): - LOG.debug(_("NeutronRestProxyV2: delete_router() called")) - - with context.session.begin(subtransactions=True): - orig_router = self._get_router(context, router_id) - tenant_id = orig_router["tenant_id"] - - # Ensure that the router is not used - router_filter = {'router_id': [router_id]} - fips = self.get_floatingips_count(context.elevated(), - filters=router_filter) - if fips: - raise l3.RouterInUse(router_id=router_id) - - device_owner = l3_db.DEVICE_OWNER_ROUTER_INTF - device_filter = {'device_id': [router_id], - 'device_owner': [device_owner]} - ports = self.get_ports_count(context.elevated(), - filters=device_filter) - if ports: - raise l3.RouterInUse(router_id=router_id) - ret_val = super(NeutronRestProxyV2, - self).delete_router(context, router_id) - - # delete from network ctrl - self.servers.rest_delete_router(tenant_id, router_id) - return ret_val - - def add_router_interface(self, context, router_id, interface_info): - - LOG.debug(_("NeutronRestProxyV2: add_router_interface() called")) - - # Validate args - router = self._get_router(context, router_id) - tenant_id = router['tenant_id'] - - with context.session.begin(subtransactions=True): - # create interface in DB - new_intf_info = super(NeutronRestProxyV2, - self).add_router_interface(context, - router_id, - interface_info) - port = self._get_port(context, new_intf_info['port_id']) - net_id = port['network_id'] - subnet_id = new_intf_info['subnet_id'] - # we will use the port's network id as interface's id - interface_id = net_id - intf_details = self._get_router_intf_details(context, - interface_id, - subnet_id) - - # create interface on the network controller - self.servers.rest_add_router_interface(tenant_id, router_id, - intf_details) - return new_intf_info - - def remove_router_interface(self, context, router_id, interface_info): - - LOG.debug(_("NeutronRestProxyV2: remove_router_interface() called")) - - # Validate args - router = self._get_router(context, router_id) - tenant_id = router['tenant_id'] - - # we will first get the interface identifier before deleting in the DB - if not interface_info: - msg = _("Either subnet_id or port_id must be specified") - raise exceptions.BadRequest(resource='router', msg=msg) - if 'port_id' in interface_info: - port = self._get_port(context, interface_info['port_id']) - interface_id = port['network_id'] - elif 'subnet_id' in interface_info: - subnet = self._get_subnet(context, interface_info['subnet_id']) - interface_id = subnet['network_id'] - else: - msg = _("Either subnet_id or port_id must be specified") - raise exceptions.BadRequest(resource='router', msg=msg) - - with context.session.begin(subtransactions=True): - # remove router in DB - del_ret = super(NeutronRestProxyV2, - self).remove_router_interface(context, - router_id, - interface_info) - - # create router on the network controller - self.servers.rest_remove_router_interface(tenant_id, router_id, - interface_id) - return del_ret - - def create_floatingip(self, context, floatingip): - LOG.debug(_("NeutronRestProxyV2: create_floatingip() called")) - - with context.session.begin(subtransactions=True): - # create floatingip in DB - new_fl_ip = super(NeutronRestProxyV2, - self).create_floatingip(context, floatingip) - - # create floatingip on the network controller - try: - if 'floatingip' in self.servers.get_capabilities(): - self.servers.rest_create_floatingip( - new_fl_ip['tenant_id'], new_fl_ip) - else: - self._send_floatingip_update(context) - except servermanager.RemoteRestError as e: - with excutils.save_and_reraise_exception(): - LOG.error( - _("NeutronRestProxyV2: Unable to create remote " - "floating IP: %s"), e) - # return created floating IP - return new_fl_ip - - def update_floatingip(self, context, id, floatingip): - LOG.debug(_("NeutronRestProxyV2: update_floatingip() called")) - - with context.session.begin(subtransactions=True): - # update floatingip in DB - new_fl_ip = super(NeutronRestProxyV2, - self).update_floatingip(context, id, floatingip) - - # update network on network controller - if 'floatingip' in self.servers.get_capabilities(): - self.servers.rest_update_floatingip(new_fl_ip['tenant_id'], - new_fl_ip, id) - else: - self._send_floatingip_update(context) - return new_fl_ip - - def delete_floatingip(self, context, id): - LOG.debug(_("NeutronRestProxyV2: delete_floatingip() called")) - - with context.session.begin(subtransactions=True): - # delete floating IP in DB - old_fip = super(NeutronRestProxyV2, self).get_floatingip(context, - id) - super(NeutronRestProxyV2, self).delete_floatingip(context, id) - - # update network on network controller - if 'floatingip' in self.servers.get_capabilities(): - self.servers.rest_delete_floatingip(old_fip['tenant_id'], id) - else: - self._send_floatingip_update(context) - - def disassociate_floatingips(self, context, port_id): - LOG.debug(_("NeutronRestProxyV2: diassociate_floatingips() called")) - super(NeutronRestProxyV2, self).disassociate_floatingips(context, - port_id) - self._send_floatingip_update(context) - - # overriding method from l3_db as original method calls - # self.delete_floatingip() which in turn calls self.delete_port() which - # is locked with 'bsn-port-barrier' - def delete_disassociated_floatingips(self, context, network_id): - query = self._model_query(context, l3_db.FloatingIP) - query = query.filter_by(floating_network_id=network_id, - fixed_port_id=None, - router_id=None) - for fip in query: - context.session.delete(fip) - self._delete_port(context.elevated(), fip['floating_port_id']) - - def _send_floatingip_update(self, context): - try: - ext_net_id = self.get_external_network_id(context) - if ext_net_id: - # Use the elevated state of the context for the ext_net query - admin_context = context.elevated() - ext_net = super(NeutronRestProxyV2, - self).get_network(admin_context, ext_net_id) - # update external network on network controller - self._send_update_network(ext_net, admin_context) - except exceptions.TooManyExternalNetworks: - # get_external_network can raise errors when multiple external - # networks are detected, which isn't supported by the Plugin - LOG.error(_("NeutronRestProxyV2: too many external networks")) - - def _add_host_route(self, context, destination, port): - subnet = {} - for fixed_ip in port['fixed_ips']: - subnet_id = fixed_ip['subnet_id'] - nexthop = fixed_ip['ip_address'] - subnet['host_routes'] = [{'destination': destination, - 'nexthop': nexthop}] - updated_subnet = self.update_subnet(context, - subnet_id, - {'subnet': subnet}) - payload = {'subnet': updated_subnet} - self._dhcp_agent_notifier.notify(context, payload, - 'subnet.update.end') - LOG.debug(_("Adding host route: ")) - LOG.debug(_("Destination:%(dst)s nexthop:%(next)s"), - {'dst': destination, 'next': nexthop}) diff --git a/neutron/plugins/bigswitch/routerrule_db.py b/neutron/plugins/bigswitch/routerrule_db.py deleted file mode 100644 index e947a1f9d..000000000 --- a/neutron/plugins/bigswitch/routerrule_db.py +++ /dev/null @@ -1,148 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013, Big Switch Networks -# 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 sqlalchemy as sa -from sqlalchemy import orm - -from neutron.db import l3_db -from neutron.db import model_base -from neutron.openstack.common import log as logging -from neutron.plugins.bigswitch.extensions import routerrule - - -LOG = logging.getLogger(__name__) - - -class RouterRule(model_base.BASEV2): - id = sa.Column(sa.Integer, primary_key=True) - source = sa.Column(sa.String(64), nullable=False) - destination = sa.Column(sa.String(64), nullable=False) - nexthops = orm.relationship('NextHop', cascade='all,delete') - action = sa.Column(sa.String(10), nullable=False) - router_id = sa.Column(sa.String(36), - sa.ForeignKey('routers.id', - ondelete="CASCADE")) - - -class NextHop(model_base.BASEV2): - rule_id = sa.Column(sa.Integer, - sa.ForeignKey('routerrules.id', - ondelete="CASCADE"), - primary_key=True) - nexthop = sa.Column(sa.String(64), nullable=False, primary_key=True) - - -class RouterRule_db_mixin(l3_db.L3_NAT_db_mixin): - """ Mixin class to support route rule configuration on a router""" - def update_router(self, context, id, router): - r = router['router'] - with context.session.begin(subtransactions=True): - router_db = self._get_router(context, id) - if 'router_rules' in r: - self._update_router_rules(context, - router_db, - r['router_rules']) - updated = super(RouterRule_db_mixin, self).update_router( - context, id, router) - updated['router_rules'] = self._get_router_rules_by_router_id( - context, id) - - return updated - - def create_router(self, context, router): - r = router['router'] - with context.session.begin(subtransactions=True): - router_db = super(RouterRule_db_mixin, self).create_router( - context, router) - if 'router_rules' in r: - self._update_router_rules(context, - router_db, - r['router_rules']) - else: - LOG.debug(_('No rules in router')) - router_db['router_rules'] = self._get_router_rules_by_router_id( - context, router_db['id']) - - return router_db - - def _update_router_rules(self, context, router, rules): - if len(rules) > cfg.CONF.ROUTER.max_router_rules: - raise routerrule.RulesExhausted( - router_id=router['id'], - quota=cfg.CONF.ROUTER.max_router_rules) - del_context = context.session.query(RouterRule) - del_context.filter_by(router_id=router['id']).delete() - context.session.expunge_all() - LOG.debug(_('Updating router rules to %s'), rules) - for rule in rules: - router_rule = RouterRule( - router_id=router['id'], - destination=rule['destination'], - source=rule['source'], - action=rule['action']) - router_rule.nexthops = [NextHop(nexthop=hop) - for hop in rule['nexthops']] - context.session.add(router_rule) - context.session.flush() - - def _make_router_rule_list(self, router_rules): - ruleslist = [] - for rule in router_rules: - hops = [hop['nexthop'] for hop in rule['nexthops']] - ruleslist.append({'id': rule['id'], - 'destination': rule['destination'], - 'source': rule['source'], - 'action': rule['action'], - 'nexthops': hops}) - return ruleslist - - def _get_router_rules_by_router_id(self, context, id): - query = context.session.query(RouterRule) - router_rules = query.filter_by(router_id=id).all() - return self._make_router_rule_list(router_rules) - - def get_router(self, context, id, fields=None): - with context.session.begin(subtransactions=True): - router = super(RouterRule_db_mixin, self).get_router( - context, id, fields) - router['router_rules'] = self._get_router_rules_by_router_id( - context, id) - return router - - def get_routers(self, context, filters=None, fields=None, - sorts=None, limit=None, marker=None, - page_reverse=False): - with context.session.begin(subtransactions=True): - routers = super(RouterRule_db_mixin, self).get_routers( - context, filters, fields, sorts=sorts, limit=limit, - marker=marker, page_reverse=page_reverse) - for router in routers: - router['router_rules'] = self._get_router_rules_by_router_id( - context, router['id']) - return routers - - def get_sync_data(self, context, router_ids=None, active=None): - """Query routers and their related floating_ips, interfaces.""" - with context.session.begin(subtransactions=True): - routers = super(RouterRule_db_mixin, - self).get_sync_data(context, router_ids, - active=active) - for router in routers: - router['router_rules'] = self._get_router_rules_by_router_id( - context, router['id']) - return routers diff --git a/neutron/plugins/bigswitch/servermanager.py b/neutron/plugins/bigswitch/servermanager.py deleted file mode 100644 index caaa10133..000000000 --- a/neutron/plugins/bigswitch/servermanager.py +++ /dev/null @@ -1,595 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2014 Big Switch Networks, 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. -# -# @author: Mandeep Dhami, Big Switch Networks, Inc. -# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc. -# @author: Kevin Benton, Big Switch Networks, Inc. - -""" -This module manages the HTTP and HTTPS connections to the backend controllers. - -The main class it provides for external use is ServerPool which manages a set -of ServerProxy objects that correspond to individual backend controllers. - -The following functionality is handled by this module: -- Translation of rest_* function calls to HTTP/HTTPS calls to the controllers -- Automatic failover between controllers -- SSL Certificate enforcement -- HTTP Authentication - -""" -import base64 -import httplib -import os -import socket -import ssl - -import eventlet -from oslo.config import cfg - -from neutron.common import exceptions -from neutron.common import utils -from neutron.openstack.common import excutils -from neutron.openstack.common import jsonutils as json -from neutron.openstack.common import log as logging -from neutron.plugins.bigswitch.db import consistency_db as cdb - -LOG = logging.getLogger(__name__) - -# The following are used to invoke the API on the external controller -CAPABILITIES_PATH = "/capabilities" -NET_RESOURCE_PATH = "/tenants/%s/networks" -PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports" -ROUTER_RESOURCE_PATH = "/tenants/%s/routers" -ROUTER_INTF_OP_PATH = "/tenants/%s/routers/%s/interfaces" -NETWORKS_PATH = "/tenants/%s/networks/%s" -FLOATINGIPS_PATH = "/tenants/%s/floatingips/%s" -PORTS_PATH = "/tenants/%s/networks/%s/ports/%s" -ATTACHMENT_PATH = "/tenants/%s/networks/%s/ports/%s/attachment" -ROUTERS_PATH = "/tenants/%s/routers/%s" -ROUTER_INTF_PATH = "/tenants/%s/routers/%s/interfaces/%s" -TOPOLOGY_PATH = "/topology" -HEALTH_PATH = "/health" -SUCCESS_CODES = range(200, 207) -FAILURE_CODES = [0, 301, 302, 303, 400, 401, 403, 404, 500, 501, 502, 503, - 504, 505] -BASE_URI = '/networkService/v1.1' -ORCHESTRATION_SERVICE_ID = 'Neutron v2.0' -HASH_MATCH_HEADER = 'X-BSN-BVS-HASH-MATCH' -# error messages -NXNETWORK = 'NXVNS' - - -class RemoteRestError(exceptions.NeutronException): - message = _("Error in REST call to remote network " - "controller: %(reason)s") - status = None - - def __init__(self, **kwargs): - self.status = kwargs.pop('status', None) - self.reason = kwargs.get('reason') - super(RemoteRestError, self).__init__(**kwargs) - - -class ServerProxy(object): - """REST server proxy to a network controller.""" - - def __init__(self, server, port, ssl, auth, neutron_id, timeout, - base_uri, name, mypool, combined_cert): - self.server = server - self.port = port - self.ssl = ssl - self.base_uri = base_uri - self.timeout = timeout - self.name = name - self.success_codes = SUCCESS_CODES - self.auth = None - self.neutron_id = neutron_id - self.failed = False - self.capabilities = [] - # enable server to reference parent pool - self.mypool = mypool - # cache connection here to avoid a SSL handshake for every connection - self.currentconn = None - if auth: - self.auth = 'Basic ' + base64.encodestring(auth).strip() - self.combined_cert = combined_cert - - def get_capabilities(self): - try: - body = self.rest_call('GET', CAPABILITIES_PATH)[2] - self.capabilities = json.loads(body) - except Exception: - LOG.exception(_("Couldn't retrieve capabilities. " - "Newer API calls won't be supported.")) - LOG.info(_("The following capabilities were received " - "for %(server)s: %(cap)s"), {'server': self.server, - 'cap': self.capabilities}) - return self.capabilities - - def rest_call(self, action, resource, data='', headers={}, timeout=False, - reconnect=False): - uri = self.base_uri + resource - body = json.dumps(data) - if not headers: - headers = {} - headers['Content-type'] = 'application/json' - headers['Accept'] = 'application/json' - headers['NeutronProxy-Agent'] = self.name - headers['Instance-ID'] = self.neutron_id - headers['Orchestration-Service-ID'] = ORCHESTRATION_SERVICE_ID - headers[HASH_MATCH_HEADER] = self.mypool.consistency_hash or '' - if 'keep-alive' in self.capabilities: - headers['Connection'] = 'keep-alive' - else: - reconnect = True - if self.auth: - headers['Authorization'] = self.auth - - LOG.debug(_("ServerProxy: server=%(server)s, port=%(port)d, " - "ssl=%(ssl)r"), - {'server': self.server, 'port': self.port, 'ssl': self.ssl}) - LOG.debug(_("ServerProxy: resource=%(resource)s, data=%(data)r, " - "headers=%(headers)r, action=%(action)s"), - {'resource': resource, 'data': data, 'headers': headers, - 'action': action}) - - # unspecified timeout is False because a timeout can be specified as - # None to indicate no timeout. - if timeout is False: - timeout = self.timeout - - if timeout != self.timeout: - # need a new connection if timeout has changed - reconnect = True - - if not self.currentconn or reconnect: - if self.currentconn: - self.currentconn.close() - if self.ssl: - self.currentconn = HTTPSConnectionWithValidation( - self.server, self.port, timeout=timeout) - if self.currentconn is None: - LOG.error(_('ServerProxy: Could not establish HTTPS ' - 'connection')) - return 0, None, None, None - self.currentconn.combined_cert = self.combined_cert - else: - self.currentconn = httplib.HTTPConnection( - self.server, self.port, timeout=timeout) - if self.currentconn is None: - LOG.error(_('ServerProxy: Could not establish HTTP ' - 'connection')) - return 0, None, None, None - - try: - self.currentconn.request(action, uri, body, headers) - response = self.currentconn.getresponse() - newhash = response.getheader(HASH_MATCH_HEADER) - if newhash: - self._put_consistency_hash(newhash) - respstr = response.read() - respdata = respstr - if response.status in self.success_codes: - try: - respdata = json.loads(respstr) - except ValueError: - # response was not JSON, ignore the exception - pass - ret = (response.status, response.reason, respstr, respdata) - except httplib.HTTPException: - # If we were using a cached connection, try again with a new one. - with excutils.save_and_reraise_exception() as ctxt: - self.currentconn.close() - if reconnect: - # if reconnect is true, this was on a fresh connection so - # reraise since this server seems to be broken - ctxt.reraise = True - else: - # if reconnect is false, it was a cached connection so - # try one more time before re-raising - ctxt.reraise = False - return self.rest_call(action, resource, data, headers, - timeout=timeout, reconnect=True) - except (socket.timeout, socket.error) as e: - self.currentconn.close() - LOG.error(_('ServerProxy: %(action)s failure, %(e)r'), - {'action': action, 'e': e}) - ret = 0, None, None, None - LOG.debug(_("ServerProxy: status=%(status)d, reason=%(reason)r, " - "ret=%(ret)s, data=%(data)r"), {'status': ret[0], - 'reason': ret[1], - 'ret': ret[2], - 'data': ret[3]}) - return ret - - def _put_consistency_hash(self, newhash): - self.mypool.consistency_hash = newhash - cdb.put_consistency_hash(newhash) - - -class ServerPool(object): - - def __init__(self, timeout=False, - base_uri=BASE_URI, name='NeutronRestProxy'): - LOG.debug(_("ServerPool: initializing")) - # 'servers' is the list of network controller REST end-points - # (used in order specified till one succeeds, and it is sticky - # till next failure). Use 'server_auth' to encode api-key - servers = cfg.CONF.RESTPROXY.servers - self.auth = cfg.CONF.RESTPROXY.server_auth - self.ssl = cfg.CONF.RESTPROXY.server_ssl - self.neutron_id = cfg.CONF.RESTPROXY.neutron_id - self.base_uri = base_uri - self.name = name - self.timeout = cfg.CONF.RESTPROXY.server_timeout - self.always_reconnect = not cfg.CONF.RESTPROXY.cache_connections - default_port = 8000 - if timeout is not False: - self.timeout = timeout - - # Function to use to retrieve topology for consistency syncs. - # Needs to be set by module that uses the servermanager. - self.get_topo_function = None - self.get_topo_function_args = {} - - # Hash to send to backend with request as expected previous - # state to verify consistency. - self.consistency_hash = cdb.get_consistency_hash() - - if not servers: - raise cfg.Error(_('Servers not defined. Aborting server manager.')) - servers = [s if len(s.rsplit(':', 1)) == 2 - else "%s:%d" % (s, default_port) - for s in servers] - if any((len(spl) != 2 or not spl[1].isdigit()) - for spl in [sp.rsplit(':', 1) - for sp in servers]): - raise cfg.Error(_('Servers must be defined as :. ' - 'Configuration was %s') % servers) - self.servers = [ - self.server_proxy_for(server, int(port)) - for server, port in (s.rsplit(':', 1) for s in servers) - ] - eventlet.spawn(self._consistency_watchdog, - cfg.CONF.RESTPROXY.consistency_interval) - LOG.debug(_("ServerPool: initialization done")) - - def get_capabilities(self): - # lookup on first try - try: - return self.capabilities - except AttributeError: - # each server should return a list of capabilities it supports - # e.g. ['floatingip'] - capabilities = [set(server.get_capabilities()) - for server in self.servers] - # Pool only supports what all of the servers support - self.capabilities = set.intersection(*capabilities) - return self.capabilities - - def server_proxy_for(self, server, port): - combined_cert = self._get_combined_cert_for_server(server, port) - return ServerProxy(server, port, self.ssl, self.auth, self.neutron_id, - self.timeout, self.base_uri, self.name, mypool=self, - combined_cert=combined_cert) - - def _get_combined_cert_for_server(self, server, port): - # The ssl library requires a combined file with all trusted certs - # so we make one containing the trusted CAs and the corresponding - # host cert for this server - combined_cert = None - if self.ssl and not cfg.CONF.RESTPROXY.no_ssl_validation: - base_ssl = cfg.CONF.RESTPROXY.ssl_cert_directory - host_dir = os.path.join(base_ssl, 'host_certs') - ca_dir = os.path.join(base_ssl, 'ca_certs') - combined_dir = os.path.join(base_ssl, 'combined') - combined_cert = os.path.join(combined_dir, '%s.pem' % server) - if not os.path.exists(base_ssl): - raise cfg.Error(_('ssl_cert_directory [%s] does not exist. ' - 'Create it or disable ssl.') % base_ssl) - for automake in [combined_dir, ca_dir, host_dir]: - if not os.path.exists(automake): - os.makedirs(automake) - - # get all CA certs - certs = self._get_ca_cert_paths(ca_dir) - - # check for a host specific cert - hcert, exists = self._get_host_cert_path(host_dir, server) - if exists: - certs.append(hcert) - elif cfg.CONF.RESTPROXY.ssl_sticky: - self._fetch_and_store_cert(server, port, hcert) - certs.append(hcert) - if not certs: - raise cfg.Error(_('No certificates were found to verify ' - 'controller %s') % (server)) - self._combine_certs_to_file(certs, combined_cert) - return combined_cert - - def _combine_certs_to_file(self, certs, cfile): - ''' - Concatenates the contents of each certificate in a list of - certificate paths to one combined location for use with ssl - sockets. - ''' - with open(cfile, 'w') as combined: - for c in certs: - with open(c, 'r') as cert_handle: - combined.write(cert_handle.read()) - - def _get_host_cert_path(self, host_dir, server): - ''' - returns full path and boolean indicating existence - ''' - hcert = os.path.join(host_dir, '%s.pem' % server) - if os.path.exists(hcert): - return hcert, True - return hcert, False - - def _get_ca_cert_paths(self, ca_dir): - certs = [os.path.join(root, name) - for name in [ - name for (root, dirs, files) in os.walk(ca_dir) - for name in files - ] - if name.endswith('.pem')] - return certs - - def _fetch_and_store_cert(self, server, port, path): - ''' - Grabs a certificate from a server and writes it to - a given path. - ''' - try: - cert = ssl.get_server_certificate((server, port)) - except Exception as e: - raise cfg.Error(_('Could not retrieve initial ' - 'certificate from controller %(server)s. ' - 'Error details: %(error)s') % - {'server': server, 'error': str(e)}) - - LOG.warning(_("Storing to certificate for host %(server)s " - "at %(path)s") % {'server': server, - 'path': path}) - self._file_put_contents(path, cert) - - return cert - - def _file_put_contents(self, path, contents): - # Simple method to write to file. - # Created for easy Mocking - with open(path, 'w') as handle: - handle.write(contents) - - def server_failure(self, resp, ignore_codes=[]): - """Define failure codes as required. - - Note: We assume 301-303 is a failure, and try the next server in - the server pool. - """ - return (resp[0] in FAILURE_CODES and resp[0] not in ignore_codes) - - def action_success(self, resp): - """Defining success codes as required. - - Note: We assume any valid 2xx as being successful response. - """ - return resp[0] in SUCCESS_CODES - - @utils.synchronized('bsn-rest-call') - def rest_call(self, action, resource, data, headers, ignore_codes, - timeout=False): - good_first = sorted(self.servers, key=lambda x: x.failed) - first_response = None - for active_server in good_first: - ret = active_server.rest_call(action, resource, data, headers, - timeout, - reconnect=self.always_reconnect) - # If inconsistent, do a full synchronization - if ret[0] == httplib.CONFLICT: - if not self.get_topo_function: - raise cfg.Error(_('Server requires synchronization, ' - 'but no topology function was defined.')) - data = self.get_topo_function(**self.get_topo_function_args) - active_server.rest_call('PUT', TOPOLOGY_PATH, data, - timeout=None) - # Store the first response as the error to be bubbled up to the - # user since it was a good server. Subsequent servers will most - # likely be cluster slaves and won't have a useful error for the - # user (e.g. 302 redirect to master) - if not first_response: - first_response = ret - if not self.server_failure(ret, ignore_codes): - active_server.failed = False - return ret - else: - LOG.error(_('ServerProxy: %(action)s failure for servers: ' - '%(server)r Response: %(response)s'), - {'action': action, - 'server': (active_server.server, - active_server.port), - 'response': ret[3]}) - LOG.error(_("ServerProxy: Error details: status=%(status)d, " - "reason=%(reason)r, ret=%(ret)s, data=%(data)r"), - {'status': ret[0], 'reason': ret[1], 'ret': ret[2], - 'data': ret[3]}) - active_server.failed = True - - # All servers failed, reset server list and try again next time - LOG.error(_('ServerProxy: %(action)s failure for all servers: ' - '%(server)r'), - {'action': action, - 'server': tuple((s.server, - s.port) for s in self.servers)}) - return first_response - - def rest_action(self, action, resource, data='', errstr='%s', - ignore_codes=[], headers={}, timeout=False): - """ - Wrapper for rest_call that verifies success and raises a - RemoteRestError on failure with a provided error string - By default, 404 errors on DELETE calls are ignored because - they already do not exist on the backend. - """ - if not ignore_codes and action == 'DELETE': - ignore_codes = [404] - resp = self.rest_call(action, resource, data, headers, ignore_codes, - timeout) - if self.server_failure(resp, ignore_codes): - LOG.error(errstr, resp[2]) - raise RemoteRestError(reason=resp[2], status=resp[0]) - if resp[0] in ignore_codes: - LOG.warning(_("NeutronRestProxyV2: Received and ignored error " - "code %(code)s on %(action)s action to resource " - "%(resource)s"), - {'code': resp[2], 'action': action, - 'resource': resource}) - return resp - - def rest_create_router(self, tenant_id, router): - resource = ROUTER_RESOURCE_PATH % tenant_id - data = {"router": router} - errstr = _("Unable to create remote router: %s") - self.rest_action('POST', resource, data, errstr) - - def rest_update_router(self, tenant_id, router, router_id): - resource = ROUTERS_PATH % (tenant_id, router_id) - data = {"router": router} - errstr = _("Unable to update remote router: %s") - self.rest_action('PUT', resource, data, errstr) - - def rest_delete_router(self, tenant_id, router_id): - resource = ROUTERS_PATH % (tenant_id, router_id) - errstr = _("Unable to delete remote router: %s") - self.rest_action('DELETE', resource, errstr=errstr) - - def rest_add_router_interface(self, tenant_id, router_id, intf_details): - resource = ROUTER_INTF_OP_PATH % (tenant_id, router_id) - data = {"interface": intf_details} - errstr = _("Unable to add router interface: %s") - self.rest_action('POST', resource, data, errstr) - - def rest_remove_router_interface(self, tenant_id, router_id, interface_id): - resource = ROUTER_INTF_PATH % (tenant_id, router_id, interface_id) - errstr = _("Unable to delete remote intf: %s") - self.rest_action('DELETE', resource, errstr=errstr) - - def rest_create_network(self, tenant_id, network): - resource = NET_RESOURCE_PATH % tenant_id - data = {"network": network} - errstr = _("Unable to create remote network: %s") - self.rest_action('POST', resource, data, errstr) - - def rest_update_network(self, tenant_id, net_id, network): - resource = NETWORKS_PATH % (tenant_id, net_id) - data = {"network": network} - errstr = _("Unable to update remote network: %s") - self.rest_action('PUT', resource, data, errstr) - - def rest_delete_network(self, tenant_id, net_id): - resource = NETWORKS_PATH % (tenant_id, net_id) - errstr = _("Unable to update remote network: %s") - self.rest_action('DELETE', resource, errstr=errstr) - - def rest_create_port(self, tenant_id, net_id, port): - resource = ATTACHMENT_PATH % (tenant_id, net_id, port["id"]) - data = {"port": port} - device_id = port.get("device_id") - if not port["mac_address"] or not device_id: - # controller only cares about ports attached to devices - LOG.warning(_("No device MAC attached to port %s. " - "Skipping notification to controller."), port["id"]) - return - data["attachment"] = {"id": device_id, - "mac": port["mac_address"]} - errstr = _("Unable to create remote port: %s") - self.rest_action('PUT', resource, data, errstr) - - def rest_delete_port(self, tenant_id, network_id, port_id): - resource = ATTACHMENT_PATH % (tenant_id, network_id, port_id) - errstr = _("Unable to delete remote port: %s") - self.rest_action('DELETE', resource, errstr=errstr) - - def rest_update_port(self, tenant_id, net_id, port): - # Controller has no update operation for the port endpoint - # the create PUT method will replace - self.rest_create_port(tenant_id, net_id, port) - - def rest_create_floatingip(self, tenant_id, floatingip): - resource = FLOATINGIPS_PATH % (tenant_id, floatingip['id']) - errstr = _("Unable to create floating IP: %s") - self.rest_action('PUT', resource, errstr=errstr) - - def rest_update_floatingip(self, tenant_id, floatingip, oldid): - resource = FLOATINGIPS_PATH % (tenant_id, oldid) - errstr = _("Unable to update floating IP: %s") - self.rest_action('PUT', resource, errstr=errstr) - - def rest_delete_floatingip(self, tenant_id, oldid): - resource = FLOATINGIPS_PATH % (tenant_id, oldid) - errstr = _("Unable to delete floating IP: %s") - self.rest_action('DELETE', resource, errstr=errstr) - - def _consistency_watchdog(self, polling_interval=60): - if 'consistency' not in self.get_capabilities(): - LOG.warning(_("Backend server(s) do not support automated " - "consitency checks.")) - return - if not polling_interval: - LOG.warning(_("Consistency watchdog disabled by polling interval " - "setting of %s."), polling_interval) - return - while True: - # If consistency is supported, all we have to do is make any - # rest call and the consistency header will be added. If it - # doesn't match, the backend will return a synchronization error - # that will be handled by the rest_action. - eventlet.sleep(polling_interval) - try: - self.rest_action('GET', HEALTH_PATH) - except Exception: - LOG.exception(_("Encountered an error checking controller " - "health.")) - - -class HTTPSConnectionWithValidation(httplib.HTTPSConnection): - - # If combined_cert is None, the connection will continue without - # any certificate validation. - combined_cert = None - - def connect(self): - try: - sock = socket.create_connection((self.host, self.port), - self.timeout, self.source_address) - except AttributeError: - # python 2.6 doesn't have the source_address attribute - sock = socket.create_connection((self.host, self.port), - self.timeout) - if self._tunnel_host: - self.sock = sock - self._tunnel() - - if self.combined_cert: - self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=self.combined_cert) - else: - self.sock = ssl.wrap_socket(sock, self.key_file, - self.cert_file, - cert_reqs=ssl.CERT_NONE) diff --git a/neutron/plugins/bigswitch/tests/__init__.py b/neutron/plugins/bigswitch/tests/__init__.py deleted file mode 100644 index 2a2421616..000000000 --- a/neutron/plugins/bigswitch/tests/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 Big Switch Networks, 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. -# diff --git a/neutron/plugins/bigswitch/tests/test_server.py b/neutron/plugins/bigswitch/tests/test_server.py deleted file mode 100755 index ee0c2be3d..000000000 --- a/neutron/plugins/bigswitch/tests/test_server.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012, Big Switch Networks, 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. -# -# @author: Mandeep Dhami, Big Switch Networks, Inc. - -"""Test server mocking a REST based network ctrl. - -Used for NeutronRestProxy tests -""" -from __future__ import print_function - -import re - -from six import moves -from wsgiref import simple_server - -from neutron.openstack.common import jsonutils as json - - -class TestNetworkCtrl(object): - - def __init__(self, host='', port=8000, - default_status='404 Not Found', - default_response='404 Not Found', - debug=False): - self.host = host - self.port = port - self.default_status = default_status - self.default_response = default_response - self.debug = debug - self.debug_env = False - self.debug_resp = False - self.matches = [] - - def match(self, prior, method_regexp, uri_regexp, handler, data=None, - multi=True): - """Add to the list of exptected inputs. - - The incoming request is matched in the order of priority. For same - priority, match the oldest match request first. - - :param prior: intgere priority of this match (e.g. 100) - :param method_regexp: regexp to match method (e.g. 'PUT|POST') - :param uri_regexp: regexp to match uri (e.g. '/quantum/v?.?/') - :param handler: function with signature: - lambda(method, uri, body, **kwargs) : status, body - where - - method: HTTP method for this request - - uri: URI for this HTTP request - - body: body of this HTTP request - - kwargs are: - - data: data object that was in the match call - - node: TestNetworkCtrl object itself - - id: offset of the matching tuple - and return values is: - (status, body) where: - - status: HTTP resp status (e.g. '200 OK'). - If None, use default_status - - body: HTTP resp body. If None, use '' - """ - assert int(prior) == prior, 'Priority should an integer be >= 0' - assert prior >= 0, 'Priority should an integer be >= 0' - - lo, hi = 0, len(self.matches) - while lo < hi: - mid = (lo + hi) // 2 - if prior < self.matches[mid]: - hi = mid - else: - lo = mid + 1 - self.matches.insert(lo, (prior, method_regexp, uri_regexp, handler, - data, multi)) - - def remove_id(self, id_): - assert id_ >= 0, 'remove_id: id < 0' - assert id_ <= len(self.matches), 'remove_id: id > len()' - self.matches.pop(id_) - - def request_handler(self, method, uri, body): - retstatus = self.default_status - retbody = self.default_response - for i in moves.xrange(len(self.matches)): - (prior, method_regexp, uri_regexp, handler, data, multi) = \ - self.matches[i] - if re.match(method_regexp, method) and re.match(uri_regexp, uri): - kwargs = { - 'data': data, - 'node': self, - 'id': i, - } - retstatus, retbody = handler(method, uri, body, **kwargs) - if multi is False: - self.remove_id(i) - break - if retbody is None: - retbody = '' - return (retstatus, retbody) - - def server(self): - def app(environ, start_response): - uri = environ['PATH_INFO'] - method = environ['REQUEST_METHOD'] - headers = [('Content-type', 'text/json')] - content_len_str = environ['CONTENT_LENGTH'] - - content_len = 0 - request_data = None - if content_len_str: - content_len = int(content_len_str) - request_data = environ.get('wsgi.input').read(content_len) - if request_data: - try: - request_data = json.loads(request_data) - except Exception: - # OK for it not to be json! Ignore it - pass - - if self.debug: - print('\n') - if self.debug_env: - print('environ:') - for (key, value) in sorted(environ.iteritems()): - print(' %16s : %s' % (key, value)) - - print('%s %s' % (method, uri)) - if request_data: - print('%s' % - json.dumps(request_data, sort_keys=True, indent=4)) - - status, body = self.request_handler(method, uri, None) - body_data = None - if body: - try: - body_data = json.loads(body) - except Exception: - # OK for it not to be json! Ignore it - pass - - start_response(status, headers) - if self.debug: - if self.debug_env: - print('%s: %s' % ('Response', - json.dumps(body_data, sort_keys=True, indent=4))) - return body - return simple_server.make_server(self.host, self.port, app) - - def run(self): - print("Serving on port %d ..." % self.port) - try: - self.server().serve_forever() - except KeyboardInterrupt: - pass - - -if __name__ == "__main__": - import sys - - port = 8899 - if len(sys.argv) > 1: - port = int(sys.argv[1]) - - debug = False - if len(sys.argv) > 2: - if sys.argv[2].lower() in ['debug', 'true']: - debug = True - - ctrl = TestNetworkCtrl(port=port, - default_status='200 OK', - default_response='{"status":"200 OK"}', - debug=debug) - ctrl.match(100, 'GET', '/test', - lambda m, u, b, **k: ('200 OK', '["200 OK"]')) - ctrl.run() diff --git a/neutron/plugins/bigswitch/vcsversion.py b/neutron/plugins/bigswitch/vcsversion.py deleted file mode 100644 index 6ed5e2680..000000000 --- a/neutron/plugins/bigswitch/vcsversion.py +++ /dev/null @@ -1,27 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Big Switch Networks, 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. -# -# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com -# -version_info = {'branch_nick': u'neutron/trunk', - 'revision_id': u'1', - 'revno': 0} - - -NEUTRONRESTPROXY_VERSION = ['2013', '1', None] - - -FINAL = False # This becomes true at Release Candidate time diff --git a/neutron/plugins/bigswitch/version.py b/neutron/plugins/bigswitch/version.py deleted file mode 100755 index 2069d0bc8..000000000 --- a/neutron/plugins/bigswitch/version.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 OpenStack Foundation -# Copyright 2012, Big Switch Networks, 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. -# -# Based on openstack generic code -# @author: Mandeep Dhami, Big Switch Networks, Inc. - -"""Determine version of NeutronRestProxy plugin""" -from __future__ import print_function - -from neutron.plugins.bigswitch import vcsversion - - -YEAR, COUNT, REVISION = vcsversion.NEUTRONRESTPROXY_VERSION - - -def canonical_version_string(): - return '.'.join(filter(None, - vcsversion.NEUTRONRESTPROXY_VERSION)) - - -def version_string(): - if vcsversion.FINAL: - return canonical_version_string() - else: - return '%s-dev' % (canonical_version_string(),) - - -def vcs_version_string(): - return "%s:%s" % (vcsversion.version_info['branch_nick'], - vcsversion.version_info['revision_id']) - - -def version_string_with_vcs(): - return "%s-%s" % (canonical_version_string(), vcs_version_string()) - - -if __name__ == "__main__": - print(version_string_with_vcs()) diff --git a/neutron/plugins/brocade/NeutronPlugin.py b/neutron/plugins/brocade/NeutronPlugin.py deleted file mode 100644 index c633085d0..000000000 --- a/neutron/plugins/brocade/NeutronPlugin.py +++ /dev/null @@ -1,497 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Brocade Communications System, 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. -# -# Authors: -# Shiv Haris (sharis@brocade.com) -# Varma Bhupatiraju (vbhupati@#brocade.com) -# -# (Some parts adapted from LinuxBridge Plugin) -# TODO(shiv) need support for security groups - - -"""Implentation of Brocade Neutron Plugin.""" - -from oslo.config import cfg - -from neutron.agent import securitygroups_rpc as sg_rpc -from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api -from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api -from neutron.common import constants as q_const -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.common import utils -from neutron.db import agents_db -from neutron.db import agentschedulers_db -from neutron.db import api as db -from neutron.db import db_base_plugin_v2 -from neutron.db import dhcp_rpc_base -from neutron.db import external_net_db -from neutron.db import extraroute_db -from neutron.db import l3_agentschedulers_db -from neutron.db import l3_rpc_base -from neutron.db import portbindings_base -from neutron.db import securitygroups_rpc_base as sg_db_rpc -from neutron.extensions import portbindings -from neutron.extensions import securitygroup as ext_sg -from neutron.openstack.common import context -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.brocade.db import models as brocade_db -from neutron.plugins.brocade import vlanbm as vbm -from neutron.plugins.common import constants as svc_constants - - -LOG = logging.getLogger(__name__) -PLUGIN_VERSION = 0.88 -AGENT_OWNER_PREFIX = "network:" -NOS_DRIVER = 'neutron.plugins.brocade.nos.nosdriver.NOSdriver' - -SWITCH_OPTS = [cfg.StrOpt('address', default='', - help=_('The address of the host to SSH to')), - cfg.StrOpt('username', default='', - help=_('The SSH username to use')), - cfg.StrOpt('password', default='', secret=True, - help=_('The SSH password to use')), - cfg.StrOpt('ostype', default='NOS', - help=_('Currently unused')) - ] - -PHYSICAL_INTERFACE_OPTS = [cfg.StrOpt('physical_interface', default='eth0', - help=_('The network interface to use when creating' - 'a port')) - ] - -cfg.CONF.register_opts(SWITCH_OPTS, "SWITCH") -cfg.CONF.register_opts(PHYSICAL_INTERFACE_OPTS, "PHYSICAL_INTERFACE") - - -class BridgeRpcCallbacks(rpc_compat.RpcCallback, - dhcp_rpc_base.DhcpRpcCallbackMixin, - l3_rpc_base.L3RpcCallbackMixin, - sg_db_rpc.SecurityGroupServerRpcCallbackMixin): - """Agent callback.""" - - RPC_API_VERSION = '1.1' - # Device names start with "tap" - # history - # 1.1 Support Security Group RPC - TAP_PREFIX_LEN = 3 - - @classmethod - def get_port_from_device(cls, device): - """Get port from the brocade specific db.""" - - # TODO(shh) context is not being passed as - # an argument to this function; - # - # need to be fixed in: - # file: neutron/db/securtygroups_rpc_base.py - # function: securitygroup_rules_for_devices() - # which needs to pass context to us - - # Doing what other plugins are doing - session = db.get_session() - port = brocade_db.get_port_from_device( - session, device[cls.TAP_PREFIX_LEN:]) - - # TODO(shiv): need to extend the db model to include device owners - # make it appears that the device owner is of type network - if port: - port['device'] = device - port['device_owner'] = AGENT_OWNER_PREFIX - port['binding:vif_type'] = 'bridge' - return port - - def get_device_details(self, rpc_context, **kwargs): - """Agent requests device details.""" - - agent_id = kwargs.get('agent_id') - device = kwargs.get('device') - LOG.debug(_("Device %(device)s details requested from %(agent_id)s"), - {'device': device, 'agent_id': agent_id}) - port = brocade_db.get_port(rpc_context, device[self.TAP_PREFIX_LEN:]) - if port: - entry = {'device': device, - 'vlan_id': port.vlan_id, - 'network_id': port.network_id, - 'port_id': port.port_id, - 'physical_network': port.physical_interface, - 'admin_state_up': port.admin_state_up - } - - else: - entry = {'device': device} - LOG.debug(_("%s can not be found in database"), device) - return entry - - def update_device_down(self, rpc_context, **kwargs): - """Device no longer exists on agent.""" - - device = kwargs.get('device') - port = self.get_port_from_device(device) - if port: - entry = {'device': device, - 'exists': True} - # Set port status to DOWN - port_id = port['port_id'] - brocade_db.update_port_state(rpc_context, port_id, False) - else: - entry = {'device': device, - 'exists': False} - LOG.debug(_("%s can not be found in database"), device) - return entry - - -class AgentNotifierApi(rpc_compat.RpcProxy, - sg_rpc.SecurityGroupAgentRpcApiMixin): - """Agent side of the linux bridge rpc API. - - API version history: - 1.0 - Initial version. - 1.1 - Added get_active_networks_info, create_dhcp_port, - and update_dhcp_port methods. - - """ - - BASE_RPC_API_VERSION = '1.1' - - def __init__(self, topic): - super(AgentNotifierApi, self).__init__( - topic=topic, default_version=self.BASE_RPC_API_VERSION) - self.topic = topic - self.topic_network_delete = topics.get_topic_name(topic, - topics.NETWORK, - topics.DELETE) - self.topic_port_update = topics.get_topic_name(topic, - topics.PORT, - topics.UPDATE) - - def network_delete(self, context, network_id): - self.fanout_cast(context, - self.make_msg('network_delete', - network_id=network_id), - topic=self.topic_network_delete) - - def port_update(self, context, port, physical_network, vlan_id): - self.fanout_cast(context, - self.make_msg('port_update', - port=port, - physical_network=physical_network, - vlan_id=vlan_id), - topic=self.topic_port_update) - - -class BrocadePluginV2(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - extraroute_db.ExtraRoute_db_mixin, - sg_db_rpc.SecurityGroupServerRpcMixin, - l3_agentschedulers_db.L3AgentSchedulerDbMixin, - agentschedulers_db.DhcpAgentSchedulerDbMixin, - portbindings_base.PortBindingBaseMixin): - """BrocadePluginV2 is a Neutron plugin. - - Provides L2 Virtual Network functionality using VDX. Upper - layer driver class that interfaces to NETCONF layer below. - - """ - - def __init__(self): - """Initialize Brocade Plugin. - - Specify switch address and db configuration. - """ - - super(BrocadePluginV2, self).__init__() - self.supported_extension_aliases = ["binding", "security-group", - "external-net", "router", - "extraroute", "agent", - "l3_agent_scheduler", - "dhcp_agent_scheduler"] - - self.physical_interface = (cfg.CONF.PHYSICAL_INTERFACE. - physical_interface) - self.base_binding_dict = self._get_base_binding_dict() - portbindings_base.register_port_dict_function() - self.ctxt = context.get_admin_context() - self.ctxt.session = db.get_session() - self._vlan_bitmap = vbm.VlanBitmap(self.ctxt) - self._setup_rpc() - self.network_scheduler = importutils.import_object( - cfg.CONF.network_scheduler_driver - ) - self.router_scheduler = importutils.import_object( - cfg.CONF.router_scheduler_driver - ) - self.brocade_init() - - def brocade_init(self): - """Brocade specific initialization.""" - - self._switch = {'address': cfg.CONF.SWITCH.address, - 'username': cfg.CONF.SWITCH.username, - 'password': cfg.CONF.SWITCH.password - } - self._driver = importutils.import_object(NOS_DRIVER) - - def _setup_rpc(self): - # RPC support - self.service_topics = {svc_constants.CORE: topics.PLUGIN, - svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN} - self.rpc_context = context.RequestContext('neutron', 'neutron', - is_admin=False) - self.conn = rpc_compat.create_connection(new=True) - self.endpoints = [BridgeRpcCallbacks(), - agents_db.AgentExtRpcCallback()] - for svc_topic in self.service_topics.values(): - self.conn.create_consumer(svc_topic, self.endpoints, fanout=False) - # Consume from all consumers in threads - self.conn.consume_in_threads() - self.notifier = AgentNotifierApi(topics.AGENT) - self.agent_notifiers[q_const.AGENT_TYPE_DHCP] = ( - dhcp_rpc_agent_api.DhcpAgentNotifyAPI() - ) - self.agent_notifiers[q_const.AGENT_TYPE_L3] = ( - l3_rpc_agent_api.L3AgentNotifyAPI() - ) - - def create_network(self, context, network): - """Create network. - - This call to create network translates to creation of port-profile on - the physical switch. - """ - - with context.session.begin(subtransactions=True): - net = super(BrocadePluginV2, self).create_network(context, network) - net_uuid = net['id'] - vlan_id = self._vlan_bitmap.get_next_vlan(None) - switch = self._switch - try: - self._driver.create_network(switch['address'], - switch['username'], - switch['password'], - vlan_id) - except Exception: - # Proper formatting - LOG.exception(_("Brocade NOS driver error")) - LOG.debug(_("Returning the allocated vlan (%d) to the pool"), - vlan_id) - self._vlan_bitmap.release_vlan(int(vlan_id)) - raise Exception(_("Brocade plugin raised exception, " - "check logs")) - - brocade_db.create_network(context, net_uuid, vlan_id) - self._process_l3_create(context, net, network['network']) - - LOG.info(_("Allocated vlan (%d) from the pool"), vlan_id) - return net - - def delete_network(self, context, net_id): - """Delete network. - - This call to delete the network translates to removing the - port-profile on the physical switch. - """ - - with context.session.begin(subtransactions=True): - self._process_l3_delete(context, net_id) - result = super(BrocadePluginV2, self).delete_network(context, - net_id) - # we must delete all ports in db first (foreign key constraint) - # there is no need to delete port in the driver (its a no-op) - # (actually: note there is no such call to the driver) - bports = brocade_db.get_ports(context, net_id) - for bport in bports: - brocade_db.delete_port(context, bport['port_id']) - - # find the vlan for this network - net = brocade_db.get_network(context, net_id) - vlan_id = net['vlan'] - - # Tell hw to do remove PP - switch = self._switch - try: - self._driver.delete_network(switch['address'], - switch['username'], - switch['password'], - vlan_id) - except Exception: - # Proper formatting - LOG.exception(_("Brocade NOS driver error")) - raise Exception(_("Brocade plugin raised exception, " - "check logs")) - - # now ok to delete the network - brocade_db.delete_network(context, net_id) - - # relinquish vlan in bitmap - self._vlan_bitmap.release_vlan(int(vlan_id)) - return result - - def update_network(self, context, id, network): - - session = context.session - with session.begin(subtransactions=True): - net = super(BrocadePluginV2, self).update_network(context, id, - network) - self._process_l3_update(context, net, network['network']) - return net - - def create_port(self, context, port): - """Create logical port on the switch.""" - - tenant_id = port['port']['tenant_id'] - network_id = port['port']['network_id'] - admin_state_up = port['port']['admin_state_up'] - - physical_interface = self.physical_interface - - with context.session.begin(subtransactions=True): - bnet = brocade_db.get_network(context, network_id) - vlan_id = bnet['vlan'] - - neutron_port = super(BrocadePluginV2, self).create_port(context, - port) - self._process_portbindings_create_and_update(context, - port['port'], - neutron_port) - interface_mac = neutron_port['mac_address'] - port_id = neutron_port['id'] - - switch = self._switch - - # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx - mac = self.mac_reformat_62to34(interface_mac) - try: - self._driver.associate_mac_to_network(switch['address'], - switch['username'], - switch['password'], - vlan_id, - mac) - except Exception: - # Proper formatting - LOG.exception(_("Brocade NOS driver error")) - raise Exception(_("Brocade plugin raised exception, " - "check logs")) - - # save to brocade persistent db - brocade_db.create_port(context, port_id, network_id, - physical_interface, - vlan_id, tenant_id, admin_state_up) - - # apply any extensions - return neutron_port - - def delete_port(self, context, port_id): - with context.session.begin(subtransactions=True): - neutron_port = self.get_port(context, port_id) - interface_mac = neutron_port['mac_address'] - # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx - mac = self.mac_reformat_62to34(interface_mac) - - brocade_port = brocade_db.get_port(context, port_id) - vlan_id = brocade_port['vlan_id'] - - switch = self._switch - try: - self._driver.dissociate_mac_from_network(switch['address'], - switch['username'], - switch['password'], - vlan_id, - mac) - except Exception: - LOG.exception(_("Brocade NOS driver error")) - raise Exception( - _("Brocade plugin raised exception, check logs")) - - super(BrocadePluginV2, self).delete_port(context, port_id) - brocade_db.delete_port(context, port_id) - - def update_port(self, context, port_id, port): - original_port = self.get_port(context, port_id) - session = context.session - port_updated = False - with session.begin(subtransactions=True): - # delete the port binding and read it with the new rules - if ext_sg.SECURITYGROUPS in port['port']: - port['port'][ext_sg.SECURITYGROUPS] = ( - self._get_security_groups_on_port(context, port)) - self._delete_port_security_group_bindings(context, port_id) - # process_port_create_security_group also needs port id - port['port']['id'] = port_id - self._process_port_create_security_group( - context, - port['port'], - port['port'][ext_sg.SECURITYGROUPS]) - port_updated = True - port_data = port['port'] - port = super(BrocadePluginV2, self).update_port( - context, port_id, port) - self._process_portbindings_create_and_update(context, - port_data, - port) - if original_port['admin_state_up'] != port['admin_state_up']: - port_updated = True - - if (original_port['fixed_ips'] != port['fixed_ips'] or - not utils.compare_elements( - original_port.get(ext_sg.SECURITYGROUPS), - port.get(ext_sg.SECURITYGROUPS))): - self.notifier.security_groups_member_updated( - context, port.get(ext_sg.SECURITYGROUPS)) - - if port_updated: - self._notify_port_updated(context, port) - - return port - - def _notify_port_updated(self, context, port): - port_id = port['id'] - bport = brocade_db.get_port(context, port_id) - self.notifier.port_update(context, port, - bport.physical_interface, - bport.vlan_id) - - def _get_base_binding_dict(self): - binding = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_BRIDGE, - portbindings.VIF_DETAILS: { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases}} - return binding - - def get_plugin_version(self): - """Get version number of the plugin.""" - return PLUGIN_VERSION - - @staticmethod - def mac_reformat_62to34(interface_mac): - """Transform MAC address format. - - Transforms from 6 groups of 2 hexadecimal numbers delimited by ":" - to 3 groups of 4 hexadecimals numbers delimited by ".". - - :param interface_mac: MAC address in the format xx:xx:xx:xx:xx:xx - :type interface_mac: string - :returns: MAC address in the format xxxx.xxxx.xxxx - :rtype: string - """ - - mac = interface_mac.replace(":", "") - mac = mac[0:4] + "." + mac[4:8] + "." + mac[8:12] - return mac diff --git a/neutron/plugins/brocade/README.md b/neutron/plugins/brocade/README.md deleted file mode 100644 index 82b3ad89d..000000000 --- a/neutron/plugins/brocade/README.md +++ /dev/null @@ -1,112 +0,0 @@ -Brocade Openstack Neutron Plugin -================================ - -* up-to-date version of these instructions are located at: - http://wiki.openstack.org/brocade-neutron-plugin - -* N.B.: Please see Prerequisites section regarding ncclient (netconf client library) - -* Supports VCS (Virtual Cluster of Switches) - - -Openstack Brocade Neutron Plugin implements the Neutron v2.0 API. - -This plugin is meant to orchestrate Brocade VCS switches running NOS, examples of these are: - - 1. VDX 67xx series of switches - 2. VDX 87xx series of switches - -Brocade Neutron plugin implements the Neutron v2.0 API. It uses NETCONF at the backend -to configure the Brocade switch. - - +------------+ +------------+ +-------------+ - | | | | | | - | | | | | Brocade | - | Openstack | v2.0 | Brocade | NETCONF | VCS Switch | - | Neutron +--------+ Neutron +----------+ | - | | | Plugin | | VDX 67xx | - | | | | | VDX 87xx | - | | | | | | - | | | | | | - +------------+ +------------+ +-------------+ - - -Directory Structure -=================== - -Normally you will have your Openstack directory structure as follows: - - /opt/stack/nova/ - /opt/stack/horizon/ - ... - /opt/stack/neutron/neutron/plugins/ - -Within this structure, Brocade plugin resides at: - - /opt/stack/neutron/neutron/plugins/brocade - - -Prerequsites -============ - -This plugin requires installation of the python netconf client (ncclient) library: - -ncclient v0.3.1 - Python library for NETCONF clients available at http://github.com/brocade/ncclient - - % git clone https://www.github.com/brocade/ncclient - % cd ncclient; sudo python ./setup.py install - - -Configuration -============= - -1. Specify to Neutron that you will be using the Brocade Plugin - this is done -by setting the parameter core_plugin in Neutron: - - core_plugin = neutron.plugins.brocade.NeutronPlugin.BrocadePluginV2 - -2. Physical switch configuration parameters and Brocade specific database configuration is specified in -the configuration file specified in the brocade.ini files: - - % cat /etc/neutron/plugins/brocade/brocade.ini - [SWITCH] - username = admin - password = password - address = - ostype = NOS - - [database] - connection = mysql://root:pass@localhost/brocade_neutron?charset=utf8 - - (please see list of more configuration parameters in the brocade.ini file) - -Running Setup.py -================ - -Running setup.py with appropriate permissions will copy the default configuration -file to /etc/neutron/plugins/brocade/brocade.ini. This file MUST be edited to -suit your setup/environment. - - % cd /opt/stack/neutron/neutron/plugins/brocade - % python setup.py - - -Devstack -======== - -Please see special notes for devstack at: -http://wiki.openstack.org/brocade-neutron-plugin - -In order to use Brocade Neutron Plugin, add the following lines in localrc, if localrc file doe - not exist create one: - -ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,cinder,c-sch,c-api,c-vol,n-sch,n-novnc,n-xvnc,n-cauth,horizon,rabbit,neutron,q-svc,q-agt -Q_PLUGIN=brocade - -As part of running devstack/stack.sh, the configuration files is copied as: - - % cp /opt/stack/neutron/etc/neutron/plugins/brocade/brocade.ini /etc/neutron/plugins/brocade/brocade.ini - -(hence it is important to make any changes to the configuration in: -/opt/stack/neutron/etc/neutron/plugins/brocade/brocade.ini) - diff --git a/neutron/plugins/brocade/__init__.py b/neutron/plugins/brocade/__init__.py deleted file mode 100644 index c22f863e3..000000000 --- a/neutron/plugins/brocade/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Brocade Communications System, 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. diff --git a/neutron/plugins/brocade/db/__init__.py b/neutron/plugins/brocade/db/__init__.py deleted file mode 100644 index c22f863e3..000000000 --- a/neutron/plugins/brocade/db/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Brocade Communications System, 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. diff --git a/neutron/plugins/brocade/db/models.py b/neutron/plugins/brocade/db/models.py deleted file mode 100644 index d9b3663a1..000000000 --- a/neutron/plugins/brocade/db/models.py +++ /dev/null @@ -1,151 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Brocade Communications System, 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. -# -# Authors: -# Shiv Haris (sharis@brocade.com) -# Varma Bhupatiraju (vbhupati@#brocade.com) - - -"""Brocade specific database schema/model.""" - -import sqlalchemy as sa - -from neutron.db import model_base -from neutron.db import models_v2 - - -class BrocadeNetwork(model_base.BASEV2, models_v2.HasId): - """Schema for brocade network.""" - - vlan = sa.Column(sa.String(10)) - - -class BrocadePort(model_base.BASEV2): - """Schema for brocade port.""" - - port_id = sa.Column(sa.String(36), primary_key=True, default="") - network_id = sa.Column(sa.String(36), - sa.ForeignKey("brocadenetworks.id"), - nullable=False) - admin_state_up = sa.Column(sa.Boolean, nullable=False) - physical_interface = sa.Column(sa.String(36)) - vlan_id = sa.Column(sa.String(36)) - tenant_id = sa.Column(sa.String(36)) - - -def create_network(context, net_id, vlan): - """Create a brocade specific network/port-profiles.""" - - session = context.session - with session.begin(subtransactions=True): - net = BrocadeNetwork(id=net_id, vlan=vlan) - session.add(net) - - return net - - -def delete_network(context, net_id): - """Delete a brocade specific network/port-profiles.""" - - session = context.session - with session.begin(subtransactions=True): - net = (session.query(BrocadeNetwork).filter_by(id=net_id).first()) - if net is not None: - session.delete(net) - - -def get_network(context, net_id, fields=None): - """Get brocade specific network, with vlan extension.""" - - session = context.session - return (session.query(BrocadeNetwork).filter_by(id=net_id).first()) - - -def get_networks(context, filters=None, fields=None): - """Get all brocade specific networks.""" - - session = context.session - try: - nets = session.query(BrocadeNetwork).all() - return nets - except sa.exc.SQLAlchemyError: - return None - - -def create_port(context, port_id, network_id, physical_interface, - vlan_id, tenant_id, admin_state_up): - """Create a brocade specific port, has policy like vlan.""" - - # port_id is truncated: since the linux-bridge tap device names are - # based on truncated port id, this enables port lookups using - # tap devices - port_id = port_id[0:11] - session = context.session - with session.begin(subtransactions=True): - port = BrocadePort(port_id=port_id, - network_id=network_id, - physical_interface=physical_interface, - vlan_id=vlan_id, - admin_state_up=admin_state_up, - tenant_id=tenant_id) - session.add(port) - return port - - -def get_port(context, port_id): - """get a brocade specific port.""" - - port_id = port_id[0:11] - session = context.session - port = (session.query(BrocadePort).filter_by(port_id=port_id).first()) - return port - - -def get_ports(context, network_id=None): - """get a brocade specific port.""" - - session = context.session - ports = (session.query(BrocadePort).filter_by(network_id=network_id).all()) - return ports - - -def delete_port(context, port_id): - """delete brocade specific port.""" - - port_id = port_id[0:11] - session = context.session - with session.begin(subtransactions=True): - port = (session.query(BrocadePort).filter_by(port_id=port_id).first()) - if port is not None: - session.delete(port) - - -def get_port_from_device(session, port_id): - """get port from the tap device.""" - - # device is same as truncated port_id - port = (session.query(BrocadePort).filter_by(port_id=port_id).first()) - return port - - -def update_port_state(context, port_id, admin_state_up): - """Update port attributes.""" - - port_id = port_id[0:11] - session = context.session - session.query(BrocadePort).filter_by( - port_id=port_id).update({'admin_state_up': admin_state_up}) diff --git a/neutron/plugins/brocade/nos/__init__.py b/neutron/plugins/brocade/nos/__init__.py deleted file mode 100644 index 9d4562b0d..000000000 --- a/neutron/plugins/brocade/nos/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2013 Brocade Communications 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. diff --git a/neutron/plugins/brocade/nos/fake_nosdriver.py b/neutron/plugins/brocade/nos/fake_nosdriver.py deleted file mode 100644 index 8984768d5..000000000 --- a/neutron/plugins/brocade/nos/fake_nosdriver.py +++ /dev/null @@ -1,117 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Brocade Communications System, 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. -# -# Authors: -# Varma Bhupatiraju (vbhupati@#brocade.com) -# Shiv Haris (sharis@brocade.com) - - -"""FAKE DRIVER, for unit tests purposes. - -Brocade NOS Driver implements NETCONF over SSHv2 for -Neutron network life-cycle management. -""" - - -class NOSdriver(): - """NOS NETCONF interface driver for Neutron network. - - Fake: Handles life-cycle management of Neutron network, - leverages AMPP on NOS - (for use by unit tests, avoids touching any hardware) - """ - - def __init__(self): - pass - - def connect(self, host, username, password): - """Connect via SSH and initialize the NETCONF session.""" - pass - - def create_network(self, host, username, password, net_id): - """Creates a new virtual network.""" - pass - - def delete_network(self, host, username, password, net_id): - """Deletes a virtual network.""" - pass - - def associate_mac_to_network(self, host, username, password, - net_id, mac): - """Associates a MAC address to virtual network.""" - pass - - def dissociate_mac_from_network(self, host, username, password, - net_id, mac): - """Dissociates a MAC address from virtual network.""" - pass - - def create_vlan_interface(self, mgr, vlan_id): - """Configures a VLAN interface.""" - pass - - def delete_vlan_interface(self, mgr, vlan_id): - """Deletes a VLAN interface.""" - pass - - def get_port_profiles(self, mgr): - """Retrieves all port profiles.""" - pass - - def get_port_profile(self, mgr, name): - """Retrieves a port profile.""" - pass - - def create_port_profile(self, mgr, name): - """Creates a port profile.""" - pass - - def delete_port_profile(self, mgr, name): - """Deletes a port profile.""" - pass - - def activate_port_profile(self, mgr, name): - """Activates a port profile.""" - pass - - def deactivate_port_profile(self, mgr, name): - """Deactivates a port profile.""" - pass - - def associate_mac_to_port_profile(self, mgr, name, mac_address): - """Associates a MAC address to a port profile.""" - pass - - def dissociate_mac_from_port_profile(self, mgr, name, mac_address): - """Dissociates a MAC address from a port profile.""" - pass - - def create_vlan_profile_for_port_profile(self, mgr, name): - """Creates VLAN sub-profile for port profile.""" - pass - - def configure_l2_mode_for_vlan_profile(self, mgr, name): - """Configures L2 mode for VLAN sub-profile.""" - pass - - def configure_trunk_mode_for_vlan_profile(self, mgr, name): - """Configures trunk mode for VLAN sub-profile.""" - pass - - def configure_allowed_vlans_for_vlan_profile(self, mgr, name, vlan_id): - """Configures allowed VLANs for VLAN sub-profile.""" - pass diff --git a/neutron/plugins/brocade/nos/nctemplates.py b/neutron/plugins/brocade/nos/nctemplates.py deleted file mode 100644 index 48071dbcd..000000000 --- a/neutron/plugins/brocade/nos/nctemplates.py +++ /dev/null @@ -1,204 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2013 Brocade Communications 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. -# -# Authors: -# Varma Bhupatiraju (vbhupati@#brocade.com) -# Shiv Haris (sharis@brocade.com) - - -"""NOS NETCONF XML Configuration Command Templates. - -Interface Configuration Commands -""" - -# Create VLAN (vlan_id) -CREATE_VLAN_INTERFACE = """ - - - - - {vlan_id} - - - - -""" - -# Delete VLAN (vlan_id) -DELETE_VLAN_INTERFACE = """ - - - - - {vlan_id} - - - - -""" - -# -# AMPP Life-cycle Management Configuration Commands -# - -# Create AMPP port-profile (port_profile_name) -CREATE_PORT_PROFILE = """ - - - {name} - - -""" - -# Create VLAN sub-profile for port-profile (port_profile_name) -CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """ - - - {name} - - - -""" - -# Configure L2 mode for VLAN sub-profile (port_profile_name) -CONFIGURE_L2_MODE_FOR_VLAN_PROFILE = """ - - - {name} - - - - - -""" - -# Configure trunk mode for VLAN sub-profile (port_profile_name) -CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE = """ - - - {name} - - - - trunk - - - - - -""" - -# Configure allowed VLANs for VLAN sub-profile -# (port_profile_name, allowed_vlan, native_vlan) -CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE = """ - - - {name} - - - - - - {vlan_id} - - - - - - - -""" - -# Delete port-profile (port_profile_name) -DELETE_PORT_PROFILE = """ - - - {name} - - -""" - -# Activate port-profile (port_profile_name) -ACTIVATE_PORT_PROFILE = """ - - - - {name} - - - - -""" - -# Deactivate port-profile (port_profile_name) -DEACTIVATE_PORT_PROFILE = """ - - - - {name} - - - - -""" - -# Associate MAC address to port-profile (port_profile_name, mac_address) -ASSOCIATE_MAC_TO_PORT_PROFILE = """ - - - - {name} - - {mac_address} - - - - -""" - -# Dissociate MAC address from port-profile (port_profile_name, mac_address) -DISSOCIATE_MAC_FROM_PORT_PROFILE = """ - - - - {name} - - {mac_address} - - - - -""" - -# -# Custom RPC Commands -# - - -# -# Constants -# - -# Port profile naming convention for Neutron networks -OS_PORT_PROFILE_NAME = "openstack-profile-{id}" - -# Port profile filter expressions -PORT_PROFILE_XPATH_FILTER = "/port-profile" -PORT_PROFILE_NAME_XPATH_FILTER = "/port-profile[name='{name}']" diff --git a/neutron/plugins/brocade/nos/nosdriver.py b/neutron/plugins/brocade/nos/nosdriver.py deleted file mode 100644 index ce4c86110..000000000 --- a/neutron/plugins/brocade/nos/nosdriver.py +++ /dev/null @@ -1,233 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Brocade Communications System, 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. -# -# Authors: -# Varma Bhupatiraju (vbhupati@#brocade.com) -# Shiv Haris (sharis@brocade.com) - - -"""Brocade NOS Driver implements NETCONF over SSHv2 for -Neutron network life-cycle management. -""" - -from ncclient import manager - -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.brocade.nos import nctemplates as template - - -LOG = logging.getLogger(__name__) -SSH_PORT = 22 - - -def nos_unknown_host_cb(host, fingerprint): - """An unknown host callback. - - Returns `True` if it finds the key acceptable, - and `False` if not. This default callback for NOS always returns 'True' - (i.e. trusts all hosts for now). - """ - return True - - -class NOSdriver(): - """NOS NETCONF interface driver for Neutron network. - - Handles life-cycle management of Neutron network (leverages AMPP on NOS) - """ - - def __init__(self): - self.mgr = None - - def connect(self, host, username, password): - """Connect via SSH and initialize the NETCONF session.""" - - # Use the persisted NETCONF connection - if self.mgr and self.mgr.connected: - return self.mgr - - # Open new NETCONF connection - try: - self.mgr = manager.connect(host=host, port=SSH_PORT, - username=username, password=password, - unknown_host_cb=nos_unknown_host_cb) - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_("Connect failed to switch: %s"), e) - - LOG.debug(_("Connect success to host %(host)s:%(ssh_port)d"), - dict(host=host, ssh_port=SSH_PORT)) - return self.mgr - - def close_session(self): - """Close NETCONF session.""" - if self.mgr: - self.mgr.close_session() - self.mgr = None - - def create_network(self, host, username, password, net_id): - """Creates a new virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.create_vlan_interface(mgr, net_id) - self.create_port_profile(mgr, name) - self.create_vlan_profile_for_port_profile(mgr, name) - self.configure_l2_mode_for_vlan_profile(mgr, name) - self.configure_trunk_mode_for_vlan_profile(mgr, name) - self.configure_allowed_vlans_for_vlan_profile(mgr, name, net_id) - self.activate_port_profile(mgr, name) - except Exception as ex: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error: %s"), ex) - self.close_session() - - def delete_network(self, host, username, password, net_id): - """Deletes a virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.deactivate_port_profile(mgr, name) - self.delete_port_profile(mgr, name) - self.delete_vlan_interface(mgr, net_id) - except Exception as ex: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error: %s"), ex) - self.close_session() - - def associate_mac_to_network(self, host, username, password, - net_id, mac): - """Associates a MAC address to virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.associate_mac_to_port_profile(mgr, name, mac) - except Exception as ex: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error: %s"), ex) - self.close_session() - - def dissociate_mac_from_network(self, host, username, password, - net_id, mac): - """Dissociates a MAC address from virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.dissociate_mac_from_port_profile(mgr, name, mac) - except Exception as ex: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error: %s"), ex) - self.close_session() - - def create_vlan_interface(self, mgr, vlan_id): - """Configures a VLAN interface.""" - - confstr = template.CREATE_VLAN_INTERFACE.format(vlan_id=vlan_id) - mgr.edit_config(target='running', config=confstr) - - def delete_vlan_interface(self, mgr, vlan_id): - """Deletes a VLAN interface.""" - - confstr = template.DELETE_VLAN_INTERFACE.format(vlan_id=vlan_id) - mgr.edit_config(target='running', config=confstr) - - def get_port_profiles(self, mgr): - """Retrieves all port profiles.""" - - filterstr = template.PORT_PROFILE_XPATH_FILTER - response = mgr.get_config(source='running', - filter=('xpath', filterstr)).data_xml - return response - - def get_port_profile(self, mgr, name): - """Retrieves a port profile.""" - - filterstr = template.PORT_PROFILE_NAME_XPATH_FILTER.format(name=name) - response = mgr.get_config(source='running', - filter=('xpath', filterstr)).data_xml - return response - - def create_port_profile(self, mgr, name): - """Creates a port profile.""" - - confstr = template.CREATE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def delete_port_profile(self, mgr, name): - """Deletes a port profile.""" - - confstr = template.DELETE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def activate_port_profile(self, mgr, name): - """Activates a port profile.""" - - confstr = template.ACTIVATE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def deactivate_port_profile(self, mgr, name): - """Deactivates a port profile.""" - - confstr = template.DEACTIVATE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def associate_mac_to_port_profile(self, mgr, name, mac_address): - """Associates a MAC address to a port profile.""" - - confstr = template.ASSOCIATE_MAC_TO_PORT_PROFILE.format( - name=name, mac_address=mac_address) - mgr.edit_config(target='running', config=confstr) - - def dissociate_mac_from_port_profile(self, mgr, name, mac_address): - """Dissociates a MAC address from a port profile.""" - - confstr = template.DISSOCIATE_MAC_FROM_PORT_PROFILE.format( - name=name, mac_address=mac_address) - mgr.edit_config(target='running', config=confstr) - - def create_vlan_profile_for_port_profile(self, mgr, name): - """Creates VLAN sub-profile for port profile.""" - - confstr = template.CREATE_VLAN_PROFILE_FOR_PORT_PROFILE.format( - name=name) - mgr.edit_config(target='running', config=confstr) - - def configure_l2_mode_for_vlan_profile(self, mgr, name): - """Configures L2 mode for VLAN sub-profile.""" - - confstr = template.CONFIGURE_L2_MODE_FOR_VLAN_PROFILE.format( - name=name) - mgr.edit_config(target='running', config=confstr) - - def configure_trunk_mode_for_vlan_profile(self, mgr, name): - """Configures trunk mode for VLAN sub-profile.""" - - confstr = template.CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE.format( - name=name) - mgr.edit_config(target='running', config=confstr) - - def configure_allowed_vlans_for_vlan_profile(self, mgr, name, vlan_id): - """Configures allowed VLANs for VLAN sub-profile.""" - - confstr = template.CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE.format( - name=name, vlan_id=vlan_id) - mgr.edit_config(target='running', config=confstr) diff --git a/neutron/plugins/brocade/tests/README b/neutron/plugins/brocade/tests/README deleted file mode 100644 index 476ca0535..000000000 --- a/neutron/plugins/brocade/tests/README +++ /dev/null @@ -1,24 +0,0 @@ -Start the neutron-server with IP address of switch configured in brocade.ini: -(for configuration instruction please see README.md in the above directory) - -nostest.py: -This tests two things: - 1. Creates port-profile on the physical switch when a neutron 'network' is created - 2. Associates the MAC address with the created port-profile - -noscli.py: - CLI interface to create/delete/associate MAC/dissociate MAC - Commands: - % noscli.py create - (after running check that PP is created on the switch) - - % noscli.py delete - (after running check that PP is deleted from the switch) - - % noscli.py associate - (after running check that MAC is associated with PP) - - % noscli.py dissociate - (after running check that MAC is dissociated from the PP) - - diff --git a/neutron/plugins/brocade/tests/noscli.py b/neutron/plugins/brocade/tests/noscli.py deleted file mode 100644 index 81e988e3c..000000000 --- a/neutron/plugins/brocade/tests/noscli.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2013 Brocade Communications 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. -# -# Authors: -# Varma Bhupatiraju (vbhupati@#brocade.com) -# Shiv Haris (sharis@brocade.com) - - -"""Brocade NOS Driver CLI.""" -from __future__ import print_function - -import argparse - -from neutron.openstack.common import log as logging -from neutron.plugins.brocade.nos import nosdriver as nos - -LOG = logging.getLogger(__name__) - - -class NOSCli(object): - - def __init__(self, host, username, password): - self.host = host - self.username = username - self.password = password - self.driver = nos.NOSdriver() - - def execute(self, cmd): - numargs = len(args.otherargs) - - if args.cmd == 'create' and numargs == 1: - self._create(args.otherargs[0]) - elif args.cmd == 'delete' and numargs == 1: - self._delete(args.otherargs[0]) - elif args.cmd == 'associate' and numargs == 2: - self._associate(args.otherargs[0], args.otherargs[1]) - elif args.cmd == 'dissociate' and numargs == 2: - self._dissociate(args.otherargs[0], args.otherargs[1]) - else: - print(usage_desc) - exit(0) - - def _create(self, net_id): - self.driver.create_network(self.host, self.username, self.password, - net_id) - - def _delete(self, net_id): - self.driver.delete_network(self.host, self.username, self.password, - net_id) - - def _associate(self, net_id, mac): - self.driver.associate_mac_to_network( - self.host, self.username, self.password, net_id, mac) - - def _dissociate(self, net_id, mac): - self.driver.dissociate_mac_from_network( - self.host, self.username, self.password, net_id, mac) - - -usage_desc = """ -Command descriptions: - - create - delete - associate - dissociate -""" - -parser = argparse.ArgumentParser(description='process args', - usage=usage_desc, epilog='foo bar help') -parser.add_argument('--ip', default='localhost') -parser.add_argument('--username', default='admin') -parser.add_argument('--password', default='password') -parser.add_argument('cmd') -parser.add_argument('otherargs', nargs='*') -args = parser.parse_args() - -noscli = NOSCli(args.ip, args.username, args.password) -noscli.execute(args.cmd) diff --git a/neutron/plugins/brocade/tests/nostest.py b/neutron/plugins/brocade/tests/nostest.py deleted file mode 100644 index 72a21ae8b..000000000 --- a/neutron/plugins/brocade/tests/nostest.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2013 Brocade Communications 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. -# -# Authors: -# Varma Bhupatiraju (vbhupati@#brocade.com) -# Shiv Haris (sharis@brocade.com) - - -"""Brocade NOS Driver Test.""" -from __future__ import print_function - -import sys - -from neutron.plugins.brocade.nos import nosdriver as nos - - -def nostest(host, username, password): - # Driver - driver = nos.NOSdriver() - - # Neutron operations - vlan = 1001 - mac = '0050.56bf.0001' - driver.create_network(host, username, password, vlan) - driver.associate_mac_to_network(host, username, password, vlan, mac) - driver.dissociate_mac_from_network(host, username, password, vlan, mac) - driver.delete_network(host, username, password, vlan) - - # AMPP enumeration - with driver.connect(host, username, password) as mgr: - print(driver.get_port_profiles(mgr)) - print(driver.get_port_profile(mgr, 'default')) - - -if __name__ == '__main__': - nostest(sys.argv[1], sys.argv[2], sys.argv[3]) diff --git a/neutron/plugins/brocade/vlanbm.py b/neutron/plugins/brocade/vlanbm.py deleted file mode 100644 index 3c4b3ccb6..000000000 --- a/neutron/plugins/brocade/vlanbm.py +++ /dev/null @@ -1,60 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Brocade Communications System, 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. -# -# Authors: -# Shiv Haris (sharis@brocade.com) -# Varma Bhupatiraju (vbhupati@#brocade.com) - - -"""A Vlan Bitmap class to handle allocation/de-allocation of vlan ids.""" -from six import moves - -from neutron.common import constants -from neutron.plugins.brocade.db import models as brocade_db - - -MIN_VLAN = constants.MIN_VLAN_TAG + 1 -MAX_VLAN = constants.MAX_VLAN_TAG - - -class VlanBitmap(object): - """Setup a vlan bitmap for allocation/de-allocation.""" - - # Keep track of the vlans that have been allocated/de-allocated - # uses a bitmap to do this - - def __init__(self, ctxt): - """Initialize the vlan as a set.""" - self.vlans = set(int(net['vlan']) - for net in brocade_db.get_networks(ctxt) - if net['vlan'] - ) - - def get_next_vlan(self, vlan_id=None): - """Try to get a specific vlan if requested or get the next vlan.""" - min_vlan_search = vlan_id or MIN_VLAN - max_vlan_search = (vlan_id and vlan_id + 1) or MAX_VLAN - - for vlan in moves.xrange(min_vlan_search, max_vlan_search): - if vlan not in self.vlans: - self.vlans.add(vlan) - return vlan - - def release_vlan(self, vlan_id): - """Return the vlan to the pool.""" - if vlan_id in self.vlans: - self.vlans.remove(vlan_id) diff --git a/neutron/plugins/cisco/README b/neutron/plugins/cisco/README deleted file mode 100644 index 2bedb75b1..000000000 --- a/neutron/plugins/cisco/README +++ /dev/null @@ -1,7 +0,0 @@ -Cisco Neutron Virtual Network Plugin - -This plugin implements Neutron v2 APIs and helps configure -topologies consisting of virtual and physical switches. - -For more details on use please refer to: -http://wiki.openstack.org/cisco-neutron diff --git a/neutron/plugins/cisco/__init__.py b/neutron/plugins/cisco/__init__.py deleted file mode 100644 index db695fb0a..000000000 --- a/neutron/plugins/cisco/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# diff --git a/neutron/plugins/cisco/common/__init__.py b/neutron/plugins/cisco/common/__init__.py deleted file mode 100644 index 833357b73..000000000 --- a/neutron/plugins/cisco/common/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. diff --git a/neutron/plugins/cisco/common/cisco_constants.py b/neutron/plugins/cisco/common/cisco_constants.py deleted file mode 100644 index 2f1992108..000000000 --- a/neutron/plugins/cisco/common/cisco_constants.py +++ /dev/null @@ -1,111 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. - - -# Attachment attributes -INSTANCE_ID = 'instance_id' -TENANT_ID = 'tenant_id' -TENANT_NAME = 'tenant_name' -HOST_NAME = 'host_name' - -# Network attributes -NET_ID = 'id' -NET_NAME = 'name' -NET_VLAN_ID = 'vlan_id' -NET_VLAN_NAME = 'vlan_name' -NET_PORTS = 'ports' - -CREDENTIAL_ID = 'credential_id' -CREDENTIAL_NAME = 'credential_name' -CREDENTIAL_USERNAME = 'user_name' -CREDENTIAL_PASSWORD = 'password' -CREDENTIAL_TYPE = 'type' -MASKED_PASSWORD = '********' - -USERNAME = 'username' -PASSWORD = 'password' - -LOGGER_COMPONENT_NAME = "cisco_plugin" - -NEXUS_PLUGIN = 'nexus_plugin' -VSWITCH_PLUGIN = 'vswitch_plugin' - -DEVICE_IP = 'device_ip' - -NETWORK_ADMIN = 'network_admin' - -NETWORK = 'network' -PORT = 'port' -BASE_PLUGIN_REF = 'base_plugin_ref' -CONTEXT = 'context' -SUBNET = 'subnet' - -#### N1Kv CONSTANTS -# Special vlan_id value in n1kv_vlan_allocations table indicating flat network -FLAT_VLAN_ID = -1 - -# Topic for tunnel notifications between the plugin and agent -TUNNEL = 'tunnel' - -# Maximum VXLAN range configurable for one network profile. -MAX_VXLAN_RANGE = 1000000 - -# Values for network_type -NETWORK_TYPE_FLAT = 'flat' -NETWORK_TYPE_VLAN = 'vlan' -NETWORK_TYPE_VXLAN = 'vxlan' -NETWORK_TYPE_LOCAL = 'local' -NETWORK_TYPE_NONE = 'none' -NETWORK_TYPE_TRUNK = 'trunk' -NETWORK_TYPE_MULTI_SEGMENT = 'multi-segment' - -# Values for network sub_type -NETWORK_TYPE_OVERLAY = 'overlay' -NETWORK_SUBTYPE_NATIVE_VXLAN = 'native_vxlan' -NETWORK_SUBTYPE_TRUNK_VLAN = NETWORK_TYPE_VLAN -NETWORK_SUBTYPE_TRUNK_VXLAN = NETWORK_TYPE_OVERLAY - -# Prefix for VM Network name -VM_NETWORK_NAME_PREFIX = 'vmn_' - -DEFAULT_HTTP_TIMEOUT = 15 -SET = 'set' -INSTANCE = 'instance' -PROPERTIES = 'properties' -NAME = 'name' -ID = 'id' -POLICY = 'policy' -TENANT_ID_NOT_SET = 'TENANT_ID_NOT_SET' -ENCAPSULATIONS = 'encapsulations' -STATE = 'state' -ONLINE = 'online' -MAPPINGS = 'mappings' -MAPPING = 'mapping' -SEGMENTS = 'segments' -SEGMENT = 'segment' -BRIDGE_DOMAIN_SUFFIX = '_bd' -LOGICAL_NETWORK_SUFFIX = '_log_net' -ENCAPSULATION_PROFILE_SUFFIX = '_profile' - -UUID_LENGTH = 36 - -# Nexus vlan and vxlan segment range -NEXUS_VLAN_RESERVED_MIN = 3968 -NEXUS_VLAN_RESERVED_MAX = 4047 -NEXUS_VXLAN_MIN = 4096 -NEXUS_VXLAN_MAX = 16000000 diff --git a/neutron/plugins/cisco/common/cisco_credentials_v2.py b/neutron/plugins/cisco/common/cisco_credentials_v2.py deleted file mode 100644 index 5d8fc8ff5..000000000 --- a/neutron/plugins/cisco/common/cisco_credentials_v2.py +++ /dev/null @@ -1,61 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. - -import logging as LOG - -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_exceptions as cexc -from neutron.plugins.cisco.common import config -from neutron.plugins.cisco.db import network_db_v2 as cdb - -LOG.basicConfig(level=LOG.WARN) -LOG.getLogger(const.LOGGER_COMPONENT_NAME) - - -class Store(object): - """Credential Store.""" - - @staticmethod - def initialize(): - dev_dict = config.get_device_dictionary() - for key in dev_dict: - dev_id, dev_ip, dev_key = key - if dev_key == const.USERNAME: - try: - cdb.add_credential( - dev_ip, - dev_dict[dev_id, dev_ip, const.USERNAME], - dev_dict[dev_id, dev_ip, const.PASSWORD], - dev_id) - except cexc.CredentialAlreadyExists: - # We are quietly ignoring this, since it only happens - # if this class module is loaded more than once, in - # which case, the credentials are already populated - pass - - @staticmethod - def get_username(cred_name): - """Get the username.""" - credential = cdb.get_credential_name(cred_name) - return credential[const.CREDENTIAL_USERNAME] - - @staticmethod - def get_password(cred_name): - """Get the password.""" - credential = cdb.get_credential_name(cred_name) - return credential[const.CREDENTIAL_PASSWORD] diff --git a/neutron/plugins/cisco/common/cisco_exceptions.py b/neutron/plugins/cisco/common/cisco_exceptions.py deleted file mode 100644 index be50e7665..000000000 --- a/neutron/plugins/cisco/common/cisco_exceptions.py +++ /dev/null @@ -1,236 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Rohit Agarwalla, Cisco Systems, Inc. - -"""Exceptions used by the Cisco plugin.""" - -from neutron.common import exceptions - - -class NetworkSegmentIDNotFound(exceptions.NeutronException): - """Segmentation ID for network is not found.""" - message = _("Segmentation ID for network %(net_id)s is not found.") - - -class NoMoreNics(exceptions.NeutronException): - """No more dynamic NICs are available in the system.""" - message = _("Unable to complete operation. No more dynamic NICs are " - "available in the system.") - - -class NetworkVlanBindingAlreadyExists(exceptions.NeutronException): - """Binding cannot be created, since it already exists.""" - message = _("NetworkVlanBinding for %(vlan_id)s and network " - "%(network_id)s already exists.") - - -class VlanIDNotFound(exceptions.NeutronException): - """VLAN ID cannot be found.""" - message = _("Vlan ID %(vlan_id)s not found.") - - -class VlanIDOutsidePool(exceptions.NeutronException): - """VLAN ID cannot be allocated, since it is outside the configured pool.""" - message = _("Unable to complete operation. VLAN ID exists outside of the " - "configured network segment range.") - - -class VlanIDNotAvailable(exceptions.NeutronException): - """No VLAN ID available.""" - message = _("No Vlan ID available.") - - -class QosNotFound(exceptions.NeutronException): - """QoS level with this ID cannot be found.""" - message = _("QoS level %(qos_id)s could not be found " - "for tenant %(tenant_id)s.") - - -class QosNameAlreadyExists(exceptions.NeutronException): - """QoS Name already exists.""" - message = _("QoS level with name %(qos_name)s already exists " - "for tenant %(tenant_id)s.") - - -class CredentialNotFound(exceptions.NeutronException): - """Credential with this ID cannot be found.""" - message = _("Credential %(credential_id)s could not be found.") - - -class CredentialNameNotFound(exceptions.NeutronException): - """Credential Name could not be found.""" - message = _("Credential %(credential_name)s could not be found.") - - -class CredentialAlreadyExists(exceptions.NeutronException): - """Credential already exists.""" - message = _("Credential %(credential_name)s already exists.") - - -class ProviderNetworkExists(exceptions.NeutronException): - """Provider network already exists.""" - message = _("Provider network %s already exists") - - -class NexusComputeHostNotConfigured(exceptions.NeutronException): - """Connection to compute host is not configured.""" - message = _("Connection to %(host)s is not configured.") - - -class NexusConnectFailed(exceptions.NeutronException): - """Failed to connect to Nexus switch.""" - message = _("Unable to connect to Nexus %(nexus_host)s. Reason: %(exc)s.") - - -class NexusConfigFailed(exceptions.NeutronException): - """Failed to configure Nexus switch.""" - message = _("Failed to configure Nexus: %(config)s. Reason: %(exc)s.") - - -class NexusPortBindingNotFound(exceptions.NeutronException): - """NexusPort Binding is not present.""" - message = _("Nexus Port Binding (%(filters)s) is not present.") - - def __init__(self, **kwargs): - filters = ','.join('%s=%s' % i for i in kwargs.items()) - super(NexusPortBindingNotFound, self).__init__(filters=filters) - - -class NoNexusSviSwitch(exceptions.NeutronException): - """No usable nexus switch found.""" - message = _("No usable Nexus switch found to create SVI interface.") - - -class PortVnicBindingAlreadyExists(exceptions.NeutronException): - """PortVnic Binding already exists.""" - message = _("PortVnic Binding %(port_id)s already exists.") - - -class PortVnicNotFound(exceptions.NeutronException): - """PortVnic Binding is not present.""" - message = _("PortVnic Binding %(port_id)s is not present.") - - -class SubnetNotSpecified(exceptions.NeutronException): - """Subnet id not specified.""" - message = _("No subnet_id specified for router gateway.") - - -class SubnetInterfacePresent(exceptions.NeutronException): - """Subnet SVI interface already exists.""" - message = _("Subnet %(subnet_id)s has an interface on %(router_id)s.") - - -class PortIdForNexusSvi(exceptions.NeutronException): - """Port Id specified for Nexus SVI.""" - message = _('Nexus hardware router gateway only uses Subnet Ids.') - - -class InvalidDetach(exceptions.NeutronException): - message = _("Unable to unplug the attachment %(att_id)s from port " - "%(port_id)s for network %(net_id)s. The attachment " - "%(att_id)s does not exist.") - - -class PolicyProfileAlreadyExists(exceptions.NeutronException): - """Policy Profile cannot be created since it already exists.""" - message = _("Policy Profile %(profile_id)s " - "already exists.") - - -class PolicyProfileIdNotFound(exceptions.NotFound): - """Policy Profile with the given UUID cannot be found.""" - message = _("Policy Profile %(profile_id)s could not be found.") - - -class NetworkProfileAlreadyExists(exceptions.NeutronException): - """Network Profile cannot be created since it already exists.""" - message = _("Network Profile %(profile_id)s " - "already exists.") - - -class NetworkProfileNotFound(exceptions.NotFound): - """Network Profile with the given UUID/name cannot be found.""" - message = _("Network Profile %(profile)s could not be found.") - - -class NetworkProfileInUse(exceptions.InUse): - """Network Profile with the given UUID is in use.""" - message = _("One or more network segments belonging to network " - "profile %(profile)s is in use.") - - -class NoMoreNetworkSegments(exceptions.NoNetworkAvailable): - """Network segments exhausted for the given network profile.""" - message = _("No more segments available in network segment pool " - "%(network_profile_name)s.") - - -class VMNetworkNotFound(exceptions.NotFound): - """VM Network with the given name cannot be found.""" - message = _("VM Network %(name)s could not be found.") - - -class VxlanIDInUse(exceptions.InUse): - """VXLAN ID is in use.""" - message = _("Unable to create the network. " - "The VXLAN ID %(vxlan_id)s is in use.") - - -class VxlanIDNotFound(exceptions.NotFound): - """VXLAN ID cannot be found.""" - message = _("Vxlan ID %(vxlan_id)s not found.") - - -class VxlanIDOutsidePool(exceptions.NeutronException): - """VXLAN ID cannot be allocated, as it is outside the configured pool.""" - message = _("Unable to complete operation. VXLAN ID exists outside of the " - "configured network segment range.") - - -class VSMConnectionFailed(exceptions.ServiceUnavailable): - """Connection to VSM failed.""" - message = _("Connection to VSM failed: %(reason)s.") - - -class VSMError(exceptions.NeutronException): - """Error has occurred on the VSM.""" - message = _("Internal VSM Error: %(reason)s.") - - -class NetworkBindingNotFound(exceptions.NotFound): - """Network Binding for network cannot be found.""" - message = _("Network Binding for network %(network_id)s could " - "not be found.") - - -class PortBindingNotFound(exceptions.NotFound): - """Port Binding for port cannot be found.""" - message = _("Port Binding for port %(port_id)s could " - "not be found.") - - -class ProfileTenantBindingNotFound(exceptions.NotFound): - """Profile to Tenant binding for given profile ID cannot be found.""" - message = _("Profile-Tenant binding for profile %(profile_id)s could " - "not be found.") - - -class NoClusterFound(exceptions.NotFound): - """No service cluster found to perform multi-segment bridging.""" - message = _("No service cluster found to perform multi-segment bridging.") diff --git a/neutron/plugins/cisco/common/cisco_faults.py b/neutron/plugins/cisco/common/cisco_faults.py deleted file mode 100644 index 80e787e41..000000000 --- a/neutron/plugins/cisco/common/cisco_faults.py +++ /dev/null @@ -1,138 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Ying Liu, Cisco Systems, Inc. - -import webob.dec - -from neutron import wsgi - - -class Fault(webob.exc.HTTPException): - """Error codes for API faults.""" - - _fault_names = { - 400: "malformedRequest", - 401: "unauthorized", - 451: "CredentialNotFound", - 452: "QoSNotFound", - 453: "NovatenantNotFound", - 454: "MultiportNotFound", - 470: "serviceUnavailable", - 471: "pluginFault" - } - - def __init__(self, exception): - """Create a Fault for the given webob.exc.exception.""" - self.wrapped_exc = exception - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - """Generate a WSGI response. - - Response is generated based on the exception passed to constructor. - """ - # Replace the body with fault details. - code = self.wrapped_exc.status_int - fault_name = self._fault_names.get(code, "neutronServiceFault") - fault_data = { - fault_name: { - 'code': code, - 'message': self.wrapped_exc.explanation}} - # 'code' is an attribute on the fault tag itself - content_type = req.best_match_content_type() - self.wrapped_exc.body = wsgi.Serializer().serialize( - fault_data, content_type) - self.wrapped_exc.content_type = content_type - return self.wrapped_exc - - -class PortNotFound(webob.exc.HTTPClientError): - """PortNotFound exception. - - subclass of :class:`~HTTPClientError` - - This indicates that the server did not find the port specified - in the HTTP request for a given network - - code: 430, title: Port not Found - """ - code = 430 - title = _('Port not Found') - explanation = _('Unable to find a port with the specified identifier.') - - -class CredentialNotFound(webob.exc.HTTPClientError): - """CredentialNotFound exception. - - subclass of :class:`~HTTPClientError` - - This indicates that the server did not find the Credential specified - in the HTTP request - - code: 451, title: Credential not Found - """ - code = 451 - title = _('Credential Not Found') - explanation = _('Unable to find a Credential with' - ' the specified identifier.') - - -class QosNotFound(webob.exc.HTTPClientError): - """QosNotFound exception. - - subclass of :class:`~HTTPClientError` - - This indicates that the server did not find the QoS specified - in the HTTP request - - code: 452, title: QoS not Found - """ - code = 452 - title = _('QoS Not Found') - explanation = _('Unable to find a QoS with' - ' the specified identifier.') - - -class NovatenantNotFound(webob.exc.HTTPClientError): - """NovatenantNotFound exception. - - subclass of :class:`~HTTPClientError` - - This indicates that the server did not find the Novatenant specified - in the HTTP request - - code: 453, title: Nova tenant not Found - """ - code = 453 - title = _('Nova tenant Not Found') - explanation = _('Unable to find a Novatenant with' - ' the specified identifier.') - - -class RequestedStateInvalid(webob.exc.HTTPClientError): - """RequestedStateInvalid exception. - - subclass of :class:`~HTTPClientError` - - This indicates that the server could not update the port state to - to the request value - - code: 431, title: Requested State Invalid - """ - code = 431 - title = _('Requested State Invalid') - explanation = _('Unable to update port state with specified value.') diff --git a/neutron/plugins/cisco/common/config.py b/neutron/plugins/cisco/common/config.py deleted file mode 100644 index f13569cea..000000000 --- a/neutron/plugins/cisco/common/config.py +++ /dev/null @@ -1,151 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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 oslo.config import cfg - -from neutron.agent.common import config - - -cisco_plugins_opts = [ - cfg.StrOpt('vswitch_plugin', - default='neutron.plugins.openvswitch.ovs_neutron_plugin.' - 'OVSNeutronPluginV2', - help=_("Virtual Switch to use")), - cfg.StrOpt('nexus_plugin', - default='neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.' - 'NexusPlugin', - help=_("Nexus Switch to use")), -] - -cisco_opts = [ - cfg.StrOpt('vlan_name_prefix', default='q-', - help=_("VLAN Name prefix")), - cfg.StrOpt('provider_vlan_name_prefix', default='p-', - help=_("VLAN Name prefix for provider vlans")), - cfg.BoolOpt('provider_vlan_auto_create', default=True, - help=_('Provider VLANs are automatically created as needed ' - 'on the Nexus switch')), - cfg.BoolOpt('provider_vlan_auto_trunk', default=True, - help=_('Provider VLANs are automatically trunked as needed ' - 'on the ports of the Nexus switch')), - cfg.BoolOpt('nexus_l3_enable', default=False, - help=_("Enable L3 support on the Nexus switches")), - cfg.BoolOpt('svi_round_robin', default=False, - help=_("Distribute SVI interfaces over all switches")), - cfg.StrOpt('model_class', - default='neutron.plugins.cisco.models.virt_phy_sw_v2.' - 'VirtualPhysicalSwitchModelV2', - help=_("Model Class")), - cfg.StrOpt('nexus_driver', - default='neutron.plugins.cisco.test.nexus.' - 'fake_nexus_driver.CiscoNEXUSFakeDriver', - help=_("Nexus Driver Name")), -] - -cisco_n1k_opts = [ - cfg.StrOpt('integration_bridge', default='br-int', - help=_("N1K Integration Bridge")), - cfg.BoolOpt('enable_tunneling', default=True, - help=_("N1K Enable Tunneling")), - cfg.StrOpt('tunnel_bridge', default='br-tun', - help=_("N1K Tunnel Bridge")), - cfg.StrOpt('local_ip', default='10.0.0.3', - help=_("N1K Local IP")), - cfg.StrOpt('tenant_network_type', default='local', - help=_("N1K Tenant Network Type")), - cfg.StrOpt('bridge_mappings', default='', - help=_("N1K Bridge Mappings")), - cfg.StrOpt('vxlan_id_ranges', default='5000:10000', - help=_("N1K VXLAN ID Ranges")), - cfg.StrOpt('network_vlan_ranges', default='vlan:1:4095', - help=_("N1K Network VLAN Ranges")), - cfg.StrOpt('default_network_profile', default='default_network_profile', - help=_("N1K default network profile")), - cfg.StrOpt('default_policy_profile', default='service_profile', - help=_("N1K default policy profile")), - cfg.StrOpt('network_node_policy_profile', default='dhcp_pp', - help=_("N1K policy profile for network node")), - cfg.IntOpt('poll_duration', default=10, - help=_("N1K Policy profile polling duration in seconds")), - cfg.IntOpt('http_pool_size', default=4, - help=_("Number of threads to use to make HTTP requests")), -] - -cfg.CONF.register_opts(cisco_opts, "CISCO") -cfg.CONF.register_opts(cisco_n1k_opts, "CISCO_N1K") -cfg.CONF.register_opts(cisco_plugins_opts, "CISCO_PLUGINS") -config.register_root_helper(cfg.CONF) - -# shortcuts -CONF = cfg.CONF -CISCO = cfg.CONF.CISCO -CISCO_N1K = cfg.CONF.CISCO_N1K -CISCO_PLUGINS = cfg.CONF.CISCO_PLUGINS - -# -# device_dictionary - Contains all external device configuration. -# -# When populated the device dictionary format is: -# {('', '', ''): '', ...} -# -# Example: -# {('NEXUS_SWITCH', '1.1.1.1', 'username'): 'admin', -# ('NEXUS_SWITCH', '1.1.1.1', 'password'): 'mySecretPassword', -# ('NEXUS_SWITCH', '1.1.1.1', 'compute1'): '1/1', ...} -# -device_dictionary = {} - -# -# first_device_ip - IP address of first switch discovered in config -# -# Used for SVI placement when round-robin placement is disabled -# -first_device_ip = None - - -class CiscoConfigOptions(): - """Cisco Configuration Options Class.""" - - def __init__(self): - self._create_device_dictionary() - - def _create_device_dictionary(self): - """ - Create the device dictionary from the cisco_plugins.ini - device supported sections. Ex. NEXUS_SWITCH, N1KV. - """ - - global first_device_ip - - multi_parser = cfg.MultiConfigParser() - read_ok = multi_parser.read(CONF.config_file) - - if len(read_ok) != len(CONF.config_file): - raise cfg.Error(_("Some config files were not parsed properly")) - - first_device_ip = None - for parsed_file in multi_parser.parsed: - for parsed_item in parsed_file.keys(): - dev_id, sep, dev_ip = parsed_item.partition(':') - if dev_id.lower() in ['nexus_switch', 'n1kv']: - for dev_key, value in parsed_file[parsed_item].items(): - if dev_ip and not first_device_ip: - first_device_ip = dev_ip - device_dictionary[dev_id, dev_ip, dev_key] = value[0] - - -def get_device_dictionary(): - return device_dictionary diff --git a/neutron/plugins/cisco/db/__init__.py b/neutron/plugins/cisco/db/__init__.py deleted file mode 100644 index db695fb0a..000000000 --- a/neutron/plugins/cisco/db/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# diff --git a/neutron/plugins/cisco/db/n1kv_db_v2.py b/neutron/plugins/cisco/db/n1kv_db_v2.py deleted file mode 100644 index d924af9b3..000000000 --- a/neutron/plugins/cisco/db/n1kv_db_v2.py +++ /dev/null @@ -1,1621 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Aruna Kushwaha, Cisco Systems Inc. -# @author: Abhishek Raut, Cisco Systems Inc. -# @author: Rudrajit Tapadar, Cisco Systems Inc. -# @author: Sergey Sudakovich, Cisco Systems Inc. - -import netaddr -import re -from sqlalchemy.orm import exc -from sqlalchemy import sql - -from neutron.api.v2 import attributes -from neutron.common import constants -from neutron.common import exceptions as n_exc -import neutron.db.api as db -from neutron.db import models_v2 -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_constants as c_const -from neutron.plugins.cisco.common import cisco_exceptions as c_exc -from neutron.plugins.cisco.db import n1kv_models_v2 - -LOG = logging.getLogger(__name__) - - -def del_trunk_segment_binding(db_session, trunk_segment_id, segment_pairs): - """ - Delete a trunk network binding. - - :param db_session: database session - :param trunk_segment_id: UUID representing the trunk network - :param segment_pairs: List of segment UUIDs in pair - representing the segments that are trunked - """ - with db_session.begin(subtransactions=True): - for (segment_id, dot1qtag) in segment_pairs: - (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding). - filter_by(trunk_segment_id=trunk_segment_id, - segment_id=segment_id, - dot1qtag=dot1qtag).delete()) - alloc = (db_session.query(n1kv_models_v2. - N1kvTrunkSegmentBinding). - filter_by(trunk_segment_id=trunk_segment_id).first()) - if not alloc: - binding = get_network_binding(db_session, trunk_segment_id) - binding.physical_network = None - - -def del_multi_segment_binding(db_session, multi_segment_id, segment_pairs): - """ - Delete a multi-segment network binding. - - :param db_session: database session - :param multi_segment_id: UUID representing the multi-segment network - :param segment_pairs: List of segment UUIDs in pair - representing the segments that are bridged - """ - with db_session.begin(subtransactions=True): - for (segment1_id, segment2_id) in segment_pairs: - (db_session.query(n1kv_models_v2. - N1kvMultiSegmentNetworkBinding).filter_by( - multi_segment_id=multi_segment_id, - segment1_id=segment1_id, - segment2_id=segment2_id).delete()) - - -def add_trunk_segment_binding(db_session, trunk_segment_id, segment_pairs): - """ - Create a trunk network binding. - - :param db_session: database session - :param trunk_segment_id: UUID representing the multi-segment network - :param segment_pairs: List of segment UUIDs in pair - representing the segments to be trunked - """ - with db_session.begin(subtransactions=True): - binding = get_network_binding(db_session, trunk_segment_id) - for (segment_id, tag) in segment_pairs: - if not binding.physical_network: - member_seg_binding = get_network_binding(db_session, - segment_id) - binding.physical_network = member_seg_binding.physical_network - trunk_segment_binding = ( - n1kv_models_v2.N1kvTrunkSegmentBinding( - trunk_segment_id=trunk_segment_id, - segment_id=segment_id, dot1qtag=tag)) - db_session.add(trunk_segment_binding) - - -def add_multi_segment_binding(db_session, multi_segment_id, segment_pairs): - """ - Create a multi-segment network binding. - - :param db_session: database session - :param multi_segment_id: UUID representing the multi-segment network - :param segment_pairs: List of segment UUIDs in pair - representing the segments to be bridged - """ - with db_session.begin(subtransactions=True): - for (segment1_id, segment2_id) in segment_pairs: - multi_segment_binding = ( - n1kv_models_v2.N1kvMultiSegmentNetworkBinding( - multi_segment_id=multi_segment_id, - segment1_id=segment1_id, - segment2_id=segment2_id)) - db_session.add(multi_segment_binding) - - -def add_multi_segment_encap_profile_name(db_session, multi_segment_id, - segment_pair, profile_name): - """ - Add the encapsulation profile name to the multi-segment network binding. - - :param db_session: database session - :param multi_segment_id: UUID representing the multi-segment network - :param segment_pair: set containing the segment UUIDs that are bridged - """ - with db_session.begin(subtransactions=True): - binding = get_multi_segment_network_binding(db_session, - multi_segment_id, - segment_pair) - binding.encap_profile_name = profile_name - - -def get_multi_segment_network_binding(db_session, - multi_segment_id, segment_pair): - """ - Retrieve multi-segment network binding. - - :param db_session: database session - :param multi_segment_id: UUID representing the trunk network whose binding - is to fetch - :param segment_pair: set containing the segment UUIDs that are bridged - :returns: binding object - """ - try: - (segment1_id, segment2_id) = segment_pair - return (db_session.query( - n1kv_models_v2.N1kvMultiSegmentNetworkBinding). - filter_by(multi_segment_id=multi_segment_id, - segment1_id=segment1_id, - segment2_id=segment2_id)).one() - except exc.NoResultFound: - raise c_exc.NetworkBindingNotFound(network_id=multi_segment_id) - - -def get_multi_segment_members(db_session, multi_segment_id): - """ - Retrieve all the member segments of a multi-segment network. - - :param db_session: database session - :param multi_segment_id: UUID representing the multi-segment network - :returns: a list of tuples representing the mapped segments - """ - with db_session.begin(subtransactions=True): - allocs = (db_session.query( - n1kv_models_v2.N1kvMultiSegmentNetworkBinding). - filter_by(multi_segment_id=multi_segment_id)) - return [(a.segment1_id, a.segment2_id) for a in allocs] - - -def get_multi_segment_encap_dict(db_session, multi_segment_id): - """ - Retrieve the encapsulation profiles for every segment pairs bridged. - - :param db_session: database session - :param multi_segment_id: UUID representing the multi-segment network - :returns: a dictionary of lists containing the segment pairs in sets - """ - with db_session.begin(subtransactions=True): - encap_dict = {} - allocs = (db_session.query( - n1kv_models_v2.N1kvMultiSegmentNetworkBinding). - filter_by(multi_segment_id=multi_segment_id)) - for alloc in allocs: - if alloc.encap_profile_name not in encap_dict: - encap_dict[alloc.encap_profile_name] = [] - seg_pair = (alloc.segment1_id, alloc.segment2_id) - encap_dict[alloc.encap_profile_name].append(seg_pair) - return encap_dict - - -def get_trunk_network_binding(db_session, trunk_segment_id, segment_pair): - """ - Retrieve trunk network binding. - - :param db_session: database session - :param trunk_segment_id: UUID representing the trunk network whose binding - is to fetch - :param segment_pair: set containing the segment_id and dot1qtag - :returns: binding object - """ - try: - (segment_id, dot1qtag) = segment_pair - return (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding). - filter_by(trunk_segment_id=trunk_segment_id, - segment_id=segment_id, - dot1qtag=dot1qtag)).one() - except exc.NoResultFound: - raise c_exc.NetworkBindingNotFound(network_id=trunk_segment_id) - - -def get_trunk_members(db_session, trunk_segment_id): - """ - Retrieve all the member segments of a trunk network. - - :param db_session: database session - :param trunk_segment_id: UUID representing the trunk network - :returns: a list of tuples representing the segment and their - corresponding dot1qtag - """ - with db_session.begin(subtransactions=True): - allocs = (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding). - filter_by(trunk_segment_id=trunk_segment_id)) - return [(a.segment_id, a.dot1qtag) for a in allocs] - - -def is_trunk_member(db_session, segment_id): - """ - Checks if a segment is a member of a trunk segment. - - :param db_session: database session - :param segment_id: UUID of the segment to be checked - :returns: boolean - """ - with db_session.begin(subtransactions=True): - ret = (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding). - filter_by(segment_id=segment_id).first()) - return bool(ret) - - -def is_multi_segment_member(db_session, segment_id): - """ - Checks if a segment is a member of a multi-segment network. - - :param db_session: database session - :param segment_id: UUID of the segment to be checked - :returns: boolean - """ - with db_session.begin(subtransactions=True): - ret1 = (db_session.query( - n1kv_models_v2.N1kvMultiSegmentNetworkBinding). - filter_by(segment1_id=segment_id).first()) - ret2 = (db_session.query( - n1kv_models_v2.N1kvMultiSegmentNetworkBinding). - filter_by(segment2_id=segment_id).first()) - return bool(ret1 or ret2) - - -def get_network_binding(db_session, network_id): - """ - Retrieve network binding. - - :param db_session: database session - :param network_id: UUID representing the network whose binding is - to fetch - :returns: binding object - """ - try: - return (db_session.query(n1kv_models_v2.N1kvNetworkBinding). - filter_by(network_id=network_id). - one()) - except exc.NoResultFound: - raise c_exc.NetworkBindingNotFound(network_id=network_id) - - -def add_network_binding(db_session, network_id, network_type, - physical_network, segmentation_id, - multicast_ip, network_profile_id, add_segments): - """ - Create network binding. - - :param db_session: database session - :param network_id: UUID representing the network - :param network_type: string representing type of network (VLAN, OVERLAY, - MULTI_SEGMENT or TRUNK) - :param physical_network: Only applicable for VLAN networks. It - represents a L2 Domain - :param segmentation_id: integer representing VLAN or VXLAN ID - :param multicast_ip: Native VXLAN technology needs a multicast IP to be - associated with every VXLAN ID to deal with broadcast - packets. A single multicast IP can be shared by - multiple VXLAN IDs. - :param network_profile_id: network profile ID based on which this network - is created - :param add_segments: List of segment UUIDs in pairs to be added to either a - multi-segment or trunk network - """ - with db_session.begin(subtransactions=True): - binding = n1kv_models_v2.N1kvNetworkBinding( - network_id=network_id, - network_type=network_type, - physical_network=physical_network, - segmentation_id=segmentation_id, - multicast_ip=multicast_ip, - profile_id=network_profile_id) - db_session.add(binding) - if add_segments is None: - pass - elif network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT: - add_multi_segment_binding(db_session, network_id, add_segments) - elif network_type == c_const.NETWORK_TYPE_TRUNK: - add_trunk_segment_binding(db_session, network_id, add_segments) - - -def get_segment_range(network_profile): - """ - Get the segment range min and max for a network profile. - - :params network_profile: object of type network profile - :returns: integer values representing minimum and maximum segment - range value - """ - # Sort the range to ensure min, max is in order - seg_min, seg_max = sorted( - int(i) for i in network_profile.segment_range.split('-')) - LOG.debug(_("seg_min %(seg_min)s, seg_max %(seg_max)s"), - {'seg_min': seg_min, 'seg_max': seg_max}) - return seg_min, seg_max - - -def get_multicast_ip(network_profile): - """ - Retrieve a multicast ip from the defined pool. - - :params network_profile: object of type network profile - :returns: string representing multicast IP - """ - # Round robin multicast ip allocation - min_ip, max_ip = _get_multicast_ip_range(network_profile) - addr_list = list((netaddr.iter_iprange(min_ip, max_ip))) - mul_ip_str = str(addr_list[network_profile.multicast_ip_index]) - - network_profile.multicast_ip_index += 1 - if network_profile.multicast_ip_index == len(addr_list): - network_profile.multicast_ip_index = 0 - return mul_ip_str - - -def _get_multicast_ip_range(network_profile): - """ - Helper method to retrieve minimum and maximum multicast ip. - - :params network_profile: object of type network profile - :returns: two strings representing minimum multicast ip and - maximum multicast ip - """ - # Assumption: ip range belongs to the same subnet - # Assumption: ip range is already sorted - return network_profile.multicast_ip_range.split('-') - - -def get_port_binding(db_session, port_id): - """ - Retrieve port binding. - - :param db_session: database session - :param port_id: UUID representing the port whose binding is to fetch - :returns: port binding object - """ - try: - return (db_session.query(n1kv_models_v2.N1kvPortBinding). - filter_by(port_id=port_id). - one()) - except exc.NoResultFound: - raise c_exc.PortBindingNotFound(port_id=port_id) - - -def add_port_binding(db_session, port_id, policy_profile_id): - """ - Create port binding. - - Bind the port with policy profile. - :param db_session: database session - :param port_id: UUID of the port - :param policy_profile_id: UUID of the policy profile - """ - with db_session.begin(subtransactions=True): - binding = n1kv_models_v2.N1kvPortBinding(port_id=port_id, - profile_id=policy_profile_id) - db_session.add(binding) - - -def delete_segment_allocations(db_session, net_p): - """ - Delete the segment allocation entry from the table. - - :params db_session: database session - :params net_p: network profile object - """ - with db_session.begin(subtransactions=True): - seg_min, seg_max = get_segment_range(net_p) - if net_p['segment_type'] == c_const.NETWORK_TYPE_VLAN: - db_session.query(n1kv_models_v2.N1kvVlanAllocation).filter( - (n1kv_models_v2.N1kvVlanAllocation.physical_network == - net_p['physical_network']), - (n1kv_models_v2.N1kvVlanAllocation.vlan_id >= seg_min), - (n1kv_models_v2.N1kvVlanAllocation.vlan_id <= - seg_max)).delete() - elif net_p['segment_type'] == c_const.NETWORK_TYPE_OVERLAY: - db_session.query(n1kv_models_v2.N1kvVxlanAllocation).filter( - (n1kv_models_v2.N1kvVxlanAllocation.vxlan_id >= seg_min), - (n1kv_models_v2.N1kvVxlanAllocation.vxlan_id <= - seg_max)).delete() - - -def sync_vlan_allocations(db_session, net_p): - """ - Synchronize vlan_allocations table with configured VLAN ranges. - - Sync the network profile range with the vlan_allocations table for each - physical network. - :param db_session: database session - :param net_p: network profile dictionary - """ - with db_session.begin(subtransactions=True): - seg_min, seg_max = get_segment_range(net_p) - for vlan_id in range(seg_min, seg_max + 1): - try: - get_vlan_allocation(db_session, - net_p['physical_network'], - vlan_id) - except c_exc.VlanIDNotFound: - alloc = n1kv_models_v2.N1kvVlanAllocation( - physical_network=net_p['physical_network'], - vlan_id=vlan_id, - network_profile_id=net_p['id']) - db_session.add(alloc) - - -def get_vlan_allocation(db_session, physical_network, vlan_id): - """ - Retrieve vlan allocation. - - :param db_session: database session - :param physical network: string name for the physical network - :param vlan_id: integer representing the VLAN ID. - :returns: allocation object for given physical network and VLAN ID - """ - try: - return (db_session.query(n1kv_models_v2.N1kvVlanAllocation). - filter_by(physical_network=physical_network, - vlan_id=vlan_id).one()) - except exc.NoResultFound: - raise c_exc.VlanIDNotFound(vlan_id=vlan_id) - - -def reserve_vlan(db_session, network_profile): - """ - Reserve a VLAN ID within the range of the network profile. - - :param db_session: database session - :param network_profile: network profile object - """ - seg_min, seg_max = get_segment_range(network_profile) - segment_type = c_const.NETWORK_TYPE_VLAN - - with db_session.begin(subtransactions=True): - alloc = (db_session.query(n1kv_models_v2.N1kvVlanAllocation). - filter(sql.and_( - n1kv_models_v2.N1kvVlanAllocation.vlan_id >= seg_min, - n1kv_models_v2.N1kvVlanAllocation.vlan_id <= seg_max, - n1kv_models_v2.N1kvVlanAllocation.physical_network == - network_profile['physical_network'], - n1kv_models_v2.N1kvVlanAllocation.allocated == - sql.false()) - )).first() - if alloc: - segment_id = alloc.vlan_id - physical_network = alloc.physical_network - alloc.allocated = True - return (physical_network, segment_type, segment_id, "0.0.0.0") - raise c_exc.NoMoreNetworkSegments( - network_profile_name=network_profile.name) - - -def reserve_vxlan(db_session, network_profile): - """ - Reserve a VXLAN ID within the range of the network profile. - - :param db_session: database session - :param network_profile: network profile object - """ - seg_min, seg_max = get_segment_range(network_profile) - segment_type = c_const.NETWORK_TYPE_OVERLAY - physical_network = "" - - with db_session.begin(subtransactions=True): - alloc = (db_session.query(n1kv_models_v2.N1kvVxlanAllocation). - filter(sql.and_( - n1kv_models_v2.N1kvVxlanAllocation.vxlan_id >= - seg_min, - n1kv_models_v2.N1kvVxlanAllocation.vxlan_id <= - seg_max, - n1kv_models_v2.N1kvVxlanAllocation.allocated == - sql.false()) - ).first()) - if alloc: - segment_id = alloc.vxlan_id - alloc.allocated = True - if network_profile.sub_type == (c_const. - NETWORK_SUBTYPE_NATIVE_VXLAN): - return (physical_network, segment_type, - segment_id, get_multicast_ip(network_profile)) - else: - return (physical_network, segment_type, segment_id, "0.0.0.0") - raise n_exc.NoNetworkAvailable() - - -def alloc_network(db_session, network_profile_id): - """ - Allocate network using first available free segment ID in segment range. - - :param db_session: database session - :param network_profile_id: UUID representing the network profile - """ - with db_session.begin(subtransactions=True): - network_profile = get_network_profile(db_session, - network_profile_id) - if network_profile.segment_type == c_const.NETWORK_TYPE_VLAN: - return reserve_vlan(db_session, network_profile) - if network_profile.segment_type == c_const.NETWORK_TYPE_OVERLAY: - return reserve_vxlan(db_session, network_profile) - return (None, network_profile.segment_type, 0, "0.0.0.0") - - -def reserve_specific_vlan(db_session, physical_network, vlan_id): - """ - Reserve a specific VLAN ID for the network. - - :param db_session: database session - :param physical_network: string representing the name of physical network - :param vlan_id: integer value of the segmentation ID to be reserved - """ - with db_session.begin(subtransactions=True): - try: - alloc = (db_session.query(n1kv_models_v2.N1kvVlanAllocation). - filter_by(physical_network=physical_network, - vlan_id=vlan_id). - one()) - if alloc.allocated: - if vlan_id == c_const.FLAT_VLAN_ID: - raise n_exc.FlatNetworkInUse( - physical_network=physical_network) - else: - raise n_exc.VlanIdInUse(vlan_id=vlan_id, - physical_network=physical_network) - LOG.debug(_("Reserving specific vlan %(vlan)s on physical " - "network %(network)s from pool"), - {"vlan": vlan_id, "network": physical_network}) - alloc.allocated = True - db_session.add(alloc) - except exc.NoResultFound: - raise c_exc.VlanIDOutsidePool - - -def release_vlan(db_session, physical_network, vlan_id): - """ - Release a given VLAN ID. - - :param db_session: database session - :param physical_network: string representing the name of physical network - :param vlan_id: integer value of the segmentation ID to be released - """ - with db_session.begin(subtransactions=True): - try: - alloc = (db_session.query(n1kv_models_v2.N1kvVlanAllocation). - filter_by(physical_network=physical_network, - vlan_id=vlan_id). - one()) - alloc.allocated = False - except exc.NoResultFound: - LOG.warning(_("vlan_id %(vlan)s on physical network %(network)s " - "not found"), - {"vlan": vlan_id, "network": physical_network}) - - -def sync_vxlan_allocations(db_session, net_p): - """ - Synchronize vxlan_allocations table with configured vxlan ranges. - - :param db_session: database session - :param net_p: network profile dictionary - """ - seg_min, seg_max = get_segment_range(net_p) - if seg_max + 1 - seg_min > c_const.MAX_VXLAN_RANGE: - msg = (_("Unreasonable vxlan ID range %(vxlan_min)s - %(vxlan_max)s"), - {"vxlan_min": seg_min, "vxlan_max": seg_max}) - raise n_exc.InvalidInput(error_message=msg) - with db_session.begin(subtransactions=True): - for vxlan_id in range(seg_min, seg_max + 1): - try: - get_vxlan_allocation(db_session, vxlan_id) - except c_exc.VxlanIDNotFound: - alloc = n1kv_models_v2.N1kvVxlanAllocation( - network_profile_id=net_p['id'], vxlan_id=vxlan_id) - db_session.add(alloc) - - -def get_vxlan_allocation(db_session, vxlan_id): - """ - Retrieve VXLAN allocation for the given VXLAN ID. - - :param db_session: database session - :param vxlan_id: integer value representing the segmentation ID - :returns: allocation object - """ - try: - return (db_session.query(n1kv_models_v2.N1kvVxlanAllocation). - filter_by(vxlan_id=vxlan_id).one()) - except exc.NoResultFound: - raise c_exc.VxlanIDNotFound(vxlan_id=vxlan_id) - - -def reserve_specific_vxlan(db_session, vxlan_id): - """ - Reserve a specific VXLAN ID. - - :param db_session: database session - :param vxlan_id: integer value representing the segmentation ID - """ - with db_session.begin(subtransactions=True): - try: - alloc = (db_session.query(n1kv_models_v2.N1kvVxlanAllocation). - filter_by(vxlan_id=vxlan_id). - one()) - if alloc.allocated: - raise c_exc.VxlanIDInUse(vxlan_id=vxlan_id) - LOG.debug(_("Reserving specific vxlan %s from pool"), vxlan_id) - alloc.allocated = True - db_session.add(alloc) - except exc.NoResultFound: - raise c_exc.VxlanIDOutsidePool - - -def release_vxlan(db_session, vxlan_id): - """ - Release a given VXLAN ID. - - :param db_session: database session - :param vxlan_id: integer value representing the segmentation ID - """ - with db_session.begin(subtransactions=True): - try: - alloc = (db_session.query(n1kv_models_v2.N1kvVxlanAllocation). - filter_by(vxlan_id=vxlan_id). - one()) - alloc.allocated = False - except exc.NoResultFound: - LOG.warning(_("vxlan_id %s not found"), vxlan_id) - - -def set_port_status(port_id, status): - """ - Set the status of the port. - - :param port_id: UUID representing the port - :param status: string representing the new status - """ - db_session = db.get_session() - try: - port = db_session.query(models_v2.Port).filter_by(id=port_id).one() - port.status = status - except exc.NoResultFound: - raise n_exc.PortNotFound(port_id=port_id) - - -def get_vm_network(db_session, policy_profile_id, network_id): - """ - Retrieve a vm_network based on policy profile and network id. - - :param db_session: database session - :param policy_profile_id: UUID representing policy profile - :param network_id: UUID representing network - :returns: VM network object - """ - try: - return (db_session.query(n1kv_models_v2.N1kVmNetwork). - filter_by(profile_id=policy_profile_id, - network_id=network_id).one()) - except exc.NoResultFound: - name = (c_const.VM_NETWORK_NAME_PREFIX + policy_profile_id - + "_" + network_id) - raise c_exc.VMNetworkNotFound(name=name) - - -def add_vm_network(db_session, - name, - policy_profile_id, - network_id, - port_count): - """ - Create a VM network. - - Add a VM network for a unique combination of network and - policy profile. All ports having the same policy profile - on one network will be associated with one VM network. - :param db_session: database session - :param name: string representing the name of the VM network - :param policy_profile_id: UUID representing policy profile - :param network_id: UUID representing a network - :param port_count: integer representing the number of ports on vm network - """ - with db_session.begin(subtransactions=True): - vm_network = n1kv_models_v2.N1kVmNetwork( - name=name, - profile_id=policy_profile_id, - network_id=network_id, - port_count=port_count) - db_session.add(vm_network) - - -def update_vm_network_port_count(db_session, name, port_count): - """ - Update a VM network with new port count. - - :param db_session: database session - :param name: string representing the name of the VM network - :param port_count: integer representing the number of ports on VM network - """ - try: - with db_session.begin(subtransactions=True): - vm_network = (db_session.query(n1kv_models_v2.N1kVmNetwork). - filter_by(name=name).one()) - if port_count is not None: - vm_network.port_count = port_count - return vm_network - except exc.NoResultFound: - raise c_exc.VMNetworkNotFound(name=name) - - -def delete_vm_network(db_session, policy_profile_id, network_id): - """ - Delete a VM network. - - :param db_session: database session - :param policy_profile_id: UUID representing a policy profile - :param network_id: UUID representing a network - :returns: deleted VM network object - """ - with db_session.begin(subtransactions=True): - try: - vm_network = get_vm_network(db_session, - policy_profile_id, - network_id) - db_session.delete(vm_network) - db_session.query(n1kv_models_v2.N1kVmNetwork).filter_by( - name=vm_network["name"]).delete() - return vm_network - except exc.NoResultFound: - name = (c_const.VM_NETWORK_NAME_PREFIX + policy_profile_id + - "_" + network_id) - raise c_exc.VMNetworkNotFound(name=name) - - -def create_network_profile(db_session, network_profile): - """Create a network profile.""" - LOG.debug(_("create_network_profile()")) - with db_session.begin(subtransactions=True): - kwargs = {"name": network_profile["name"], - "segment_type": network_profile["segment_type"]} - if network_profile["segment_type"] == c_const.NETWORK_TYPE_VLAN: - kwargs["physical_network"] = network_profile["physical_network"] - kwargs["segment_range"] = network_profile["segment_range"] - elif network_profile["segment_type"] == c_const.NETWORK_TYPE_OVERLAY: - kwargs["multicast_ip_index"] = 0 - kwargs["multicast_ip_range"] = network_profile[ - "multicast_ip_range"] - kwargs["segment_range"] = network_profile["segment_range"] - kwargs["sub_type"] = network_profile["sub_type"] - elif network_profile["segment_type"] == c_const.NETWORK_TYPE_TRUNK: - kwargs["sub_type"] = network_profile["sub_type"] - net_profile = n1kv_models_v2.NetworkProfile(**kwargs) - db_session.add(net_profile) - return net_profile - - -def delete_network_profile(db_session, id): - """Delete Network Profile.""" - LOG.debug(_("delete_network_profile()")) - with db_session.begin(subtransactions=True): - try: - network_profile = get_network_profile(db_session, id) - db_session.delete(network_profile) - (db_session.query(n1kv_models_v2.ProfileBinding). - filter_by(profile_id=id).delete()) - return network_profile - except exc.NoResultFound: - raise c_exc.ProfileTenantBindingNotFound(profile_id=id) - - -def update_network_profile(db_session, id, network_profile): - """Update Network Profile.""" - LOG.debug(_("update_network_profile()")) - with db_session.begin(subtransactions=True): - profile = get_network_profile(db_session, id) - profile.update(network_profile) - return profile - - -def get_network_profile(db_session, id): - """Get Network Profile.""" - LOG.debug(_("get_network_profile()")) - try: - return db_session.query( - n1kv_models_v2.NetworkProfile).filter_by(id=id).one() - except exc.NoResultFound: - raise c_exc.NetworkProfileNotFound(profile=id) - - -def _get_network_profiles(db_session=None, physical_network=None): - """ - Retrieve all network profiles. - - Get Network Profiles on a particular physical network, if physical - network is specified. If no physical network is specified, return - all network profiles. - """ - db_session = db_session or db.get_session() - if physical_network: - return (db_session.query(n1kv_models_v2.NetworkProfile). - filter_by(physical_network=physical_network)) - return db_session.query(n1kv_models_v2.NetworkProfile) - - -def create_policy_profile(policy_profile): - """Create Policy Profile.""" - LOG.debug(_("create_policy_profile()")) - db_session = db.get_session() - with db_session.begin(subtransactions=True): - p_profile = n1kv_models_v2.PolicyProfile(id=policy_profile["id"], - name=policy_profile["name"]) - db_session.add(p_profile) - return p_profile - - -def delete_policy_profile(id): - """Delete Policy Profile.""" - LOG.debug(_("delete_policy_profile()")) - db_session = db.get_session() - with db_session.begin(subtransactions=True): - policy_profile = get_policy_profile(db_session, id) - db_session.delete(policy_profile) - - -def update_policy_profile(db_session, id, policy_profile): - """Update a policy profile.""" - LOG.debug(_("update_policy_profile()")) - with db_session.begin(subtransactions=True): - _profile = get_policy_profile(db_session, id) - _profile.update(policy_profile) - return _profile - - -def get_policy_profile(db_session, id): - """Get Policy Profile.""" - LOG.debug(_("get_policy_profile()")) - try: - return db_session.query( - n1kv_models_v2.PolicyProfile).filter_by(id=id).one() - except exc.NoResultFound: - raise c_exc.PolicyProfileIdNotFound(profile_id=id) - - -def get_policy_profiles(): - """Retrieve all policy profiles.""" - db_session = db.get_session() - with db_session.begin(subtransactions=True): - return db_session.query(n1kv_models_v2.PolicyProfile) - - -def create_profile_binding(db_session, tenant_id, profile_id, profile_type): - """Create Network/Policy Profile association with a tenant.""" - db_session = db_session or db.get_session() - if profile_type not in ["network", "policy"]: - raise n_exc.NeutronException(_("Invalid profile type")) - - if _profile_binding_exists(db_session, - tenant_id, - profile_id, - profile_type): - return get_profile_binding(db_session, tenant_id, profile_id) - - with db_session.begin(subtransactions=True): - binding = n1kv_models_v2.ProfileBinding(profile_type=profile_type, - profile_id=profile_id, - tenant_id=tenant_id) - db_session.add(binding) - return binding - - -def _profile_binding_exists(db_session, tenant_id, profile_id, profile_type): - LOG.debug(_("_profile_binding_exists()")) - return (db_session.query(n1kv_models_v2.ProfileBinding). - filter_by(tenant_id=tenant_id, profile_id=profile_id, - profile_type=profile_type).first()) - - -def get_profile_binding(db_session, tenant_id, profile_id): - """Get Network/Policy Profile - Tenant binding.""" - LOG.debug(_("get_profile_binding()")) - try: - return (db_session.query(n1kv_models_v2.ProfileBinding).filter_by( - tenant_id=tenant_id, profile_id=profile_id).one()) - except exc.NoResultFound: - raise c_exc.ProfileTenantBindingNotFound(profile_id=profile_id) - - -def delete_profile_binding(db_session, tenant_id, profile_id): - """Delete Policy Binding.""" - LOG.debug(_("delete_profile_binding()")) - db_session = db_session or db.get_session() - try: - binding = get_profile_binding(db_session, tenant_id, profile_id) - with db_session.begin(subtransactions=True): - db_session.delete(binding) - except c_exc.ProfileTenantBindingNotFound: - LOG.debug(_("Profile-Tenant binding missing for profile ID " - "%(profile_id)s and tenant ID %(tenant_id)s"), - {"profile_id": profile_id, "tenant_id": tenant_id}) - return - - -def _get_profile_bindings(db_session, profile_type=None): - """ - Retrieve a list of profile bindings. - - Get all profile-tenant bindings based on profile type. - If profile type is None, return profile-tenant binding for all - profile types. - """ - LOG.debug(_("_get_profile_bindings()")) - if profile_type: - profile_bindings = (db_session.query(n1kv_models_v2.ProfileBinding). - filter_by(profile_type=profile_type)) - return profile_bindings - return db_session.query(n1kv_models_v2.ProfileBinding) - - -class NetworkProfile_db_mixin(object): - - """Network Profile Mixin.""" - - def _replace_fake_tenant_id_with_real(self, context): - """ - Replace default tenant-id with admin tenant-ids. - - Default tenant-ids are populated in profile bindings when plugin is - initialized. Replace these tenant-ids with admin's tenant-id. - :param context: neutron api request context - """ - if context.is_admin and context.tenant_id: - tenant_id = context.tenant_id - db_session = context.session - with db_session.begin(subtransactions=True): - (db_session.query(n1kv_models_v2.ProfileBinding). - filter_by(tenant_id=c_const.TENANT_ID_NOT_SET). - update({'tenant_id': tenant_id})) - - def _get_network_collection_for_tenant(self, db_session, model, tenant_id): - net_profile_ids = (db_session.query(n1kv_models_v2.ProfileBinding. - profile_id). - filter_by(tenant_id=tenant_id). - filter_by(profile_type=c_const.NETWORK)) - network_profiles = (db_session.query(model).filter(model.id.in_( - pid[0] for pid in net_profile_ids))) - return [self._make_network_profile_dict(p) for p in network_profiles] - - def _make_profile_bindings_dict(self, profile_binding, fields=None): - res = {"profile_id": profile_binding["profile_id"], - "tenant_id": profile_binding["tenant_id"]} - return self._fields(res, fields) - - def _make_network_profile_dict(self, network_profile, fields=None): - res = {"id": network_profile["id"], - "name": network_profile["name"], - "segment_type": network_profile["segment_type"], - "sub_type": network_profile["sub_type"], - "segment_range": network_profile["segment_range"], - "multicast_ip_index": network_profile["multicast_ip_index"], - "multicast_ip_range": network_profile["multicast_ip_range"], - "physical_network": network_profile["physical_network"]} - return self._fields(res, fields) - - def _segment_in_use(self, db_session, network_profile): - """Verify whether a segment is allocated for given network profile.""" - with db_session.begin(subtransactions=True): - return (db_session.query(n1kv_models_v2.N1kvNetworkBinding). - filter_by(profile_id=network_profile['id'])).first() - - def get_network_profile_bindings(self, context, filters=None, fields=None): - """ - Retrieve a list of profile bindings for network profiles. - - :param context: neutron api request context - :param filters: a dictionary with keys that are valid keys for a - profile bindings object. Values in this dictiontary are - an iterable containing values that will be used for an - exact match comparison for that value. Each result - returned by this function will have matched one of the - values for each key in filters - :params fields: a list of strings that are valid keys in a profile - bindings dictionary. Only these fields will be returned - :returns: list of profile bindings - """ - if context.is_admin: - profile_bindings = _get_profile_bindings( - context.session, - profile_type=c_const.NETWORK) - return [self._make_profile_bindings_dict(pb) - for pb in profile_bindings] - - def create_network_profile(self, context, network_profile): - """ - Create a network profile. - - :param context: neutron api request context - :param network_profile: network profile dictionary - :returns: network profile dictionary - """ - self._replace_fake_tenant_id_with_real(context) - p = network_profile["network_profile"] - self._validate_network_profile_args(context, p) - with context.session.begin(subtransactions=True): - net_profile = create_network_profile(context.session, p) - if net_profile.segment_type == c_const.NETWORK_TYPE_VLAN: - sync_vlan_allocations(context.session, net_profile) - elif net_profile.segment_type == c_const.NETWORK_TYPE_OVERLAY: - sync_vxlan_allocations(context.session, net_profile) - create_profile_binding(context.session, - context.tenant_id, - net_profile.id, - c_const.NETWORK) - if p.get("add_tenant"): - self.add_network_profile_tenant(context.session, - net_profile.id, - p["add_tenant"]) - return self._make_network_profile_dict(net_profile) - - def delete_network_profile(self, context, id): - """ - Delete a network profile. - - :param context: neutron api request context - :param id: UUID representing network profile to delete - :returns: deleted network profile dictionary - """ - # Check whether the network profile is in use. - if self._segment_in_use(context.session, - get_network_profile(context.session, id)): - raise c_exc.NetworkProfileInUse(profile=id) - # Delete and return the network profile if it is not in use. - _profile = delete_network_profile(context.session, id) - return self._make_network_profile_dict(_profile) - - def update_network_profile(self, context, id, network_profile): - """ - Update a network profile. - - Add/remove network profile to tenant-id binding for the corresponding - options and if user is admin. - :param context: neutron api request context - :param id: UUID representing network profile to update - :param network_profile: network profile dictionary - :returns: updated network profile dictionary - """ - # Flag to check whether network profile is updated or not. - is_updated = False - p = network_profile["network_profile"] - original_net_p = get_network_profile(context.session, id) - # Update network profile to tenant id binding. - if context.is_admin and "add_tenant" in p: - self.add_network_profile_tenant(context.session, id, - p["add_tenant"]) - is_updated = True - if context.is_admin and "remove_tenant" in p: - delete_profile_binding(context.session, p["remove_tenant"], id) - is_updated = True - if original_net_p.segment_type == c_const.NETWORK_TYPE_TRUNK: - #TODO(abhraut): Remove check when Trunk supports segment range. - if p.get('segment_range'): - msg = _("segment_range not required for TRUNK") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if original_net_p.segment_type in [c_const.NETWORK_TYPE_VLAN, - c_const.NETWORK_TYPE_TRUNK]: - if p.get("multicast_ip_range"): - msg = _("multicast_ip_range not required") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - # Update segment range if network profile is not in use. - if (p.get("segment_range") and - p.get("segment_range") != original_net_p.segment_range): - if not self._segment_in_use(context.session, original_net_p): - delete_segment_allocations(context.session, original_net_p) - updated_net_p = update_network_profile(context.session, id, p) - self._validate_segment_range_uniqueness(context, - updated_net_p, id) - if original_net_p.segment_type == c_const.NETWORK_TYPE_VLAN: - sync_vlan_allocations(context.session, updated_net_p) - if original_net_p.segment_type == c_const.NETWORK_TYPE_OVERLAY: - sync_vxlan_allocations(context.session, updated_net_p) - is_updated = True - else: - raise c_exc.NetworkProfileInUse(profile=id) - if (p.get('multicast_ip_range') and - (p.get("multicast_ip_range") != - original_net_p.get("multicast_ip_range"))): - self._validate_multicast_ip_range(p) - if not self._segment_in_use(context.session, original_net_p): - is_updated = True - else: - raise c_exc.NetworkProfileInUse(profile=id) - # Update network profile if name is updated and the network profile - # is not yet updated. - if "name" in p and not is_updated: - is_updated = True - # Return network profile if it is successfully updated. - if is_updated: - return self._make_network_profile_dict( - update_network_profile(context.session, id, p)) - - def get_network_profile(self, context, id, fields=None): - """ - Retrieve a network profile. - - :param context: neutron api request context - :param id: UUID representing the network profile to retrieve - :params fields: a list of strings that are valid keys in a network - profile dictionary. Only these fields will be returned - :returns: network profile dictionary - """ - profile = get_network_profile(context.session, id) - return self._make_network_profile_dict(profile, fields) - - def get_network_profiles(self, context, filters=None, fields=None): - """ - Retrieve a list of all network profiles. - - Retrieve all network profiles if tenant is admin. For a non-admin - tenant, retrieve all network profiles belonging to this tenant only. - :param context: neutron api request context - :param filters: a dictionary with keys that are valid keys for a - network profile object. Values in this dictiontary are - an iterable containing values that will be used for an - exact match comparison for that value. Each result - returned by this function will have matched one of the - values for each key in filters - :params fields: a list of strings that are valid keys in a network - profile dictionary. Only these fields will be returned - :returns: list of all network profiles - """ - if context.is_admin: - return self._get_collection(context, n1kv_models_v2.NetworkProfile, - self._make_network_profile_dict, - filters=filters, fields=fields) - return self._get_network_collection_for_tenant(context.session, - n1kv_models_v2. - NetworkProfile, - context.tenant_id) - - def add_network_profile_tenant(self, - db_session, - network_profile_id, - tenant_id): - """ - Add a tenant to a network profile. - - :param db_session: database session - :param network_profile_id: UUID representing network profile - :param tenant_id: UUID representing the tenant - :returns: profile binding object - """ - return create_profile_binding(db_session, - tenant_id, - network_profile_id, - c_const.NETWORK) - - def network_profile_exists(self, context, id): - """ - Verify whether a network profile for given id exists. - - :param context: neutron api request context - :param id: UUID representing network profile - :returns: true if network profile exist else False - """ - try: - get_network_profile(context.session, id) - return True - except c_exc.NetworkProfileNotFound(profile=id): - return False - - def _get_segment_range(self, data): - return (int(seg) for seg in data.split("-")[:2]) - - def _validate_network_profile_args(self, context, p): - """ - Validate completeness of Nexus1000V network profile arguments. - - :param context: neutron api request context - :param p: network profile object - """ - self._validate_network_profile(p) - segment_type = p['segment_type'].lower() - if segment_type != c_const.NETWORK_TYPE_TRUNK: - self._validate_segment_range_uniqueness(context, p) - - def _validate_segment_range(self, network_profile): - """ - Validate segment range values. - - :param network_profile: network profile object - """ - if not re.match(r"(\d+)\-(\d+)", network_profile["segment_range"]): - msg = _("Invalid segment range. example range: 500-550") - raise n_exc.InvalidInput(error_message=msg) - - def _validate_multicast_ip_range(self, network_profile): - """ - Validate multicast ip range values. - - :param network_profile: network profile object - """ - try: - min_ip, max_ip = (network_profile - ['multicast_ip_range'].split('-', 1)) - except ValueError: - msg = _("Invalid multicast ip address range. " - "example range: 224.1.1.1-224.1.1.10") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - for ip in [min_ip, max_ip]: - try: - if not netaddr.IPAddress(ip).is_multicast(): - msg = _("%s is not a valid multicast ip address") % ip - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if netaddr.IPAddress(ip) <= netaddr.IPAddress('224.0.0.255'): - msg = _("%s is reserved multicast ip address") % ip - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - except netaddr.AddrFormatError: - msg = _("%s is not a valid ip address") % ip - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if netaddr.IPAddress(min_ip) > netaddr.IPAddress(max_ip): - msg = (_("Invalid multicast IP range '%(min_ip)s-%(max_ip)s':" - " Range should be from low address to high address") % - {'min_ip': min_ip, 'max_ip': max_ip}) - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - - def _validate_network_profile(self, net_p): - """ - Validate completeness of a network profile arguments. - - :param net_p: network profile object - """ - if any(net_p[arg] == "" for arg in ["segment_type"]): - msg = _("Arguments segment_type missing" - " for network profile") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - segment_type = net_p["segment_type"].lower() - if segment_type not in [c_const.NETWORK_TYPE_VLAN, - c_const.NETWORK_TYPE_OVERLAY, - c_const.NETWORK_TYPE_TRUNK, - c_const.NETWORK_TYPE_MULTI_SEGMENT]: - msg = _("segment_type should either be vlan, overlay, " - "multi-segment or trunk") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if segment_type == c_const.NETWORK_TYPE_VLAN: - if "physical_network" not in net_p: - msg = _("Argument physical_network missing " - "for network profile") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if segment_type == c_const.NETWORK_TYPE_TRUNK: - if net_p["segment_range"]: - msg = _("segment_range not required for trunk") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if segment_type in [c_const.NETWORK_TYPE_TRUNK, - c_const.NETWORK_TYPE_OVERLAY]: - if not attributes.is_attr_set(net_p.get("sub_type")): - msg = _("Argument sub_type missing " - "for network profile") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if segment_type in [c_const.NETWORK_TYPE_VLAN, - c_const.NETWORK_TYPE_OVERLAY]: - if "segment_range" not in net_p: - msg = _("Argument segment_range missing " - "for network profile") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - self._validate_segment_range(net_p) - if segment_type == c_const.NETWORK_TYPE_OVERLAY: - if net_p['sub_type'] != c_const.NETWORK_SUBTYPE_NATIVE_VXLAN: - net_p['multicast_ip_range'] = '0.0.0.0' - else: - multicast_ip_range = net_p.get("multicast_ip_range") - if not attributes.is_attr_set(multicast_ip_range): - msg = _("Argument multicast_ip_range missing" - " for VXLAN multicast network profile") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - self._validate_multicast_ip_range(net_p) - else: - net_p['multicast_ip_range'] = '0.0.0.0' - - def _validate_segment_range_uniqueness(self, context, net_p, id=None): - """ - Validate that segment range doesn't overlap. - - :param context: neutron api request context - :param net_p: network profile dictionary - :param id: UUID representing the network profile being updated - """ - segment_type = net_p["segment_type"].lower() - seg_min, seg_max = self._get_segment_range(net_p['segment_range']) - if segment_type == c_const.NETWORK_TYPE_VLAN: - if not ((seg_min <= seg_max) and - ((seg_min in range(constants.MIN_VLAN_TAG, - c_const.NEXUS_VLAN_RESERVED_MIN) and - seg_max in range(constants.MIN_VLAN_TAG, - c_const.NEXUS_VLAN_RESERVED_MIN)) or - (seg_min in range(c_const.NEXUS_VLAN_RESERVED_MAX + 1, - constants.MAX_VLAN_TAG) and - seg_max in range(c_const.NEXUS_VLAN_RESERVED_MAX + 1, - constants.MAX_VLAN_TAG)))): - msg = (_("Segment range is invalid, select from " - "%(min)s-%(nmin)s, %(nmax)s-%(max)s") % - {"min": constants.MIN_VLAN_TAG, - "nmin": c_const.NEXUS_VLAN_RESERVED_MIN - 1, - "nmax": c_const.NEXUS_VLAN_RESERVED_MAX + 1, - "max": constants.MAX_VLAN_TAG - 1}) - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - profiles = _get_network_profiles( - db_session=context.session, - physical_network=net_p["physical_network"] - ) - elif segment_type in [c_const.NETWORK_TYPE_OVERLAY, - c_const.NETWORK_TYPE_MULTI_SEGMENT, - c_const.NETWORK_TYPE_TRUNK]: - if (seg_min > seg_max or - seg_min < c_const.NEXUS_VXLAN_MIN or - seg_max > c_const.NEXUS_VXLAN_MAX): - msg = (_("segment range is invalid. Valid range is : " - "%(min)s-%(max)s") % - {"min": c_const.NEXUS_VXLAN_MIN, - "max": c_const.NEXUS_VXLAN_MAX}) - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - profiles = _get_network_profiles(db_session=context.session) - if profiles: - for profile in profiles: - if id and profile.id == id: - continue - name = profile.name - segment_range = profile.segment_range - if net_p["name"] == name: - msg = (_("NetworkProfile name %s already exists"), - net_p["name"]) - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - if (c_const.NETWORK_TYPE_MULTI_SEGMENT in - [profile.segment_type, net_p["segment_type"]] or - c_const.NETWORK_TYPE_TRUNK in - [profile.segment_type, net_p["segment_type"]]): - continue - seg_min, seg_max = self._get_segment_range( - net_p["segment_range"]) - profile_seg_min, profile_seg_max = self._get_segment_range( - segment_range) - if ((profile_seg_min <= seg_min <= profile_seg_max) or - (profile_seg_min <= seg_max <= profile_seg_max) or - ((seg_min <= profile_seg_min) and - (seg_max >= profile_seg_max))): - msg = _("Segment range overlaps with another profile") - LOG.error(msg) - raise n_exc.InvalidInput(error_message=msg) - - def _get_network_profile_by_name(self, db_session, name): - """ - Retrieve network profile based on name. - - :param db_session: database session - :param name: string representing the name for the network profile - :returns: network profile object - """ - with db_session.begin(subtransactions=True): - try: - return (db_session.query(n1kv_models_v2.NetworkProfile). - filter_by(name=name).one()) - except exc.NoResultFound: - raise c_exc.NetworkProfileNotFound(profile=name) - - -class PolicyProfile_db_mixin(object): - - """Policy Profile Mixin.""" - - def _get_policy_collection_for_tenant(self, db_session, model, tenant_id): - profile_ids = (db_session.query(n1kv_models_v2. - ProfileBinding.profile_id) - .filter_by(tenant_id=tenant_id). - filter_by(profile_type=c_const.POLICY).all()) - profiles = db_session.query(model).filter(model.id.in_( - pid[0] for pid in profile_ids)) - return [self._make_policy_profile_dict(p) for p in profiles] - - def _make_policy_profile_dict(self, policy_profile, fields=None): - res = {"id": policy_profile["id"], "name": policy_profile["name"]} - return self._fields(res, fields) - - def _make_profile_bindings_dict(self, profile_binding, fields=None): - res = {"profile_id": profile_binding["profile_id"], - "tenant_id": profile_binding["tenant_id"]} - return self._fields(res, fields) - - def _policy_profile_exists(self, id): - db_session = db.get_session() - return (db_session.query(n1kv_models_v2.PolicyProfile). - filter_by(id=id).first()) - - def get_policy_profile(self, context, id, fields=None): - """ - Retrieve a policy profile for the given UUID. - - :param context: neutron api request context - :param id: UUID representing policy profile to fetch - :params fields: a list of strings that are valid keys in a policy - profile dictionary. Only these fields will be returned - :returns: policy profile dictionary - """ - profile = get_policy_profile(context.session, id) - return self._make_policy_profile_dict(profile, fields) - - def get_policy_profiles(self, context, filters=None, fields=None): - """ - Retrieve a list of policy profiles. - - Retrieve all policy profiles if tenant is admin. For a non-admin - tenant, retrieve all policy profiles belonging to this tenant only. - :param context: neutron api request context - :param filters: a dictionary with keys that are valid keys for a - policy profile object. Values in this dictiontary are - an iterable containing values that will be used for an - exact match comparison for that value. Each result - returned by this function will have matched one of the - values for each key in filters - :params fields: a list of strings that are valid keys in a policy - profile dictionary. Only these fields will be returned - :returns: list of all policy profiles - """ - if context.is_admin: - return self._get_collection(context, n1kv_models_v2.PolicyProfile, - self._make_policy_profile_dict, - filters=filters, fields=fields) - else: - return self._get_policy_collection_for_tenant(context.session, - n1kv_models_v2. - PolicyProfile, - context.tenant_id) - - def get_policy_profile_bindings(self, context, filters=None, fields=None): - """ - Retrieve a list of profile bindings for policy profiles. - - :param context: neutron api request context - :param filters: a dictionary with keys that are valid keys for a - profile bindings object. Values in this dictiontary are - an iterable containing values that will be used for an - exact match comparison for that value. Each result - returned by this function will have matched one of the - values for each key in filters - :params fields: a list of strings that are valid keys in a profile - bindings dictionary. Only these fields will be returned - :returns: list of profile bindings - """ - if context.is_admin: - profile_bindings = _get_profile_bindings( - context.session, - profile_type=c_const.POLICY) - return [self._make_profile_bindings_dict(pb) - for pb in profile_bindings] - - def update_policy_profile(self, context, id, policy_profile): - """ - Update a policy profile. - - Add/remove policy profile to tenant-id binding for the corresponding - option and if user is admin. - :param context: neutron api request context - :param id: UUID representing policy profile to update - :param policy_profile: policy profile dictionary - :returns: updated policy profile dictionary - """ - p = policy_profile["policy_profile"] - if context.is_admin and "add_tenant" in p: - self.add_policy_profile_tenant(context.session, - id, - p["add_tenant"]) - return self._make_policy_profile_dict(get_policy_profile( - context.session, id)) - if context.is_admin and "remove_tenant" in p: - delete_profile_binding(context.session, p["remove_tenant"], id) - return self._make_policy_profile_dict(get_policy_profile( - context.session, id)) - return self._make_policy_profile_dict( - update_policy_profile(context.session, id, p)) - - def add_policy_profile_tenant(self, - db_session, - policy_profile_id, - tenant_id): - """ - Add a tenant to a policy profile binding. - - :param db_session: database session - :param policy_profile_id: UUID representing policy profile - :param tenant_id: UUID representing the tenant - :returns: profile binding object - """ - return create_profile_binding(db_session, - tenant_id, - policy_profile_id, - c_const.POLICY) - - def remove_policy_profile_tenant(self, policy_profile_id, tenant_id): - """ - Remove a tenant to a policy profile binding. - - :param policy_profile_id: UUID representing policy profile - :param tenant_id: UUID representing the tenant - """ - delete_profile_binding(None, tenant_id, policy_profile_id) - - def _delete_policy_profile(self, policy_profile_id): - """Delete policy profile and associated binding.""" - db_session = db.get_session() - with db_session.begin(subtransactions=True): - (db_session.query(n1kv_models_v2.PolicyProfile). - filter_by(id=policy_profile_id).delete()) - - def _get_policy_profile_by_name(self, name): - """ - Retrieve policy profile based on name. - - :param name: string representing the name for the policy profile - :returns: policy profile object - """ - db_session = db.get_session() - with db_session.begin(subtransactions=True): - return (db_session.query(n1kv_models_v2.PolicyProfile). - filter_by(name=name).one()) - - def _remove_all_fake_policy_profiles(self): - """ - Remove all policy profiles associated with fake tenant id. - - This will find all Profile ID where tenant is not set yet - set A - and profiles where tenant was already set - set B - and remove what is in both and no tenant id set - """ - db_session = db.get_session() - with db_session.begin(subtransactions=True): - a_set_q = (db_session.query(n1kv_models_v2.ProfileBinding). - filter_by(tenant_id=c_const.TENANT_ID_NOT_SET, - profile_type=c_const.POLICY)) - a_set = set(i.profile_id for i in a_set_q) - b_set_q = (db_session.query(n1kv_models_v2.ProfileBinding). - filter(sql.and_(n1kv_models_v2.ProfileBinding. - tenant_id != c_const.TENANT_ID_NOT_SET, - n1kv_models_v2.ProfileBinding. - profile_type == c_const.POLICY))) - b_set = set(i.profile_id for i in b_set_q) - (db_session.query(n1kv_models_v2.ProfileBinding). - filter(sql.and_(n1kv_models_v2.ProfileBinding.profile_id. - in_(a_set & b_set), - n1kv_models_v2.ProfileBinding.tenant_id == - c_const.TENANT_ID_NOT_SET)). - delete(synchronize_session="fetch")) - - def _add_policy_profile(self, - policy_profile_name, - policy_profile_id, - tenant_id=None): - """ - Add Policy profile and tenant binding. - - :param policy_profile_name: string representing the name for the - policy profile - :param policy_profile_id: UUID representing the policy profile - :param tenant_id: UUID representing the tenant - """ - policy_profile = {"id": policy_profile_id, "name": policy_profile_name} - tenant_id = tenant_id or c_const.TENANT_ID_NOT_SET - if not self._policy_profile_exists(policy_profile_id): - create_policy_profile(policy_profile) - create_profile_binding(None, - tenant_id, - policy_profile["id"], - c_const.POLICY) diff --git a/neutron/plugins/cisco/db/n1kv_models_v2.py b/neutron/plugins/cisco/db/n1kv_models_v2.py deleted file mode 100644 index 6c81aabba..000000000 --- a/neutron/plugins/cisco/db/n1kv_models_v2.py +++ /dev/null @@ -1,185 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Abhishek Raut, Cisco Systems Inc. -# @author: Rudrajit Tapadar, Cisco Systems Inc. - -import sqlalchemy as sa - -from neutron.db import model_base -from neutron.db import models_v2 -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_constants - - -LOG = logging.getLogger(__name__) - - -class N1kvVlanAllocation(model_base.BASEV2): - - """Represents allocation state of vlan_id on physical network.""" - __tablename__ = 'cisco_n1kv_vlan_allocations' - - physical_network = sa.Column(sa.String(64), - nullable=False, - primary_key=True) - vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True, - autoincrement=False) - allocated = sa.Column(sa.Boolean, nullable=False, default=False) - network_profile_id = sa.Column(sa.String(36), - sa.ForeignKey('cisco_network_profiles.id', - ondelete="CASCADE"), - nullable=False) - - -class N1kvVxlanAllocation(model_base.BASEV2): - - """Represents allocation state of vxlan_id.""" - __tablename__ = 'cisco_n1kv_vxlan_allocations' - - vxlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True, - autoincrement=False) - allocated = sa.Column(sa.Boolean, nullable=False, default=False) - network_profile_id = sa.Column(sa.String(36), - sa.ForeignKey('cisco_network_profiles.id', - ondelete="CASCADE"), - nullable=False) - - -class N1kvPortBinding(model_base.BASEV2): - - """Represents binding of ports to policy profile.""" - __tablename__ = 'cisco_n1kv_port_bindings' - - port_id = sa.Column(sa.String(36), - sa.ForeignKey('ports.id', ondelete="CASCADE"), - primary_key=True) - profile_id = sa.Column(sa.String(36), - sa.ForeignKey('cisco_policy_profiles.id')) - - -class N1kvNetworkBinding(model_base.BASEV2): - - """Represents binding of virtual network to physical realization.""" - __tablename__ = 'cisco_n1kv_network_bindings' - - network_id = sa.Column(sa.String(36), - sa.ForeignKey('networks.id', ondelete="CASCADE"), - primary_key=True) - network_type = sa.Column(sa.String(32), nullable=False) - physical_network = sa.Column(sa.String(64)) - segmentation_id = sa.Column(sa.Integer) - multicast_ip = sa.Column(sa.String(32)) - profile_id = sa.Column(sa.String(36), - sa.ForeignKey('cisco_network_profiles.id')) - - -class N1kVmNetwork(model_base.BASEV2): - - """Represents VM Network information.""" - __tablename__ = 'cisco_n1kv_vmnetworks' - - name = sa.Column(sa.String(80), primary_key=True) - profile_id = sa.Column(sa.String(36), - sa.ForeignKey('cisco_policy_profiles.id')) - network_id = sa.Column(sa.String(36)) - port_count = sa.Column(sa.Integer) - - -class NetworkProfile(model_base.BASEV2, models_v2.HasId): - - """ - Nexus1000V Network Profiles - - segment_type - VLAN, OVERLAY, TRUNK, MULTI_SEGMENT - sub_type - TRUNK_VLAN, TRUNK_VXLAN, native_vxlan, enhanced_vxlan - segment_range - '-' - multicast_ip_index - - multicast_ip_range - '-' - physical_network - Name for the physical network - """ - __tablename__ = 'cisco_network_profiles' - - name = sa.Column(sa.String(255)) - segment_type = sa.Column(sa.Enum(cisco_constants.NETWORK_TYPE_VLAN, - cisco_constants.NETWORK_TYPE_OVERLAY, - cisco_constants.NETWORK_TYPE_TRUNK, - cisco_constants. - NETWORK_TYPE_MULTI_SEGMENT, - name='segment_type'), - nullable=False) - sub_type = sa.Column(sa.String(255)) - segment_range = sa.Column(sa.String(255)) - multicast_ip_index = sa.Column(sa.Integer, default=0) - multicast_ip_range = sa.Column(sa.String(255)) - physical_network = sa.Column(sa.String(255)) - - -class PolicyProfile(model_base.BASEV2): - - """ - Nexus1000V Network Profiles - - Both 'id' and 'name' are coming from Nexus1000V switch - """ - __tablename__ = 'cisco_policy_profiles' - - id = sa.Column(sa.String(36), primary_key=True) - name = sa.Column(sa.String(255)) - - -class ProfileBinding(model_base.BASEV2): - - """ - Represents a binding of Network Profile - or Policy Profile to tenant_id - """ - __tablename__ = 'cisco_n1kv_profile_bindings' - - profile_type = sa.Column(sa.Enum(cisco_constants.NETWORK, - cisco_constants.POLICY, - name='profile_type')) - tenant_id = sa.Column(sa.String(36), - primary_key=True, - default=cisco_constants.TENANT_ID_NOT_SET) - profile_id = sa.Column(sa.String(36), primary_key=True) - - -class N1kvTrunkSegmentBinding(model_base.BASEV2): - - """Represents binding of segments in trunk networks.""" - __tablename__ = 'cisco_n1kv_trunk_segments' - - trunk_segment_id = sa.Column(sa.String(36), - sa.ForeignKey('networks.id', - ondelete="CASCADE"), - primary_key=True) - segment_id = sa.Column(sa.String(36), nullable=False, primary_key=True) - dot1qtag = sa.Column(sa.String(36), nullable=False, primary_key=True) - - -class N1kvMultiSegmentNetworkBinding(model_base.BASEV2): - - """Represents binding of segments in multi-segment networks.""" - __tablename__ = 'cisco_n1kv_multi_segments' - - multi_segment_id = sa.Column(sa.String(36), - sa.ForeignKey('networks.id', - ondelete="CASCADE"), - primary_key=True) - segment1_id = sa.Column(sa.String(36), nullable=False, primary_key=True) - segment2_id = sa.Column(sa.String(36), nullable=False, primary_key=True) - encap_profile_name = sa.Column(sa.String(36)) diff --git a/neutron/plugins/cisco/db/network_db_v2.py b/neutron/plugins/cisco/db/network_db_v2.py deleted file mode 100644 index 94c5a37de..000000000 --- a/neutron/plugins/cisco/db/network_db_v2.py +++ /dev/null @@ -1,290 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012, Cisco Systems, 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. -# -# @author: Rohit Agarwalla, Cisco Systems, Inc. - -from sqlalchemy.orm import exc - -from neutron.db import api as db -from neutron.openstack.common import log as logging -from neutron.openstack.common import uuidutils -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_exceptions as c_exc -from neutron.plugins.cisco.db import network_models_v2 -# Do NOT remove this import. It is required for all the models to be seen -# by db.initialize() when called from VirtualPhysicalSwitchModelV2.__init__. -from neutron.plugins.cisco.db import nexus_models_v2 # noqa -from neutron.plugins.openvswitch import ovs_models_v2 - - -LOG = logging.getLogger(__name__) - - -def get_all_qoss(tenant_id): - """Lists all the qos to tenant associations.""" - LOG.debug(_("get_all_qoss() called")) - session = db.get_session() - return (session.query(network_models_v2.QoS). - filter_by(tenant_id=tenant_id).all()) - - -def get_qos(tenant_id, qos_id): - """Lists the qos given a tenant_id and qos_id.""" - LOG.debug(_("get_qos() called")) - session = db.get_session() - try: - return (session.query(network_models_v2.QoS). - filter_by(tenant_id=tenant_id). - filter_by(qos_id=qos_id).one()) - except exc.NoResultFound: - raise c_exc.QosNotFound(qos_id=qos_id, - tenant_id=tenant_id) - - -def add_qos(tenant_id, qos_name, qos_desc): - """Adds a qos to tenant association.""" - LOG.debug(_("add_qos() called")) - session = db.get_session() - try: - qos = (session.query(network_models_v2.QoS). - filter_by(tenant_id=tenant_id). - filter_by(qos_name=qos_name).one()) - raise c_exc.QosNameAlreadyExists(qos_name=qos_name, - tenant_id=tenant_id) - except exc.NoResultFound: - qos = network_models_v2.QoS(qos_id=uuidutils.generate_uuid(), - tenant_id=tenant_id, - qos_name=qos_name, - qos_desc=qos_desc) - session.add(qos) - session.flush() - return qos - - -def remove_qos(tenant_id, qos_id): - """Removes a qos to tenant association.""" - session = db.get_session() - try: - qos = (session.query(network_models_v2.QoS). - filter_by(tenant_id=tenant_id). - filter_by(qos_id=qos_id).one()) - session.delete(qos) - session.flush() - return qos - except exc.NoResultFound: - pass - - -def update_qos(tenant_id, qos_id, new_qos_name=None): - """Updates a qos to tenant association.""" - session = db.get_session() - try: - qos = (session.query(network_models_v2.QoS). - filter_by(tenant_id=tenant_id). - filter_by(qos_id=qos_id).one()) - if new_qos_name: - qos["qos_name"] = new_qos_name - session.merge(qos) - session.flush() - return qos - except exc.NoResultFound: - raise c_exc.QosNotFound(qos_id=qos_id, - tenant_id=tenant_id) - - -def get_all_credentials(): - """Lists all the creds for a tenant.""" - session = db.get_session() - return (session.query(network_models_v2.Credential).all()) - - -def get_credential(credential_id): - """Lists the creds for given a cred_id.""" - session = db.get_session() - try: - return (session.query(network_models_v2.Credential). - filter_by(credential_id=credential_id).one()) - except exc.NoResultFound: - raise c_exc.CredentialNotFound(credential_id=credential_id) - - -def get_credential_name(credential_name): - """Lists the creds for given a cred_name.""" - session = db.get_session() - try: - return (session.query(network_models_v2.Credential). - filter_by(credential_name=credential_name).one()) - except exc.NoResultFound: - raise c_exc.CredentialNameNotFound(credential_name=credential_name) - - -def add_credential(credential_name, user_name, password, type): - """Create a credential.""" - session = db.get_session() - try: - cred = (session.query(network_models_v2.Credential). - filter_by(credential_name=credential_name).one()) - raise c_exc.CredentialAlreadyExists(credential_name=credential_name) - except exc.NoResultFound: - cred = network_models_v2.Credential( - credential_id=uuidutils.generate_uuid(), - credential_name=credential_name, - user_name=user_name, - password=password, - type=type) - session.add(cred) - session.flush() - return cred - - -def remove_credential(credential_id): - """Removes a credential.""" - session = db.get_session() - try: - cred = (session.query(network_models_v2.Credential). - filter_by(credential_id=credential_id).one()) - session.delete(cred) - session.flush() - return cred - except exc.NoResultFound: - pass - - -def update_credential(credential_id, - new_user_name=None, new_password=None): - """Updates a credential for a tenant.""" - session = db.get_session() - try: - cred = (session.query(network_models_v2.Credential). - filter_by(credential_id=credential_id).one()) - if new_user_name: - cred["user_name"] = new_user_name - if new_password: - cred["password"] = new_password - session.merge(cred) - session.flush() - return cred - except exc.NoResultFound: - raise c_exc.CredentialNotFound(credential_id=credential_id) - - -def get_all_n1kv_credentials(): - session = db.get_session() - return (session.query(network_models_v2.Credential). - filter_by(type='n1kv')) - - -def add_provider_network(network_id, network_type, segmentation_id): - """Add a network to the provider network table.""" - session = db.get_session() - if session.query(network_models_v2.ProviderNetwork).filter_by( - network_id=network_id).first(): - raise c_exc.ProviderNetworkExists(network_id) - pnet = network_models_v2.ProviderNetwork(network_id=network_id, - network_type=network_type, - segmentation_id=segmentation_id) - session.add(pnet) - session.flush() - - -def remove_provider_network(network_id): - """Remove network_id from the provider network table. - - :param network_id: Any network id. If it is not in the table, do nothing. - :return: network_id if it was in the table and successfully removed. - """ - session = db.get_session() - pnet = (session.query(network_models_v2.ProviderNetwork). - filter_by(network_id=network_id).first()) - if pnet: - session.delete(pnet) - session.flush() - return network_id - - -def is_provider_network(network_id): - """Return True if network_id is in the provider network table.""" - session = db.get_session() - if session.query(network_models_v2.ProviderNetwork).filter_by( - network_id=network_id).first(): - return True - - -def is_provider_vlan(vlan_id): - """Check for a for a vlan provider network with the specified vland_id. - - Returns True if the provider network table contains a vlan network - with the specified vlan_id. - """ - session = db.get_session() - if (session.query(network_models_v2.ProviderNetwork). - filter_by(network_type=const.NETWORK_TYPE_VLAN, - segmentation_id=vlan_id).first()): - return True - - -def get_ovs_vlans(): - session = db.get_session() - bindings = (session.query(ovs_models_v2.VlanAllocation.vlan_id). - filter_by(allocated=True)) - return [binding.vlan_id for binding in bindings] - - -class Credential_db_mixin(object): - - """Mixin class for Cisco Credentials as a resource.""" - - def _make_credential_dict(self, credential, fields=None): - res = {'credential_id': credential['credential_id'], - 'credential_name': credential['credential_name'], - 'user_name': credential['user_name'], - 'password': credential['password'], - 'type': credential['type']} - return self._fields(res, fields) - - def create_credential(self, context, credential): - """Create a credential.""" - c = credential['credential'] - cred = add_credential(c['credential_name'], - c['user_name'], - c['password'], - c['type']) - return self._make_credential_dict(cred) - - def get_credentials(self, context, filters=None, fields=None): - """Retrieve a list of credentials.""" - return self._get_collection(context, - network_models_v2.Credential, - self._make_credential_dict, - filters=filters, - fields=fields) - - def get_credential(self, context, id, fields=None): - """Retireve the requested credential based on its id.""" - credential = get_credential(id) - return self._make_credential_dict(credential, fields) - - def update_credential(self, context, id, credential): - """Update a credential based on its id.""" - c = credential['credential'] - cred = update_credential(id, - c['user_name'], - c['password']) - return self._make_credential_dict(cred) - - def delete_credential(self, context, id): - """Delete a credential based on its id.""" - return remove_credential(id) diff --git a/neutron/plugins/cisco/db/network_models_v2.py b/neutron/plugins/cisco/db/network_models_v2.py deleted file mode 100644 index 49768371d..000000000 --- a/neutron/plugins/cisco/db/network_models_v2.py +++ /dev/null @@ -1,56 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012, Cisco Systems, 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. -# -# @author: Rohit Agarwalla, Cisco Systems, Inc. - -import sqlalchemy as sa - -from neutron.db import model_base - - -class QoS(model_base.BASEV2): - """Represents QoS policies for a tenant.""" - - __tablename__ = 'cisco_qos_policies' - - qos_id = sa.Column(sa.String(255)) - tenant_id = sa.Column(sa.String(255), primary_key=True) - qos_name = sa.Column(sa.String(255), primary_key=True) - qos_desc = sa.Column(sa.String(255)) - - -class Credential(model_base.BASEV2): - """Represents credentials for a tenant to control Cisco switches.""" - - __tablename__ = 'cisco_credentials' - - credential_id = sa.Column(sa.String(255)) - credential_name = sa.Column(sa.String(255), primary_key=True) - user_name = sa.Column(sa.String(255)) - password = sa.Column(sa.String(255)) - type = sa.Column(sa.String(255)) - - -class ProviderNetwork(model_base.BASEV2): - """Represents networks that were created as provider networks.""" - - __tablename__ = 'cisco_provider_networks' - - network_id = sa.Column(sa.String(36), - sa.ForeignKey('networks.id', ondelete="CASCADE"), - primary_key=True) - network_type = sa.Column(sa.String(255), nullable=False) - segmentation_id = sa.Column(sa.Integer, nullable=False) diff --git a/neutron/plugins/cisco/db/nexus_db_v2.py b/neutron/plugins/cisco/db/nexus_db_v2.py deleted file mode 100644 index a11a8a041..000000000 --- a/neutron/plugins/cisco/db/nexus_db_v2.py +++ /dev/null @@ -1,154 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012, Cisco Systems, 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. -# -# @author: Rohit Agarwalla, Cisco Systems, Inc. -# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com) -# - -import sqlalchemy.orm.exc as sa_exc - -import neutron.db.api as db -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_exceptions as c_exc -from neutron.plugins.cisco.db import nexus_models_v2 - - -LOG = logging.getLogger(__name__) - - -def get_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): - """Lists a nexusport binding.""" - LOG.debug(_("get_nexusport_binding() called")) - return _lookup_all_nexus_bindings(port_id=port_id, - vlan_id=vlan_id, - switch_ip=switch_ip, - instance_id=instance_id) - - -def get_nexusvlan_binding(vlan_id, switch_ip): - """Lists a vlan and switch binding.""" - LOG.debug(_("get_nexusvlan_binding() called")) - return _lookup_all_nexus_bindings(vlan_id=vlan_id, switch_ip=switch_ip) - - -def add_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): - """Adds a nexusport binding.""" - LOG.debug(_("add_nexusport_binding() called")) - session = db.get_session() - binding = nexus_models_v2.NexusPortBinding(port_id=port_id, - vlan_id=vlan_id, - switch_ip=switch_ip, - instance_id=instance_id) - session.add(binding) - session.flush() - return binding - - -def remove_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): - """Removes a nexusport binding.""" - LOG.debug(_("remove_nexusport_binding() called")) - session = db.get_session() - binding = _lookup_all_nexus_bindings(session=session, - vlan_id=vlan_id, - switch_ip=switch_ip, - port_id=port_id, - instance_id=instance_id) - for bind in binding: - session.delete(bind) - session.flush() - return binding - - -def update_nexusport_binding(port_id, new_vlan_id): - """Updates nexusport binding.""" - if not new_vlan_id: - LOG.warning(_("update_nexusport_binding called with no vlan")) - return - LOG.debug(_("update_nexusport_binding called")) - session = db.get_session() - binding = _lookup_one_nexus_binding(session=session, port_id=port_id) - binding.vlan_id = new_vlan_id - session.merge(binding) - session.flush() - return binding - - -def get_nexusvm_bindings(vlan_id, instance_id): - """Lists nexusvm bindings.""" - LOG.debug(_("get_nexusvm_binding() called")) - - return _lookup_all_nexus_bindings(vlan_id=vlan_id, - instance_id=instance_id) - - -def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip): - """Lists nexusvm bindings.""" - LOG.debug(_("get_port_vlan_switch_binding() called")) - return _lookup_all_nexus_bindings(port_id=port_id, - switch_ip=switch_ip, - vlan_id=vlan_id) - - -def get_port_switch_bindings(port_id, switch_ip): - """List all vm/vlan bindings on a Nexus switch port.""" - LOG.debug(_("get_port_switch_bindings() called, " - "port:'%(port_id)s', switch:'%(switch_ip)s'"), - {'port_id': port_id, 'switch_ip': switch_ip}) - try: - return _lookup_all_nexus_bindings(port_id=port_id, - switch_ip=switch_ip) - except c_exc.NexusPortBindingNotFound: - pass - - -def get_nexussvi_bindings(): - """Lists nexus svi bindings.""" - LOG.debug(_("get_nexussvi_bindings() called")) - return _lookup_all_nexus_bindings(port_id='router') - - -def _lookup_nexus_bindings(query_type, session=None, **bfilter): - """Look up 'query_type' Nexus bindings matching the filter. - - :param query_type: 'all', 'one' or 'first' - :param session: db session - :param bfilter: filter for bindings query - :return: bindings if query gave a result, else - raise NexusPortBindingNotFound. - """ - if session is None: - session = db.get_session() - query_method = getattr(session.query( - nexus_models_v2.NexusPortBinding).filter_by(**bfilter), query_type) - try: - bindings = query_method() - if bindings: - return bindings - except sa_exc.NoResultFound: - pass - raise c_exc.NexusPortBindingNotFound(**bfilter) - - -def _lookup_all_nexus_bindings(session=None, **bfilter): - return _lookup_nexus_bindings('all', session, **bfilter) - - -def _lookup_one_nexus_binding(session=None, **bfilter): - return _lookup_nexus_bindings('one', session, **bfilter) - - -def _lookup_first_nexus_binding(session=None, **bfilter): - return _lookup_nexus_bindings('first', session, **bfilter) diff --git a/neutron/plugins/cisco/db/nexus_models_v2.py b/neutron/plugins/cisco/db/nexus_models_v2.py deleted file mode 100644 index e639e47c6..000000000 --- a/neutron/plugins/cisco/db/nexus_models_v2.py +++ /dev/null @@ -1,46 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012, Cisco Systems, 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. -# @author: Rohit Agarwalla, Cisco Systems, Inc. - -import sqlalchemy as sa - -from neutron.db import model_base - - -class NexusPortBinding(model_base.BASEV2): - """Represents a binding of VM's to nexus ports.""" - - __tablename__ = "cisco_nexusport_bindings" - - id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) - port_id = sa.Column(sa.String(255)) - vlan_id = sa.Column(sa.Integer, nullable=False) - switch_ip = sa.Column(sa.String(255), nullable=False) - instance_id = sa.Column(sa.String(255), nullable=False) - - def __repr__(self): - """Just the binding, without the id key.""" - return ("" % - (self.port_id, self.vlan_id, self.switch_ip, self.instance_id)) - - def __eq__(self, other): - """Compare only the binding, without the id key.""" - return ( - self.port_id == other.port_id and - self.vlan_id == other.vlan_id and - self.switch_ip == other.switch_ip and - self.instance_id == other.instance_id - ) diff --git a/neutron/plugins/cisco/extensions/__init__.py b/neutron/plugins/cisco/extensions/__init__.py deleted file mode 100644 index 63082aba2..000000000 --- a/neutron/plugins/cisco/extensions/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# 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. diff --git a/neutron/plugins/cisco/extensions/_credential_view.py b/neutron/plugins/cisco/extensions/_credential_view.py deleted file mode 100644 index 9dcbbc81e..000000000 --- a/neutron/plugins/cisco/extensions/_credential_view.py +++ /dev/null @@ -1,52 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Ying Liu, Cisco Systems, Inc. -# - - -def get_view_builder(req): - base_url = req.application_url - return ViewBuilder(base_url) - - -class ViewBuilder(object): - """ViewBuilder for Credential, derived from neutron.views.networks.""" - - def __init__(self, base_url): - """Initialize builder. - - :param base_url: url of the root wsgi application - """ - self.base_url = base_url - - def build(self, credential_data, is_detail=False): - """Generic method used to generate a credential entity.""" - if is_detail: - credential = self._build_detail(credential_data) - else: - credential = self._build_simple(credential_data) - return credential - - def _build_simple(self, credential_data): - """Return a simple description of credential.""" - return dict(credential=dict(id=credential_data['credential_id'])) - - def _build_detail(self, credential_data): - """Return a detailed description of credential.""" - return dict(credential=dict(id=credential_data['credential_id'], - name=credential_data['user_name'], - password=credential_data['password'])) diff --git a/neutron/plugins/cisco/extensions/_qos_view.py b/neutron/plugins/cisco/extensions/_qos_view.py deleted file mode 100644 index 81ef5fef6..000000000 --- a/neutron/plugins/cisco/extensions/_qos_view.py +++ /dev/null @@ -1,52 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Ying Liu, Cisco Systems, Inc. -# - - -def get_view_builder(req): - base_url = req.application_url - return ViewBuilder(base_url) - - -class ViewBuilder(object): - """ViewBuilder for QoS, derived from neutron.views.networks.""" - - def __init__(self, base_url): - """Initialize builder. - - :param base_url: url of the root wsgi application - """ - self.base_url = base_url - - def build(self, qos_data, is_detail=False): - """Generic method used to generate a QoS entity.""" - if is_detail: - qos = self._build_detail(qos_data) - else: - qos = self._build_simple(qos_data) - return qos - - def _build_simple(self, qos_data): - """Return a simple description of qos.""" - return dict(qos=dict(id=qos_data['qos_id'])) - - def _build_detail(self, qos_data): - """Return a detailed description of qos.""" - return dict(qos=dict(id=qos_data['qos_id'], - name=qos_data['qos_name'], - description=qos_data['qos_desc'])) diff --git a/neutron/plugins/cisco/extensions/credential.py b/neutron/plugins/cisco/extensions/credential.py deleted file mode 100644 index 8838136c1..000000000 --- a/neutron/plugins/cisco/extensions/credential.py +++ /dev/null @@ -1,84 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 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. -# -# @author: Ying Liu, Cisco Systems, Inc. -# @author: Abhishek Raut, Cisco Systems, Inc - -from neutron.api import extensions -from neutron.api.v2 import attributes -from neutron.api.v2 import base -from neutron import manager - - -# Attribute Map -RESOURCE_ATTRIBUTE_MAP = { - 'credentials': { - 'credential_id': {'allow_post': False, 'allow_put': False, - 'validate': {'type:regex': attributes.UUID_PATTERN}, - 'is_visible': True}, - 'credential_name': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': ''}, - 'tenant_id': {'allow_post': True, 'allow_put': False, - 'is_visible': False, 'default': ''}, - 'type': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': ''}, - 'user_name': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': ''}, - 'password': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': ''}, - }, -} - - -class Credential(extensions.ExtensionDescriptor): - - @classmethod - def get_name(cls): - """Returns Extended Resource Name.""" - return "Cisco Credential" - - @classmethod - def get_alias(cls): - """Returns Extended Resource Alias.""" - return "credential" - - @classmethod - def get_description(cls): - """Returns Extended Resource Description.""" - return "Credential include username and password" - - @classmethod - def get_namespace(cls): - """Returns Extended Resource Namespace.""" - return "http://docs.ciscocloud.com/api/ext/credential/v2.0" - - @classmethod - def get_updated(cls): - """Returns Extended Resource Update Time.""" - return "2011-07-25T13:25:27-06:00" - - @classmethod - def get_resources(cls): - """Returns Extended Resources.""" - resource_name = "credential" - collection_name = resource_name + "s" - plugin = manager.NeutronManager.get_plugin() - params = RESOURCE_ATTRIBUTE_MAP.get(collection_name, dict()) - controller = base.create_resource(collection_name, - resource_name, - plugin, params) - return [extensions.ResourceExtension(collection_name, - controller)] diff --git a/neutron/plugins/cisco/extensions/n1kv.py b/neutron/plugins/cisco/extensions/n1kv.py deleted file mode 100644 index 352ad816a..000000000 --- a/neutron/plugins/cisco/extensions/n1kv.py +++ /dev/null @@ -1,106 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Abhishek Raut, Cisco Systems, Inc. -# @author: Rudrajit Tapadar, Cisco Systems, Inc. -# @author: Aruna Kushwaha, Cisco Systems, Inc. -# @author: Sergey Sudakovich, Cisco Systems, Inc. - -from neutron.api import extensions -from neutron.api.v2 import attributes - - -PROFILE_ID = 'n1kv:profile_id' -MULTICAST_IP = 'n1kv:multicast_ip' -SEGMENT_ADD = 'n1kv:segment_add' -SEGMENT_DEL = 'n1kv:segment_del' -MEMBER_SEGMENTS = 'n1kv:member_segments' - -EXTENDED_ATTRIBUTES_2_0 = { - 'networks': { - PROFILE_ID: {'allow_post': True, 'allow_put': False, - 'validate': {'type:regex': attributes.UUID_PATTERN}, - 'default': attributes.ATTR_NOT_SPECIFIED, - 'is_visible': True}, - MULTICAST_IP: {'allow_post': True, 'allow_put': True, - 'default': attributes.ATTR_NOT_SPECIFIED, - 'is_visible': True}, - SEGMENT_ADD: {'allow_post': True, 'allow_put': True, - 'default': attributes.ATTR_NOT_SPECIFIED, - 'is_visible': True}, - SEGMENT_DEL: {'allow_post': True, 'allow_put': True, - 'default': attributes.ATTR_NOT_SPECIFIED, - 'is_visible': True}, - MEMBER_SEGMENTS: {'allow_post': True, 'allow_put': True, - 'default': attributes.ATTR_NOT_SPECIFIED, - 'is_visible': True}, - }, - 'ports': { - PROFILE_ID: {'allow_post': True, 'allow_put': False, - 'validate': {'type:regex': attributes.UUID_PATTERN}, - 'default': attributes.ATTR_NOT_SPECIFIED, - 'is_visible': True} - } -} - - -class N1kv(extensions.ExtensionDescriptor): - - """Extension class supporting N1kv profiles. - - This class is used by neutron's extension framework to make - metadata about the n1kv profile extension available to - clients. No new resources are defined by this extension. Instead, - the existing network resource's request and response messages are - extended with attributes in the n1kv profile namespace. - - To create a network based on n1kv profile using the CLI with admin rights: - - (shell) net-create --tenant_id \ - --n1kv:profile_id - (shell) port-create --tenant_id \ - --n1kv:profile_id - - - With admin rights, network dictionaries returned from CLI commands - will also include n1kv profile attributes. - """ - - @classmethod - def get_name(cls): - return "n1kv" - - @classmethod - def get_alias(cls): - return "n1kv" - - @classmethod - def get_description(cls): - return "Expose network profile" - - @classmethod - def get_namespace(cls): - return "http://docs.openstack.org/ext/n1kv/api/v2.0" - - @classmethod - def get_updated(cls): - return "2012-11-15T10:00:00-00:00" - - def get_extended_resources(self, version): - if version == "2.0": - return EXTENDED_ATTRIBUTES_2_0 - else: - return {} diff --git a/neutron/plugins/cisco/extensions/network_profile.py b/neutron/plugins/cisco/extensions/network_profile.py deleted file mode 100644 index bb05bd944..000000000 --- a/neutron/plugins/cisco/extensions/network_profile.py +++ /dev/null @@ -1,103 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Abhishek Raut, Cisco Systems, Inc. -# @author: Sergey Sudakovich, Cisco Systems, Inc. -# @author: Rudrajit Tapadar, Cisco Systems, Inc. - -from neutron.api import extensions -from neutron.api.v2 import attributes -from neutron.api.v2 import base -from neutron import manager - - -# Attribute Map -RESOURCE_ATTRIBUTE_MAP = { - 'network_profiles': { - 'id': {'allow_post': False, 'allow_put': False, - 'validate': {'type:regex': attributes.UUID_PATTERN}, - 'is_visible': True}, - 'name': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': ''}, - 'segment_type': {'allow_post': True, 'allow_put': False, - 'is_visible': True, 'default': ''}, - 'sub_type': {'allow_post': True, 'allow_put': False, - 'is_visible': True, - 'default': attributes.ATTR_NOT_SPECIFIED}, - 'segment_range': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': ''}, - 'multicast_ip_range': {'allow_post': True, 'allow_put': True, - 'is_visible': True, - 'default': attributes.ATTR_NOT_SPECIFIED}, - 'multicast_ip_index': {'allow_post': False, 'allow_put': False, - 'is_visible': False, 'default': '0'}, - 'physical_network': {'allow_post': True, 'allow_put': False, - 'is_visible': True, 'default': ''}, - 'tenant_id': {'allow_post': True, 'allow_put': False, - 'is_visible': False, 'default': ''}, - 'add_tenant': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': None}, - 'remove_tenant': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': None}, - }, - 'network_profile_bindings': { - 'profile_id': {'allow_post': False, 'allow_put': False, - 'validate': {'type:regex': attributes.UUID_PATTERN}, - 'is_visible': True}, - 'tenant_id': {'allow_post': True, 'allow_put': False, - 'is_visible': True}, - }, -} - - -class Network_profile(extensions.ExtensionDescriptor): - - @classmethod - def get_name(cls): - return "Cisco N1kv Network Profiles" - - @classmethod - def get_alias(cls): - return 'network_profile' - - @classmethod - def get_description(cls): - return ("Profile includes the type of profile for N1kv") - - @classmethod - def get_namespace(cls): - return "http://docs.openstack.org/ext/n1kv/network-profile/api/v2.0" - - @classmethod - def get_updated(cls): - return "2012-07-20T10:00:00-00:00" - - @classmethod - def get_resources(cls): - """Returns Extended Resources.""" - exts = [] - plugin = manager.NeutronManager.get_plugin() - for resource_name in ['network_profile', 'network_profile_binding']: - collection_name = resource_name + "s" - controller = base.create_resource( - collection_name, - resource_name, - plugin, - RESOURCE_ATTRIBUTE_MAP.get(collection_name)) - ex = extensions.ResourceExtension(collection_name, - controller) - exts.append(ex) - return exts diff --git a/neutron/plugins/cisco/extensions/policy_profile.py b/neutron/plugins/cisco/extensions/policy_profile.py deleted file mode 100644 index af3c25083..000000000 --- a/neutron/plugins/cisco/extensions/policy_profile.py +++ /dev/null @@ -1,85 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Abhishek Raut, Cisco Systems, Inc. -# @author: Sergey Sudakovich, Cisco Systems, Inc. - -from neutron.api import extensions -from neutron.api.v2 import attributes -from neutron.api.v2 import base -from neutron import manager - -# Attribute Map -RESOURCE_ATTRIBUTE_MAP = { - 'policy_profiles': { - 'id': {'allow_post': False, 'allow_put': False, - 'validate': {'type:regex': attributes.UUID_PATTERN}, - 'is_visible': True}, - 'name': {'allow_post': False, 'allow_put': False, - 'is_visible': True, 'default': ''}, - 'add_tenant': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': None}, - 'remove_tenant': {'allow_post': True, 'allow_put': True, - 'is_visible': True, 'default': None}, - }, - 'policy_profile_bindings': { - 'profile_id': {'allow_post': False, 'allow_put': False, - 'validate': {'type:regex': attributes.UUID_PATTERN}, - 'is_visible': True}, - 'tenant_id': {'allow_post': True, 'allow_put': False, - 'is_visible': True}, - }, -} - - -class Policy_profile(extensions.ExtensionDescriptor): - - @classmethod - def get_name(cls): - return "Cisco Nexus1000V Policy Profiles" - - @classmethod - def get_alias(cls): - return 'policy_profile' - - @classmethod - def get_description(cls): - return "Profile includes the type of profile for N1kv" - - @classmethod - def get_namespace(cls): - return "http://docs.openstack.org/ext/n1kv/policy-profile/api/v2.0" - - @classmethod - def get_updated(cls): - return "2012-07-20T10:00:00-00:00" - - @classmethod - def get_resources(cls): - """Returns Extended Resources.""" - exts = [] - plugin = manager.NeutronManager.get_plugin() - for resource_name in ['policy_profile', 'policy_profile_binding']: - collection_name = resource_name + "s" - controller = base.create_resource( - collection_name, - resource_name, - plugin, - RESOURCE_ATTRIBUTE_MAP.get(collection_name)) - ex = extensions.ResourceExtension(collection_name, - controller) - exts.append(ex) - return exts diff --git a/neutron/plugins/cisco/extensions/qos.py b/neutron/plugins/cisco/extensions/qos.py deleted file mode 100644 index 255601b5b..000000000 --- a/neutron/plugins/cisco/extensions/qos.py +++ /dev/null @@ -1,156 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 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. -# -# @author: Ying Liu, Cisco Systems, Inc. -# - -from webob import exc - -from neutron.api import api_common as common -from neutron.api import extensions -from neutron import manager -from neutron.plugins.cisco.common import cisco_exceptions as exception -from neutron.plugins.cisco.common import cisco_faults as faults -from neutron.plugins.cisco.extensions import _qos_view as qos_view -from neutron import wsgi - - -class Qos(extensions.ExtensionDescriptor): - """Qos extension file.""" - - @classmethod - def get_name(cls): - """Returns Ext Resource Name.""" - return "Cisco qos" - - @classmethod - def get_alias(cls): - """Returns Ext Resource Alias.""" - return "Cisco qos" - - @classmethod - def get_description(cls): - """Returns Ext Resource Description.""" - return "qos includes qos_name and qos_desc" - - @classmethod - def get_namespace(cls): - """Returns Ext Resource Namespace.""" - return "http://docs.ciscocloud.com/api/ext/qos/v1.0" - - @classmethod - def get_updated(cls): - """Returns Ext Resource update.""" - return "2011-07-25T13:25:27-06:00" - - @classmethod - def get_resources(cls): - """Returns Ext Resources.""" - parent_resource = dict(member_name="tenant", - collection_name="extensions/csco/tenants") - - controller = QosController(manager.NeutronManager.get_plugin()) - return [extensions.ResourceExtension('qoss', controller, - parent=parent_resource)] - - -class QosController(common.NeutronController, wsgi.Controller): - """qos API controller based on NeutronController.""" - - _qos_ops_param_list = [ - {'param-name': 'qos_name', 'required': True}, - {'param-name': 'qos_desc', 'required': True}, - ] - - _serialization_metadata = { - "application/xml": { - "attributes": { - "qos": ["id", "name"], - }, - }, - } - - def __init__(self, plugin): - self._resource_name = 'qos' - self._plugin = plugin - - def index(self, request, tenant_id): - """Returns a list of qos ids.""" - return self._items(request, tenant_id, is_detail=False) - - def _items(self, request, tenant_id, is_detail): - """Returns a list of qoss.""" - qoss = self._plugin.get_all_qoss(tenant_id) - builder = qos_view.get_view_builder(request) - result = [builder.build(qos, is_detail)['qos'] for qos in qoss] - return dict(qoss=result) - - # pylint: disable-msg=E1101 - def show(self, request, tenant_id, id): - """Returns qos details for the given qos id.""" - try: - qos = self._plugin.get_qos_details(tenant_id, id) - builder = qos_view.get_view_builder(request) - #build response with details - result = builder.build(qos, True) - return dict(qoss=result) - except exception.QosNotFound as exp: - return faults.Fault(faults.QosNotFound(exp)) - - def create(self, request, tenant_id): - """Creates a new qos for a given tenant.""" - #look for qos name in request - try: - body = self._deserialize(request.body, request.get_content_type()) - req_body = self._prepare_request_body(body, - self._qos_ops_param_list) - req_params = req_body[self._resource_name] - except exc.HTTPError as exp: - return faults.Fault(exp) - qos = self._plugin.create_qos(tenant_id, - req_params['qos_name'], - req_params['qos_desc']) - builder = qos_view.get_view_builder(request) - result = builder.build(qos) - return dict(qoss=result) - - def update(self, request, tenant_id, id): - """Updates the name for the qos with the given id.""" - try: - body = self._deserialize(request.body, request.get_content_type()) - req_body = self._prepare_request_body(body, - self._qos_ops_param_list) - req_params = req_body[self._resource_name] - except exc.HTTPError as exp: - return faults.Fault(exp) - try: - qos = self._plugin.rename_qos(tenant_id, id, - req_params['qos_name']) - - builder = qos_view.get_view_builder(request) - result = builder.build(qos, True) - return dict(qoss=result) - except exception.QosNotFound as exp: - return faults.Fault(faults.QosNotFound(exp)) - - def delete(self, request, tenant_id, id): - """Destroys the qos with the given id.""" - try: - self._plugin.delete_qos(tenant_id, id) - return exc.HTTPOk() - except exception.QosNotFound as exp: - return faults.Fault(faults.QosNotFound(exp)) diff --git a/neutron/plugins/cisco/l2device_plugin_base.py b/neutron/plugins/cisco/l2device_plugin_base.py deleted file mode 100644 index ef75e1188..000000000 --- a/neutron/plugins/cisco/l2device_plugin_base.py +++ /dev/null @@ -1,175 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. - -import abc -import inspect -import six - - -@six.add_metaclass(abc.ABCMeta) -class L2DevicePluginBase(object): - """Base class for a device-specific plugin. - - An example of a device-specific plugin is a Nexus switch plugin. - The network model relies on device-category-specific plugins to perform - the configuration on each device. - """ - - @abc.abstractmethod - def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, - **kwargs): - """Create network. - - :returns: - :raises: - """ - pass - - @abc.abstractmethod - def delete_network(self, tenant_id, net_id, **kwargs): - """Delete network. - - :returns: - :raises: - """ - pass - - @abc.abstractmethod - def update_network(self, tenant_id, net_id, name, **kwargs): - """Update network. - - :returns: - :raises: - """ - pass - - @abc.abstractmethod - def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): - """Create port. - - :returns: - :raises: - """ - pass - - @abc.abstractmethod - def delete_port(self, tenant_id, net_id, port_id, **kwargs): - """Delete port. - - :returns: - :raises: - """ - pass - - @abc.abstractmethod - def update_port(self, tenant_id, net_id, port_id, **kwargs): - """Update port. - - :returns: - :raises: - """ - pass - - @abc.abstractmethod - def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, - **kwargs): - """Plug interface. - - :returns: - :raises: - """ - pass - - @abc.abstractmethod - def unplug_interface(self, tenant_id, net_id, port_id, **kwargs): - """Unplug interface. - - :returns: - :raises: - """ - pass - - def create_subnet(self, tenant_id, net_id, ip_version, - subnet_cidr, **kwargs): - """Create subnet. - - :returns: - :raises: - """ - pass - - def get_subnets(self, tenant_id, net_id, **kwargs): - """Get subnets. - - :returns: - :raises: - """ - pass - - def get_subnet(self, tenant_id, net_id, subnet_id, **kwargs): - """Get subnet. - - :returns: - :raises: - """ - pass - - def update_subnet(self, tenant_id, net_id, subnet_id, **kwargs): - """Update subnet. - - :returns: - :raises: - """ - pass - - def delete_subnet(self, tenant_id, net_id, subnet_id, **kwargs): - """Delete subnet. - - :returns: - :raises: - """ - pass - - @classmethod - def __subclasshook__(cls, klass): - """Check plugin class. - - The __subclasshook__ method is a class method - that will be called every time a class is tested - using issubclass(klass, Plugin). - In that case, it will check that every method - marked with the abstractmethod decorator is - provided by the plugin class. - """ - if cls is L2DevicePluginBase: - for method in cls.__abstractmethods__: - method_ok = False - for base in klass.__mro__: - if method in base.__dict__: - fn_obj = base.__dict__[method] - if inspect.isfunction(fn_obj): - abstract_fn_obj = cls.__dict__[method] - arg_count = fn_obj.func_code.co_argcount - expected_arg_count = \ - abstract_fn_obj.func_code.co_argcount - method_ok = arg_count == expected_arg_count - if method_ok: - continue - return NotImplemented - return True - return NotImplemented diff --git a/neutron/plugins/cisco/models/__init__.py b/neutron/plugins/cisco/models/__init__.py deleted file mode 100644 index 833357b73..000000000 --- a/neutron/plugins/cisco/models/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. diff --git a/neutron/plugins/cisco/models/virt_phy_sw_v2.py b/neutron/plugins/cisco/models/virt_phy_sw_v2.py deleted file mode 100644 index b7452f363..000000000 --- a/neutron/plugins/cisco/models/virt_phy_sw_v2.py +++ /dev/null @@ -1,553 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Rohit Agarwalla, Cisco Systems, Inc. -# - -import inspect -import logging -import sys - -from neutron.api.v2 import attributes -from neutron.db import api as db_api -from neutron.extensions import portbindings -from neutron.extensions import providernet as provider -from neutron import neutron_plugin_base_v2 -from neutron.openstack.common import importutils -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_credentials_v2 as cred -from neutron.plugins.cisco.common import cisco_exceptions as cexc -from neutron.plugins.cisco.common import config as conf -from neutron.plugins.cisco.db import network_db_v2 as cdb -from neutron.plugins.openvswitch import ovs_db_v2 as odb - - -LOG = logging.getLogger(__name__) - - -class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): - """Virtual Physical Switch Model. - - This implementation works with OVS and Nexus plugin for the - following topology: - One or more servers to a nexus switch. - """ - __native_bulk_support = True - supported_extension_aliases = ["provider", "binding"] - _methods_to_delegate = ['create_network_bulk', - 'get_network', 'get_networks', - 'create_port_bulk', - 'get_port', 'get_ports', - 'create_subnet', 'create_subnet_bulk', - 'delete_subnet', 'update_subnet', - 'get_subnet', 'get_subnets', - 'create_or_update_agent', 'report_state'] - - def __init__(self): - """Initialize the segmentation manager. - - Checks which device plugins are configured, and load the inventories - those device plugins for which the inventory is configured. - """ - conf.CiscoConfigOptions() - - self._plugins = {} - for key in conf.CISCO_PLUGINS.keys(): - plugin_obj = conf.CISCO_PLUGINS[key] - if plugin_obj is not None: - self._plugins[key] = importutils.import_object(plugin_obj) - LOG.debug(_("Loaded device plugin %s"), - conf.CISCO_PLUGINS[key]) - - if ((const.VSWITCH_PLUGIN in self._plugins) and - hasattr(self._plugins[const.VSWITCH_PLUGIN], - "supported_extension_aliases")): - self.supported_extension_aliases.extend( - self._plugins[const.VSWITCH_PLUGIN]. - supported_extension_aliases) - # At this point, all the database models should have been loaded. It's - # possible that configure_db() may have been called by one of the - # plugins loaded in above. Otherwise, this call is to make sure that - # the database is initialized - db_api.configure_db() - - # Initialize credential store after database initialization - cred.Store.initialize() - LOG.debug(_("%(module)s.%(name)s init done"), - {'module': __name__, - 'name': self.__class__.__name__}) - - # Check whether we have a valid Nexus driver loaded - self.is_nexus_plugin = False - nexus_driver = conf.CISCO.nexus_driver - if nexus_driver.endswith('CiscoNEXUSDriver'): - self.is_nexus_plugin = True - - def __getattribute__(self, name): - """Delegate calls to OVS sub-plugin. - - This delegates the calls to the methods implemented only by the OVS - sub-plugin. Note: Currently, bulking is handled by the caller - (PluginV2), and this model class expects to receive only non-bulking - calls. If, however, a bulking call is made, this will method will - delegate the call to the OVS plugin. - """ - super_getattribute = super(VirtualPhysicalSwitchModelV2, - self).__getattribute__ - methods = super_getattribute('_methods_to_delegate') - - if name in methods: - plugin = super_getattribute('_plugins')[const.VSWITCH_PLUGIN] - return getattr(plugin, name) - - try: - return super_getattribute(name) - except AttributeError: - plugin = super_getattribute('_plugins')[const.VSWITCH_PLUGIN] - return getattr(plugin, name) - - def _func_name(self, offset=0): - """Get the name of the calling function.""" - frame_record = inspect.stack()[1 + offset] - func_name = frame_record[3] - return func_name - - def _invoke_plugin_per_device(self, plugin_key, function_name, - args, **kwargs): - """Invoke plugin per device. - - Invokes a device plugin's relevant functions (based on the - plugin implementation) for completing this operation. - """ - if plugin_key not in self._plugins: - LOG.info(_("No %s Plugin loaded"), plugin_key) - LOG.info(_("%(plugin_key)s: %(function_name)s with args %(args)s " - "ignored"), - {'plugin_key': plugin_key, - 'function_name': function_name, - 'args': args}) - else: - func = getattr(self._plugins[plugin_key], function_name) - return func(*args, **kwargs) - - def _get_segmentation_id(self, network_id): - binding_seg_id = odb.get_network_binding(None, network_id) - if not binding_seg_id: - raise cexc.NetworkSegmentIDNotFound(net_id=network_id) - return binding_seg_id.segmentation_id - - def _get_provider_vlan_id(self, network): - if (all(attributes.is_attr_set(network.get(attr)) - for attr in (provider.NETWORK_TYPE, - provider.PHYSICAL_NETWORK, - provider.SEGMENTATION_ID)) - and - network[provider.NETWORK_TYPE] == const.NETWORK_TYPE_VLAN): - return network[provider.SEGMENTATION_ID] - - def create_network(self, context, network): - """Create network. - - Perform this operation in the context of the configured device - plugins. - """ - LOG.debug(_("create_network() called")) - provider_vlan_id = self._get_provider_vlan_id(network[const.NETWORK]) - args = [context, network] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) - # The vswitch plugin did all the verification. If it's a provider - # vlan network, save it for the nexus plugin to use later. - if provider_vlan_id: - network_id = ovs_output[const.NET_ID] - cdb.add_provider_network(network_id, - const.NETWORK_TYPE_VLAN, - provider_vlan_id) - LOG.debug(_("Provider network added to DB: %(network_id)s, " - "%(vlan_id)s"), - {'network_id': network_id, 'vlan_id': provider_vlan_id}) - return ovs_output - - def update_network(self, context, id, network): - """Update network. - - Perform this operation in the context of the configured device - plugins. - - Note that the Nexus sub-plugin does not need to be notified - (and the Nexus switch does not need to be [re]configured) - for an update network operation because the Nexus sub-plugin - is agnostic of all network-level attributes except the - segmentation ID. Furthermore, updating of the segmentation ID - is not supported by the OVS plugin since it is considered a - provider attribute, so it is not supported by this method. - """ - LOG.debug(_("update_network() called")) - - # We can only support updating of provider attributes if all the - # configured sub-plugins support it. Currently we have no method - # in place for checking whether a sub-plugin supports it, - # so assume not. - provider._raise_if_updates_provider_attributes(network['network']) - - args = [context, id, network] - return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) - - def delete_network(self, context, id): - """Delete network. - - Perform this operation in the context of the configured device - plugins. - """ - args = [context, id] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) - if cdb.remove_provider_network(id): - LOG.debug(_("Provider network removed from DB: %s"), id) - return ovs_output - - def get_network(self, context, id, fields=None): - """Get network. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def get_networks(self, context, filters=None, fields=None, - sorts=None, limit=None, marker=None, page_reverse=False): - """Get networks. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def _invoke_nexus_for_net_create(self, context, tenant_id, net_id, - instance_id, host_id): - if not self.is_nexus_plugin: - return False - - network = self.get_network(context, net_id) - vlan_id = self._get_segmentation_id(net_id) - vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id) - network[const.NET_VLAN_ID] = vlan_id - network[const.NET_VLAN_NAME] = vlan_name - attachment = { - const.TENANT_ID: tenant_id, - const.INSTANCE_ID: instance_id, - const.HOST_NAME: host_id, - } - self._invoke_plugin_per_device( - const.NEXUS_PLUGIN, - 'create_network', - [network, attachment]) - - def _check_valid_port_device_owner(self, port): - """Check the port for valid device_owner. - - Don't call the nexus plugin for router and dhcp - port owners. - """ - return port['device_owner'].startswith('compute') - - def _get_port_host_id_from_bindings(self, port): - """Get host_id from portbindings.""" - host_id = None - - if (portbindings.HOST_ID in port and - attributes.is_attr_set(port[portbindings.HOST_ID])): - host_id = port[portbindings.HOST_ID] - - return host_id - - def create_port(self, context, port): - """Create port. - - Perform this operation in the context of the configured device - plugins. - """ - LOG.debug(_("create_port() called")) - args = [context, port] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) - instance_id = port['port']['device_id'] - - # Only call nexus plugin if there's a valid instance_id, host_id - # and device_owner - try: - host_id = self._get_port_host_id_from_bindings(port['port']) - if (instance_id and host_id and - self._check_valid_port_device_owner(port['port'])): - net_id = port['port']['network_id'] - tenant_id = port['port']['tenant_id'] - self._invoke_nexus_for_net_create( - context, tenant_id, net_id, instance_id, host_id) - except Exception: - # Create network on the Nexus plugin has failed, so we need - # to rollback the port creation on the VSwitch plugin. - exc_info = sys.exc_info() - try: - id = ovs_output['id'] - args = [context, id] - ovs_output = self._invoke_plugin_per_device( - const.VSWITCH_PLUGIN, - 'delete_port', - args) - finally: - # Re-raise the original exception - raise exc_info[0], exc_info[1], exc_info[2] - return ovs_output - - def get_port(self, context, id, fields=None): - """Get port. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def get_ports(self, context, filters=None, fields=None): - """Get ports. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def _check_nexus_net_create_needed(self, new_port, old_port): - """Check if nexus plugin should be invoked for net_create. - - In the following cases, the plugin should be invoked: - -- a port is attached to a VM instance. The old host id is None - -- VM migration. The old host id has a valid value - - When the plugin needs to be invoked, return the old_host_id, - and a list of calling arguments. - Otherwise, return '' for old host id and an empty list - """ - old_device_id = old_port['device_id'] - new_device_id = new_port.get('device_id') - new_host_id = self._get_port_host_id_from_bindings(new_port) - tenant_id = old_port['tenant_id'] - net_id = old_port['network_id'] - old_host_id = self._get_port_host_id_from_bindings(old_port) - - LOG.debug(_("tenant_id: %(tid)s, net_id: %(nid)s, " - "old_device_id: %(odi)s, new_device_id: %(ndi)s, " - "old_host_id: %(ohi)s, new_host_id: %(nhi)s, " - "old_device_owner: %(odo)s, new_device_owner: %(ndo)s"), - {'tid': tenant_id, 'nid': net_id, - 'odi': old_device_id, 'ndi': new_device_id, - 'ohi': old_host_id, 'nhi': new_host_id, - 'odo': old_port.get('device_owner'), - 'ndo': new_port.get('device_owner')}) - - # A port is attached to an instance - if (new_device_id and not old_device_id and new_host_id and - self._check_valid_port_device_owner(new_port)): - return '', [tenant_id, net_id, new_device_id, new_host_id] - - # An instance is being migrated - if (old_device_id and old_host_id and new_host_id != old_host_id and - self._check_valid_port_device_owner(old_port)): - return old_host_id, [tenant_id, net_id, old_device_id, new_host_id] - - # no need to invoke the plugin - return '', [] - - def update_port(self, context, id, port): - """Update port. - - Perform this operation in the context of the configured device - plugins. - """ - LOG.debug(_("update_port() called")) - old_port = self.get_port(context, id) - args = [context, id, port] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) - try: - # Check if the nexus plugin needs to be invoked - old_host_id, create_args = self._check_nexus_net_create_needed( - port['port'], old_port) - - # In the case of migration, invoke it to remove - # the previous port binding - if old_host_id: - vlan_id = self._get_segmentation_id(old_port['network_id']) - delete_args = [old_port['device_id'], vlan_id] - self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - "delete_port", - delete_args) - - # Invoke the Nexus plugin to create a net and/or new port binding - if create_args: - self._invoke_nexus_for_net_create(context, *create_args) - - return ovs_output - except Exception: - exc_info = sys.exc_info() - LOG.error(_("Unable to update port '%s' on Nexus switch"), - old_port['name'], exc_info=exc_info) - try: - # Roll back vSwitch plugin to original port attributes. - args = [context, id, {'port': old_port}] - self._invoke_plugin_per_device( - const.VSWITCH_PLUGIN, - self._func_name(), - args) - finally: - # Re-raise the original exception - raise exc_info[0], exc_info[1], exc_info[2] - - def delete_port(self, context, id, l3_port_check=True): - """Delete port. - - Perform this operation in the context of the configured device - plugins. - """ - LOG.debug(_("delete_port() called")) - port = self.get_port(context, id) - - host_id = self._get_port_host_id_from_bindings(port) - - if (self.is_nexus_plugin and host_id and - self._check_valid_port_device_owner(port)): - vlan_id = self._get_segmentation_id(port['network_id']) - n_args = [port['device_id'], vlan_id] - self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - n_args) - try: - args = [context, id] - ovs_output = self._invoke_plugin_per_device( - const.VSWITCH_PLUGIN, self._func_name(), - args, l3_port_check=l3_port_check) - except Exception: - exc_info = sys.exc_info() - # Roll back the delete port on the Nexus plugin - try: - tenant_id = port['tenant_id'] - net_id = port['network_id'] - instance_id = port['device_id'] - host_id = port[portbindings.HOST_ID] - self._invoke_nexus_for_net_create(context, tenant_id, net_id, - instance_id, host_id) - finally: - # Raise the original exception. - raise exc_info[0], exc_info[1], exc_info[2] - - return ovs_output - - def add_router_interface(self, context, router_id, interface_info): - """Add a router interface on a subnet. - - Only invoke the Nexus plugin to create SVI if L3 support on - the Nexus switches is enabled and a Nexus plugin is loaded, - otherwise send it to the vswitch plugin - """ - if (conf.CISCO.nexus_l3_enable and self.is_nexus_plugin): - LOG.debug(_("L3 enabled on Nexus plugin, create SVI on switch")) - if 'subnet_id' not in interface_info: - raise cexc.SubnetNotSpecified() - if 'port_id' in interface_info: - raise cexc.PortIdForNexusSvi() - subnet = self.get_subnet(context, interface_info['subnet_id']) - gateway_ip = subnet['gateway_ip'] - # Get gateway IP address and netmask - cidr = subnet['cidr'] - netmask = cidr.split('/', 1)[1] - gateway_ip = gateway_ip + '/' + netmask - network_id = subnet['network_id'] - vlan_id = self._get_segmentation_id(network_id) - vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id) - - n_args = [vlan_name, vlan_id, subnet['id'], gateway_ip, router_id] - return self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - n_args) - else: - LOG.debug(_("L3 disabled or not Nexus plugin, send to vswitch")) - n_args = [context, router_id, interface_info] - return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - n_args) - - def remove_router_interface(self, context, router_id, interface_info): - """Remove a router interface. - - Only invoke the Nexus plugin to delete SVI if L3 support on - the Nexus switches is enabled and a Nexus plugin is loaded, - otherwise send it to the vswitch plugin - """ - if (conf.CISCO.nexus_l3_enable and self.is_nexus_plugin): - LOG.debug(_("L3 enabled on Nexus plugin, delete SVI from switch")) - - subnet = self.get_subnet(context, interface_info['subnet_id']) - network_id = subnet['network_id'] - vlan_id = self._get_segmentation_id(network_id) - n_args = [vlan_id, router_id] - - return self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - n_args) - else: - LOG.debug(_("L3 disabled or not Nexus plugin, send to vswitch")) - n_args = [context, router_id, interface_info] - return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - n_args) - - def create_subnet(self, context, subnet): - """Create subnet. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def update_subnet(self, context, id, subnet): - """Update subnet. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def get_subnet(self, context, id, fields=None): - """Get subnet. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def delete_subnet(self, context, id, kwargs): - """Delete subnet. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover - - def get_subnets(self, context, filters=None, fields=None, - sorts=None, limit=None, marker=None, page_reverse=False): - """Get subnets. This method is delegated to the vswitch plugin. - - This method is included here to satisfy abstract method requirements. - """ - pass # pragma no cover diff --git a/neutron/plugins/cisco/n1kv/__init__.py b/neutron/plugins/cisco/n1kv/__init__.py deleted file mode 100644 index 59a411933..000000000 --- a/neutron/plugins/cisco/n1kv/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Abhishek Raut, Cisco Systems, Inc. -# diff --git a/neutron/plugins/cisco/n1kv/n1kv_client.py b/neutron/plugins/cisco/n1kv/n1kv_client.py deleted file mode 100644 index 541750835..000000000 --- a/neutron/plugins/cisco/n1kv/n1kv_client.py +++ /dev/null @@ -1,541 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Abhishek Raut, Cisco Systems, Inc. -# @author: Rudrajit Tapadar, Cisco Systems, Inc. - -import base64 -import eventlet -import netaddr -import requests - -from neutron.common import exceptions as n_exc -from neutron.extensions import providernet -from neutron.openstack.common import jsonutils -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_constants as c_const -from neutron.plugins.cisco.common import cisco_credentials_v2 as c_cred -from neutron.plugins.cisco.common import cisco_exceptions as c_exc -from neutron.plugins.cisco.common import config as c_conf -from neutron.plugins.cisco.db import network_db_v2 -from neutron.plugins.cisco.extensions import n1kv - -LOG = logging.getLogger(__name__) - - -class Client(object): - - """ - Client for the Cisco Nexus1000V Neutron Plugin. - - This client implements functions to communicate with - Cisco Nexus1000V VSM. - - For every Neutron objects, Cisco Nexus1000V Neutron Plugin - creates a corresponding object in the controller (Cisco - Nexus1000V VSM). - - CONCEPTS: - - Following are few concepts used in Nexus1000V VSM: - - port-profiles: - Policy profiles correspond to port profiles on Nexus1000V VSM. - Port profiles are the primary mechanism by which network policy is - defined and applied to switch interfaces in a Nexus 1000V system. - - network-segment: - Each network-segment represents a broadcast domain. - - network-segment-pool: - A network-segment-pool contains one or more network-segments. - - logical-network: - A logical-network contains one or more network-segment-pools. - - bridge-domain: - A bridge-domain is created when the network-segment is of type VXLAN. - Each VXLAN <--> VLAN combination can be thought of as a bridge domain. - - ip-pool: - Each ip-pool represents a subnet on the Nexus1000V VSM. - - vm-network: - vm-network refers to a network-segment and policy-profile. - It maintains a list of ports that uses the network-segment and - policy-profile this vm-network refers to. - - events: - Events correspond to commands that are logged on Nexus1000V VSM. - Events are used to poll for a certain resource on Nexus1000V VSM. - Event type of port_profile: Return all updates/create/deletes - of port profiles from the VSM. - Event type of port_profile_update: Return only updates regarding - policy-profiles. - Event type of port_profile_delete: Return only deleted policy profiles. - - - WORK FLOW: - - For every network profile a corresponding logical-network and - a network-segment-pool, under this logical-network, will be created. - - For every network created from a given network profile, a - network-segment will be added to the network-segment-pool corresponding - to that network profile. - - A port is created on a network and associated with a policy-profile. - Hence for every unique combination of a network and a policy-profile, a - unique vm-network will be created and a reference to the port will be - added. If the same combination of network and policy-profile is used by - another port, the references to that port will be added to the same - vm-network. - - - """ - - # Define paths for the URI where the client connects for HTTP requests. - port_profiles_path = "/virtual-port-profile" - network_segment_path = "/network-segment/%s" - network_segment_pool_path = "/network-segment-pool/%s" - ip_pool_path = "/ip-pool-template/%s" - ports_path = "/kvm/vm-network/%s/ports" - port_path = "/kvm/vm-network/%s/ports/%s" - vm_networks_path = "/kvm/vm-network" - vm_network_path = "/kvm/vm-network/%s" - bridge_domains_path = "/kvm/bridge-domain" - bridge_domain_path = "/kvm/bridge-domain/%s" - logical_network_path = "/logical-network/%s" - events_path = "/kvm/events" - clusters_path = "/cluster" - encap_profiles_path = "/encapsulation-profile" - encap_profile_path = "/encapsulation-profile/%s" - - pool = eventlet.GreenPool(c_conf.CISCO_N1K.http_pool_size) - - def __init__(self, **kwargs): - """Initialize a new client for the plugin.""" - self.format = 'json' - self.hosts = self._get_vsm_hosts() - self.action_prefix = 'http://%s/api/n1k' % self.hosts[0] - self.timeout = c_const.DEFAULT_HTTP_TIMEOUT - - def list_port_profiles(self): - """ - Fetch all policy profiles from the VSM. - - :returns: JSON string - """ - return self._get(self.port_profiles_path) - - def create_bridge_domain(self, network, overlay_subtype): - """ - Create a bridge domain on VSM. - - :param network: network dict - :param overlay_subtype: string representing subtype of overlay network - """ - body = {'name': network['id'] + c_const.BRIDGE_DOMAIN_SUFFIX, - 'segmentId': network[providernet.SEGMENTATION_ID], - 'subType': overlay_subtype, - 'tenantId': network['tenant_id']} - if overlay_subtype == c_const.NETWORK_SUBTYPE_NATIVE_VXLAN: - body['groupIp'] = network[n1kv.MULTICAST_IP] - return self._post(self.bridge_domains_path, - body=body) - - def delete_bridge_domain(self, name): - """ - Delete a bridge domain on VSM. - - :param name: name of the bridge domain to be deleted - """ - return self._delete(self.bridge_domain_path % name) - - def create_network_segment(self, network, network_profile): - """ - Create a network segment on the VSM. - - :param network: network dict - :param network_profile: network profile dict - """ - body = {'publishName': network['id'], - 'description': network['name'], - 'id': network['id'], - 'tenantId': network['tenant_id'], - 'networkSegmentPool': network_profile['id'], } - if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VLAN: - body['vlan'] = network[providernet.SEGMENTATION_ID] - elif network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_OVERLAY: - body['bridgeDomain'] = (network['id'] + - c_const.BRIDGE_DOMAIN_SUFFIX) - if network_profile['segment_type'] == c_const.NETWORK_TYPE_TRUNK: - body['mode'] = c_const.NETWORK_TYPE_TRUNK - body['segmentType'] = network_profile['sub_type'] - if network_profile['sub_type'] == c_const.NETWORK_TYPE_VLAN: - body['addSegments'] = network['add_segment_list'] - body['delSegments'] = network['del_segment_list'] - else: - body['encapProfile'] = (network['id'] + - c_const.ENCAPSULATION_PROFILE_SUFFIX) - else: - body['mode'] = 'access' - body['segmentType'] = network_profile['segment_type'] - return self._post(self.network_segment_path % network['id'], - body=body) - - def update_network_segment(self, network_segment_id, body): - """ - Update a network segment on the VSM. - - Network segment on VSM can be updated to associate it with an ip-pool - or update its description and segment id. - - :param network_segment_id: UUID representing the network segment - :param body: dict of arguments to be updated - """ - return self._post(self.network_segment_path % network_segment_id, - body=body) - - def delete_network_segment(self, network_segment_id): - """ - Delete a network segment on the VSM. - - :param network_segment_id: UUID representing the network segment - """ - return self._delete(self.network_segment_path % network_segment_id) - - def create_logical_network(self, network_profile, tenant_id): - """ - Create a logical network on the VSM. - - :param network_profile: network profile dict - :param tenant_id: UUID representing the tenant - """ - LOG.debug(_("Logical network")) - body = {'description': network_profile['name'], - 'tenantId': tenant_id} - logical_network_name = (network_profile['id'] + - c_const.LOGICAL_NETWORK_SUFFIX) - return self._post(self.logical_network_path % logical_network_name, - body=body) - - def delete_logical_network(self, logical_network_name): - """ - Delete a logical network on VSM. - - :param logical_network_name: string representing name of the logical - network - """ - return self._delete( - self.logical_network_path % logical_network_name) - - def create_network_segment_pool(self, network_profile, tenant_id): - """ - Create a network segment pool on the VSM. - - :param network_profile: network profile dict - :param tenant_id: UUID representing the tenant - """ - LOG.debug(_("network_segment_pool")) - logical_network_name = (network_profile['id'] + - c_const.LOGICAL_NETWORK_SUFFIX) - body = {'name': network_profile['name'], - 'description': network_profile['name'], - 'id': network_profile['id'], - 'logicalNetwork': logical_network_name, - 'tenantId': tenant_id} - return self._post( - self.network_segment_pool_path % network_profile['id'], - body=body) - - def update_network_segment_pool(self, network_profile): - """ - Update a network segment pool on the VSM. - - :param network_profile: network profile dict - """ - body = {'name': network_profile['name'], - 'description': network_profile['name']} - return self._post(self.network_segment_pool_path % - network_profile['id'], body=body) - - def delete_network_segment_pool(self, network_segment_pool_id): - """ - Delete a network segment pool on the VSM. - - :param network_segment_pool_id: UUID representing the network - segment pool - """ - return self._delete(self.network_segment_pool_path % - network_segment_pool_id) - - def create_ip_pool(self, subnet): - """ - Create an ip-pool on the VSM. - - :param subnet: subnet dict - """ - if subnet['cidr']: - try: - ip = netaddr.IPNetwork(subnet['cidr']) - netmask = str(ip.netmask) - network_address = str(ip.network) - except (ValueError, netaddr.AddrFormatError): - msg = _("Invalid input for CIDR") - raise n_exc.InvalidInput(error_message=msg) - else: - netmask = network_address = "" - - if subnet['allocation_pools']: - address_range_start = subnet['allocation_pools'][0]['start'] - address_range_end = subnet['allocation_pools'][0]['end'] - else: - address_range_start = None - address_range_end = None - - body = {'addressRangeStart': address_range_start, - 'addressRangeEnd': address_range_end, - 'ipAddressSubnet': netmask, - 'description': subnet['name'], - 'gateway': subnet['gateway_ip'], - 'dhcp': subnet['enable_dhcp'], - 'dnsServersList': subnet['dns_nameservers'], - 'networkAddress': network_address, - 'tenantId': subnet['tenant_id']} - return self._post(self.ip_pool_path % subnet['id'], - body=body) - - def update_ip_pool(self, subnet): - """ - Update an ip-pool on the VSM. - - :param subnet: subnet dictionary - """ - body = {'description': subnet['name'], - 'dhcp': subnet['enable_dhcp'], - 'dnsServersList': subnet['dns_nameservers']} - return self._post(self.ip_pool_path % subnet['id'], - body=body) - - def delete_ip_pool(self, subnet_id): - """ - Delete an ip-pool on the VSM. - - :param subnet_id: UUID representing the subnet - """ - return self._delete(self.ip_pool_path % subnet_id) - - def create_vm_network(self, - port, - vm_network_name, - policy_profile): - """ - Create a VM network on the VSM. - - :param port: port dict - :param vm_network_name: name of the VM network - :param policy_profile: policy profile dict - """ - body = {'name': vm_network_name, - 'networkSegmentId': port['network_id'], - 'networkSegment': port['network_id'], - 'portProfile': policy_profile['name'], - 'portProfileId': policy_profile['id'], - 'tenantId': port['tenant_id'], - 'portId': port['id'], - 'macAddress': port['mac_address'], - } - if port.get('fixed_ips'): - body['ipAddress'] = port['fixed_ips'][0]['ip_address'] - body['subnetId'] = port['fixed_ips'][0]['subnet_id'] - return self._post(self.vm_networks_path, - body=body) - - def delete_vm_network(self, vm_network_name): - """ - Delete a VM network on the VSM. - - :param vm_network_name: name of the VM network - """ - return self._delete(self.vm_network_path % vm_network_name) - - def create_n1kv_port(self, port, vm_network_name): - """ - Create a port on the VSM. - - :param port: port dict - :param vm_network_name: name of the VM network which imports this port - """ - body = {'id': port['id'], - 'macAddress': port['mac_address']} - if port.get('fixed_ips'): - body['ipAddress'] = port['fixed_ips'][0]['ip_address'] - body['subnetId'] = port['fixed_ips'][0]['subnet_id'] - return self._post(self.ports_path % vm_network_name, - body=body) - - def update_n1kv_port(self, vm_network_name, port_id, body): - """ - Update a port on the VSM. - - Update the mac address associated with the port - - :param vm_network_name: name of the VM network which imports this port - :param port_id: UUID of the port - :param body: dict of the arguments to be updated - """ - return self._post(self.port_path % (vm_network_name, port_id), - body=body) - - def delete_n1kv_port(self, vm_network_name, port_id): - """ - Delete a port on the VSM. - - :param vm_network_name: name of the VM network which imports this port - :param port_id: UUID of the port - """ - return self._delete(self.port_path % (vm_network_name, port_id)) - - def _do_request(self, method, action, body=None, - headers=None): - """ - Perform the HTTP request. - - The response is in either JSON format or plain text. A GET method will - invoke a JSON response while a PUT/POST/DELETE returns message from the - VSM in plain text format. - Exception is raised when VSM replies with an INTERNAL SERVER ERROR HTTP - status code (500) i.e. an error has occurred on the VSM or SERVICE - UNAVAILABLE (503) i.e. VSM is not reachable. - - :param method: type of the HTTP request. POST, GET, PUT or DELETE - :param action: path to which the client makes request - :param body: dict for arguments which are sent as part of the request - :param headers: header for the HTTP request - :returns: JSON or plain text in HTTP response - """ - action = self.action_prefix + action - if not headers and self.hosts: - headers = self._get_auth_header(self.hosts[0]) - headers['Content-Type'] = self._set_content_type('json') - headers['Accept'] = self._set_content_type('json') - if body: - body = jsonutils.dumps(body, indent=2) - LOG.debug(_("req: %s"), body) - try: - resp = self.pool.spawn(requests.request, - method, - url=action, - data=body, - headers=headers, - timeout=self.timeout).wait() - except Exception as e: - raise c_exc.VSMConnectionFailed(reason=e) - LOG.debug(_("status_code %s"), resp.status_code) - if resp.status_code == requests.codes.OK: - if 'application/json' in resp.headers['content-type']: - try: - return resp.json() - except ValueError: - return {} - elif 'text/plain' in resp.headers['content-type']: - LOG.debug(_("VSM: %s"), resp.text) - else: - raise c_exc.VSMError(reason=resp.text) - - def _set_content_type(self, format=None): - """ - Set the mime-type to either 'xml' or 'json'. - - :param format: format to be set. - :return: mime-type string - """ - if not format: - format = self.format - return "application/%s" % format - - def _delete(self, action, body=None, headers=None): - return self._do_request("DELETE", action, body=body, - headers=headers) - - def _get(self, action, body=None, headers=None): - return self._do_request("GET", action, body=body, - headers=headers) - - def _post(self, action, body=None, headers=None): - return self._do_request("POST", action, body=body, - headers=headers) - - def _put(self, action, body=None, headers=None): - return self._do_request("PUT", action, body=body, - headers=headers) - - def _get_vsm_hosts(self): - """ - Retrieve a list of VSM ip addresses. - - :return: list of host ip addresses - """ - return [cr[c_const.CREDENTIAL_NAME] for cr in - network_db_v2.get_all_n1kv_credentials()] - - def _get_auth_header(self, host_ip): - """ - Retrieve header with auth info for the VSM. - - :param host_ip: IP address of the VSM - :return: authorization header dict - """ - username = c_cred.Store.get_username(host_ip) - password = c_cred.Store.get_password(host_ip) - auth = base64.encodestring("%s:%s" % (username, password)).rstrip() - header = {"Authorization": "Basic %s" % auth} - return header - - def get_clusters(self): - """Fetches a list of all vxlan gateway clusters.""" - return self._get(self.clusters_path) - - def create_encapsulation_profile(self, encap): - """ - Create an encapsulation profile on VSM. - - :param encap: encapsulation dict - """ - body = {'name': encap['name'], - 'addMappings': encap['add_segment_list'], - 'delMappings': encap['del_segment_list']} - return self._post(self.encap_profiles_path, - body=body) - - def update_encapsulation_profile(self, context, profile_name, body): - """ - Adds a vlan to bridge-domain mapping to an encapsulation profile. - - :param profile_name: Name of the encapsulation profile - :param body: mapping dictionary - """ - return self._post(self.encap_profile_path - % profile_name, body=body) - - def delete_encapsulation_profile(self, name): - """ - Delete an encapsulation profile on VSM. - - :param name: name of the encapsulation profile to be deleted - """ - return self._delete(self.encap_profile_path % name) diff --git a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py deleted file mode 100644 index 6ef51f3d0..000000000 --- a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py +++ /dev/null @@ -1,1438 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cisco Systems, 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. -# -# @author: Aruna Kushwaha, Cisco Systems, Inc. -# @author: Rudrajit Tapadar, Cisco Systems, Inc. -# @author: Abhishek Raut, Cisco Systems, Inc. -# @author: Sergey Sudakovich, Cisco Systems, Inc. - -import eventlet - -from oslo.config import cfg as q_conf - -from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api -from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api -from neutron.api.v2 import attributes -from neutron.common import constants -from neutron.common import exceptions as n_exc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.common import utils -from neutron.db import agents_db -from neutron.db import agentschedulers_db -from neutron.db import db_base_plugin_v2 -from neutron.db import dhcp_rpc_base -from neutron.db import external_net_db -from neutron.db import extraroute_db -from neutron.db import l3_agentschedulers_db -from neutron.db import l3_rpc_base -from neutron.db import portbindings_db -from neutron.extensions import portbindings -from neutron.extensions import providernet -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.openstack.common import uuidutils as uuidutils -from neutron.plugins.cisco.common import cisco_constants as c_const -from neutron.plugins.cisco.common import cisco_credentials_v2 as c_cred -from neutron.plugins.cisco.common import cisco_exceptions -from neutron.plugins.cisco.common import config as c_conf -from neutron.plugins.cisco.db import n1kv_db_v2 -from neutron.plugins.cisco.db import network_db_v2 -from neutron.plugins.cisco.extensions import n1kv -from neutron.plugins.cisco.n1kv import n1kv_client -from neutron.plugins.common import constants as svc_constants - - -LOG = logging.getLogger(__name__) - - -class N1kvRpcCallbacks(rpc_compat.RpcCallback, - dhcp_rpc_base.DhcpRpcCallbackMixin, - l3_rpc_base.L3RpcCallbackMixin): - - """Class to handle agent RPC calls.""" - - # Set RPC API version to 1.1 by default. - RPC_API_VERSION = '1.1' - - -class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - extraroute_db.ExtraRoute_db_mixin, - portbindings_db.PortBindingMixin, - n1kv_db_v2.NetworkProfile_db_mixin, - n1kv_db_v2.PolicyProfile_db_mixin, - network_db_v2.Credential_db_mixin, - l3_agentschedulers_db.L3AgentSchedulerDbMixin, - agentschedulers_db.DhcpAgentSchedulerDbMixin): - - """ - Implement the Neutron abstractions using Cisco Nexus1000V. - - Refer README file for the architecture, new features, and - workflow - - """ - - # This attribute specifies whether the plugin supports or not - # bulk operations. - __native_bulk_support = False - supported_extension_aliases = ["provider", "agent", - "n1kv", "network_profile", - "policy_profile", "external-net", "router", - "binding", "credential", - "l3_agent_scheduler", - "dhcp_agent_scheduler"] - - def __init__(self, configfile=None): - """ - Initialize Nexus1000V Neutron plugin. - - 1. Initialize VIF type to OVS - 2. Initialize Nexus1000v and Credential DB - 3. Establish communication with Cisco Nexus1000V - """ - super(N1kvNeutronPluginV2, self).__init__() - self.base_binding_dict = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, - portbindings.VIF_DETAILS: { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases}} - c_cred.Store.initialize() - self._setup_vsm() - self._setup_rpc() - self.network_scheduler = importutils.import_object( - q_conf.CONF.network_scheduler_driver - ) - self.router_scheduler = importutils.import_object( - q_conf.CONF.router_scheduler_driver - ) - - def _setup_rpc(self): - # RPC support - self.service_topics = {svc_constants.CORE: topics.PLUGIN, - svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN} - self.conn = rpc_compat.create_connection(new=True) - self.endpoints = [N1kvRpcCallbacks(), agents_db.AgentExtRpcCallback()] - for svc_topic in self.service_topics.values(): - self.conn.create_consumer(svc_topic, self.endpoints, fanout=False) - self.dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI() - self.l3_agent_notifier = l3_rpc_agent_api.L3AgentNotifyAPI() - # Consume from all consumers in threads - self.conn.consume_in_threads() - - def _setup_vsm(self): - """ - Setup Cisco Nexus 1000V related parameters and pull policy profiles. - - Retrieve all the policy profiles from the VSM when the plugin is - is instantiated for the first time and then continue to poll for - policy profile updates. - """ - LOG.debug(_('_setup_vsm')) - self.agent_vsm = True - # Poll VSM for create/delete of policy profile. - eventlet.spawn(self._poll_policy_profiles) - - def _poll_policy_profiles(self): - """Start a green thread to pull policy profiles from VSM.""" - while True: - self._populate_policy_profiles() - eventlet.sleep(c_conf.CISCO_N1K.poll_duration) - - def _populate_policy_profiles(self): - """ - Populate all the policy profiles from VSM. - - The tenant id is not available when the policy profiles are polled - from the VSM. Hence we associate the policy profiles with fake - tenant-ids. - """ - LOG.debug(_('_populate_policy_profiles')) - try: - n1kvclient = n1kv_client.Client() - policy_profiles = n1kvclient.list_port_profiles() - vsm_profiles = {} - plugin_profiles_set = set() - # Fetch policy profiles from VSM - for profile_name in policy_profiles: - profile_id = (policy_profiles - [profile_name][c_const.PROPERTIES][c_const.ID]) - vsm_profiles[profile_id] = profile_name - # Fetch policy profiles previously populated - for profile in n1kv_db_v2.get_policy_profiles(): - plugin_profiles_set.add(profile.id) - vsm_profiles_set = set(vsm_profiles) - # Update database if the profile sets differ. - if vsm_profiles_set ^ plugin_profiles_set: - # Add profiles in database if new profiles were created in VSM - for pid in vsm_profiles_set - plugin_profiles_set: - self._add_policy_profile(vsm_profiles[pid], pid) - - # Delete profiles from database if profiles were deleted in VSM - for pid in plugin_profiles_set - vsm_profiles_set: - self._delete_policy_profile(pid) - self._remove_all_fake_policy_profiles() - except (cisco_exceptions.VSMError, - cisco_exceptions.VSMConnectionFailed): - LOG.warning(_('No policy profile populated from VSM')) - - def _extend_network_dict_provider(self, context, network): - """Add extended network parameters.""" - binding = n1kv_db_v2.get_network_binding(context.session, - network['id']) - network[providernet.NETWORK_TYPE] = binding.network_type - if binding.network_type == c_const.NETWORK_TYPE_OVERLAY: - network[providernet.PHYSICAL_NETWORK] = None - network[providernet.SEGMENTATION_ID] = binding.segmentation_id - network[n1kv.MULTICAST_IP] = binding.multicast_ip - elif binding.network_type == c_const.NETWORK_TYPE_VLAN: - network[providernet.PHYSICAL_NETWORK] = binding.physical_network - network[providernet.SEGMENTATION_ID] = binding.segmentation_id - elif binding.network_type == c_const.NETWORK_TYPE_TRUNK: - network[providernet.PHYSICAL_NETWORK] = binding.physical_network - network[providernet.SEGMENTATION_ID] = None - network[n1kv.MULTICAST_IP] = None - elif binding.network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT: - network[providernet.PHYSICAL_NETWORK] = None - network[providernet.SEGMENTATION_ID] = None - network[n1kv.MULTICAST_IP] = None - - def _process_provider_create(self, context, attrs): - network_type = attrs.get(providernet.NETWORK_TYPE) - physical_network = attrs.get(providernet.PHYSICAL_NETWORK) - segmentation_id = attrs.get(providernet.SEGMENTATION_ID) - - network_type_set = attributes.is_attr_set(network_type) - physical_network_set = attributes.is_attr_set(physical_network) - segmentation_id_set = attributes.is_attr_set(segmentation_id) - - if not (network_type_set or physical_network_set or - segmentation_id_set): - return (None, None, None) - - if not network_type_set: - msg = _("provider:network_type required") - raise n_exc.InvalidInput(error_message=msg) - elif network_type == c_const.NETWORK_TYPE_VLAN: - if not segmentation_id_set: - msg = _("provider:segmentation_id required") - raise n_exc.InvalidInput(error_message=msg) - if segmentation_id < 1 or segmentation_id > 4094: - msg = _("provider:segmentation_id out of range " - "(1 through 4094)") - raise n_exc.InvalidInput(error_message=msg) - elif network_type == c_const.NETWORK_TYPE_OVERLAY: - if physical_network_set: - msg = _("provider:physical_network specified for Overlay " - "network") - raise n_exc.InvalidInput(error_message=msg) - else: - physical_network = None - if not segmentation_id_set: - msg = _("provider:segmentation_id required") - raise n_exc.InvalidInput(error_message=msg) - if segmentation_id < 5000: - msg = _("provider:segmentation_id out of range " - "(5000+)") - raise n_exc.InvalidInput(error_message=msg) - else: - msg = _("provider:network_type %s not supported"), network_type - raise n_exc.InvalidInput(error_message=msg) - - if network_type == c_const.NETWORK_TYPE_VLAN: - if physical_network_set: - network_profiles = n1kv_db_v2.get_network_profiles() - for network_profile in network_profiles: - if physical_network == network_profile[ - 'physical_network']: - break - else: - msg = (_("Unknown provider:physical_network %s"), - physical_network) - raise n_exc.InvalidInput(error_message=msg) - else: - msg = _("provider:physical_network required") - raise n_exc.InvalidInput(error_message=msg) - - return (network_type, physical_network, segmentation_id) - - def _check_provider_update(self, context, attrs): - """Handle Provider network updates.""" - network_type = attrs.get(providernet.NETWORK_TYPE) - physical_network = attrs.get(providernet.PHYSICAL_NETWORK) - segmentation_id = attrs.get(providernet.SEGMENTATION_ID) - - network_type_set = attributes.is_attr_set(network_type) - physical_network_set = attributes.is_attr_set(physical_network) - segmentation_id_set = attributes.is_attr_set(segmentation_id) - - if not (network_type_set or physical_network_set or - segmentation_id_set): - return - - # TBD : Need to handle provider network updates - msg = _("Plugin does not support updating provider attributes") - raise n_exc.InvalidInput(error_message=msg) - - def _get_cluster(self, segment1, segment2, clusters): - """ - Returns a cluster to apply the segment mapping - - :param segment1: UUID of segment to be mapped - :param segment2: UUID of segment to be mapped - :param clusters: List of clusters - """ - for cluster in sorted(clusters, key=lambda k: k['size']): - for mapping in cluster[c_const.MAPPINGS]: - for segment in mapping[c_const.SEGMENTS]: - if segment1 in segment or segment2 in segment: - break - else: - cluster['size'] += 2 - return cluster['encapProfileName'] - break - return - - def _extend_mapping_dict(self, context, mapping_dict, segment): - """ - Extend a mapping dictionary with dot1q tag and bridge-domain name. - - :param context: neutron api request context - :param mapping_dict: dictionary to populate values - :param segment: id of the segment being populated - """ - net = self.get_network(context, segment) - if net[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VLAN: - mapping_dict['dot1q'] = str(net[providernet.SEGMENTATION_ID]) - else: - mapping_dict['bridgeDomain'] = (net['name'] + - c_const.BRIDGE_DOMAIN_SUFFIX) - - def _send_add_multi_segment_request(self, context, net_id, segment_pairs): - """ - Send Add multi-segment network request to VSM. - - :param context: neutron api request context - :param net_id: UUID of the multi-segment network - :param segment_pairs: List of segments in UUID pairs - that need to be bridged - """ - - if not segment_pairs: - return - - session = context.session - n1kvclient = n1kv_client.Client() - clusters = n1kvclient.get_clusters() - online_clusters = [] - encap_dict = {} - for cluster in clusters['body'][c_const.SET]: - cluster = cluster[c_const.PROPERTIES] - if cluster[c_const.STATE] == c_const.ONLINE: - cluster['size'] = 0 - for mapping in cluster[c_const.MAPPINGS]: - cluster['size'] += ( - len(mapping[c_const.SEGMENTS])) - online_clusters.append(cluster) - for (segment1, segment2) in segment_pairs: - encap_profile = self._get_cluster(segment1, segment2, - online_clusters) - if encap_profile is not None: - if encap_profile in encap_dict: - profile_dict = encap_dict[encap_profile] - else: - profile_dict = {'name': encap_profile, - 'addMappings': [], - 'delMappings': []} - encap_dict[encap_profile] = profile_dict - mapping_dict = {} - self._extend_mapping_dict(context, - mapping_dict, segment1) - self._extend_mapping_dict(context, - mapping_dict, segment2) - profile_dict['addMappings'].append(mapping_dict) - n1kv_db_v2.add_multi_segment_encap_profile_name(session, - net_id, - (segment1, - segment2), - encap_profile) - else: - raise cisco_exceptions.NoClusterFound - - for profile in encap_dict: - n1kvclient.update_encapsulation_profile(context, profile, - encap_dict[profile]) - - def _send_del_multi_segment_request(self, context, net_id, segment_pairs): - """ - Send Delete multi-segment network request to VSM. - - :param context: neutron api request context - :param net_id: UUID of the multi-segment network - :param segment_pairs: List of segments in UUID pairs - whose bridging needs to be removed - """ - if not segment_pairs: - return - session = context.session - encap_dict = {} - n1kvclient = n1kv_client.Client() - for (segment1, segment2) in segment_pairs: - binding = ( - n1kv_db_v2.get_multi_segment_network_binding(session, net_id, - (segment1, - segment2))) - encap_profile = binding['encap_profile_name'] - if encap_profile in encap_dict: - profile_dict = encap_dict[encap_profile] - else: - profile_dict = {'name': encap_profile, - 'addMappings': [], - 'delMappings': []} - encap_dict[encap_profile] = profile_dict - mapping_dict = {} - self._extend_mapping_dict(context, - mapping_dict, segment1) - self._extend_mapping_dict(context, - mapping_dict, segment2) - profile_dict['delMappings'].append(mapping_dict) - - for profile in encap_dict: - n1kvclient.update_encapsulation_profile(context, profile, - encap_dict[profile]) - - def _get_encap_segments(self, context, segment_pairs): - """ - Get the list of segments in encapsulation profile format. - - :param context: neutron api request context - :param segment_pairs: List of segments that need to be bridged - """ - member_list = [] - for pair in segment_pairs: - (segment, dot1qtag) = pair - member_dict = {} - net = self.get_network(context, segment) - member_dict['bridgeDomain'] = (net['name'] + - c_const.BRIDGE_DOMAIN_SUFFIX) - member_dict['dot1q'] = dot1qtag - member_list.append(member_dict) - return member_list - - def _populate_member_segments(self, context, network, segment_pairs, oper): - """ - Populate trunk network dict with member segments. - - :param context: neutron api request context - :param network: Dictionary containing the trunk network information - :param segment_pairs: List of segments in UUID pairs - that needs to be trunked - :param oper: Operation to be performed - """ - LOG.debug(_('_populate_member_segments %s'), segment_pairs) - trunk_list = [] - for (segment, dot1qtag) in segment_pairs: - net = self.get_network(context, segment) - member_dict = {'segment': net['name'], - 'dot1qtag': dot1qtag} - trunk_list.append(member_dict) - if oper == n1kv.SEGMENT_ADD: - network['add_segment_list'] = trunk_list - elif oper == n1kv.SEGMENT_DEL: - network['del_segment_list'] = trunk_list - - def _parse_multi_segments(self, context, attrs, param): - """ - Parse the multi-segment network attributes. - - :param context: neutron api request context - :param attrs: Attributes of the network - :param param: Additional parameter indicating an add - or del operation - :returns: List of segment UUIDs in set pairs - """ - pair_list = [] - valid_seg_types = [c_const.NETWORK_TYPE_VLAN, - c_const.NETWORK_TYPE_OVERLAY] - segments = attrs.get(param) - if not attributes.is_attr_set(segments): - return pair_list - for pair in segments.split(','): - segment1, sep, segment2 = pair.partition(':') - if (uuidutils.is_uuid_like(segment1) and - uuidutils.is_uuid_like(segment2)): - binding1 = n1kv_db_v2.get_network_binding(context.session, - segment1) - binding2 = n1kv_db_v2.get_network_binding(context.session, - segment2) - if (binding1.network_type not in valid_seg_types or - binding2.network_type not in valid_seg_types or - binding1.network_type == binding2.network_type): - msg = _("Invalid pairing supplied") - raise n_exc.InvalidInput(error_message=msg) - else: - pair_list.append((segment1, segment2)) - else: - LOG.debug(_('Invalid UUID supplied in %s'), pair) - msg = _("Invalid UUID supplied") - raise n_exc.InvalidInput(error_message=msg) - return pair_list - - def _parse_trunk_segments(self, context, attrs, param, physical_network, - sub_type): - """ - Parse the trunk network attributes. - - :param context: neutron api request context - :param attrs: Attributes of the network - :param param: Additional parameter indicating an add - or del operation - :param physical_network: Physical network of the trunk segment - :param sub_type: Sub-type of the trunk segment - :returns: List of segment UUIDs and dot1qtag (for vxlan) in set pairs - """ - pair_list = [] - segments = attrs.get(param) - if not attributes.is_attr_set(segments): - return pair_list - for pair in segments.split(','): - segment, sep, dot1qtag = pair.partition(':') - if sub_type == c_const.NETWORK_TYPE_VLAN: - dot1qtag = '' - if uuidutils.is_uuid_like(segment): - binding = n1kv_db_v2.get_network_binding(context.session, - segment) - if binding.network_type == c_const.NETWORK_TYPE_TRUNK: - msg = _("Cannot add a trunk segment '%s' as a member of " - "another trunk segment") % segment - raise n_exc.InvalidInput(error_message=msg) - elif binding.network_type == c_const.NETWORK_TYPE_VLAN: - if sub_type == c_const.NETWORK_TYPE_OVERLAY: - msg = _("Cannot add vlan segment '%s' as a member of " - "a vxlan trunk segment") % segment - raise n_exc.InvalidInput(error_message=msg) - if not physical_network: - physical_network = binding.physical_network - elif physical_network != binding.physical_network: - msg = _("Network UUID '%s' belongs to a different " - "physical network") % segment - raise n_exc.InvalidInput(error_message=msg) - elif binding.network_type == c_const.NETWORK_TYPE_OVERLAY: - if sub_type == c_const.NETWORK_TYPE_VLAN: - msg = _("Cannot add vxlan segment '%s' as a member of " - "a vlan trunk segment") % segment - raise n_exc.InvalidInput(error_message=msg) - try: - if not utils.is_valid_vlan_tag(int(dot1qtag)): - msg = _("Vlan tag '%s' is out of range") % dot1qtag - raise n_exc.InvalidInput(error_message=msg) - except ValueError: - msg = _("Vlan tag '%s' is not an integer " - "value") % dot1qtag - raise n_exc.InvalidInput(error_message=msg) - pair_list.append((segment, dot1qtag)) - else: - LOG.debug(_('%s is not a valid uuid'), segment) - msg = _("'%s' is not a valid UUID") % segment - raise n_exc.InvalidInput(error_message=msg) - return pair_list - - def _extend_network_dict_member_segments(self, context, network): - """Add the extended parameter member segments to the network.""" - members = [] - binding = n1kv_db_v2.get_network_binding(context.session, - network['id']) - if binding.network_type == c_const.NETWORK_TYPE_TRUNK: - members = n1kv_db_v2.get_trunk_members(context.session, - network['id']) - elif binding.network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT: - members = n1kv_db_v2.get_multi_segment_members(context.session, - network['id']) - network[n1kv.MEMBER_SEGMENTS] = members - - def _extend_network_dict_profile(self, context, network): - """Add the extended parameter network profile to the network.""" - binding = n1kv_db_v2.get_network_binding(context.session, - network['id']) - network[n1kv.PROFILE_ID] = binding.profile_id - - def _extend_port_dict_profile(self, context, port): - """Add the extended parameter port profile to the port.""" - binding = n1kv_db_v2.get_port_binding(context.session, - port['id']) - port[n1kv.PROFILE_ID] = binding.profile_id - - def _process_network_profile(self, context, network): - """Validate network profile exists.""" - profile_id = network.get(n1kv.PROFILE_ID) - profile_id_set = attributes.is_attr_set(profile_id) - if not profile_id_set: - profile_name = c_conf.CISCO_N1K.default_network_profile - net_p = self._get_network_profile_by_name(context.session, - profile_name) - profile_id = net_p['id'] - network['n1kv:profile_id'] = profile_id - return profile_id - - def _process_policy_profile(self, context, attrs): - """Validates whether policy profile exists.""" - profile_id = attrs.get(n1kv.PROFILE_ID) - profile_id_set = attributes.is_attr_set(profile_id) - if not profile_id_set: - msg = _("n1kv:profile_id does not exist") - raise n_exc.InvalidInput(error_message=msg) - if not self._policy_profile_exists(profile_id): - msg = _("n1kv:profile_id does not exist") - raise n_exc.InvalidInput(error_message=msg) - - return profile_id - - def _send_create_logical_network_request(self, network_profile, tenant_id): - """ - Send create logical network request to VSM. - - :param network_profile: network profile dictionary - :param tenant_id: UUID representing the tenant - """ - LOG.debug(_('_send_create_logical_network')) - n1kvclient = n1kv_client.Client() - n1kvclient.create_logical_network(network_profile, tenant_id) - - def _send_delete_logical_network_request(self, network_profile): - """ - Send delete logical network request to VSM. - - :param network_profile: network profile dictionary - """ - LOG.debug('_send_delete_logical_network') - n1kvclient = n1kv_client.Client() - logical_network_name = (network_profile['id'] + - c_const.LOGICAL_NETWORK_SUFFIX) - n1kvclient.delete_logical_network(logical_network_name) - - def _send_create_network_profile_request(self, context, profile): - """ - Send create network profile request to VSM. - - :param context: neutron api request context - :param profile: network profile dictionary - """ - LOG.debug(_('_send_create_network_profile_request: %s'), profile['id']) - n1kvclient = n1kv_client.Client() - n1kvclient.create_network_segment_pool(profile, context.tenant_id) - - def _send_update_network_profile_request(self, profile): - """ - Send update network profile request to VSM. - - :param profile: network profile dictionary - """ - LOG.debug(_('_send_update_network_profile_request: %s'), profile['id']) - n1kvclient = n1kv_client.Client() - n1kvclient.update_network_segment_pool(profile) - - def _send_delete_network_profile_request(self, profile): - """ - Send delete network profile request to VSM. - - :param profile: network profile dictionary - """ - LOG.debug(_('_send_delete_network_profile_request: %s'), - profile['name']) - n1kvclient = n1kv_client.Client() - n1kvclient.delete_network_segment_pool(profile['id']) - - def _send_create_network_request(self, context, network, segment_pairs): - """ - Send create network request to VSM. - - Create a bridge domain for network of type Overlay. - :param context: neutron api request context - :param network: network dictionary - :param segment_pairs: List of segments in UUID pairs - that need to be bridged - """ - LOG.debug(_('_send_create_network_request: %s'), network['id']) - profile = self.get_network_profile(context, - network[n1kv.PROFILE_ID]) - n1kvclient = n1kv_client.Client() - if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_OVERLAY: - n1kvclient.create_bridge_domain(network, profile['sub_type']) - if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_TRUNK: - self._populate_member_segments(context, network, segment_pairs, - n1kv.SEGMENT_ADD) - network['del_segment_list'] = [] - if profile['sub_type'] == c_const.NETWORK_TYPE_OVERLAY: - encap_dict = {'name': (network['name'] + - c_const.ENCAPSULATION_PROFILE_SUFFIX), - 'add_segment_list': ( - self._get_encap_segments(context, - segment_pairs)), - 'del_segment_list': []} - n1kvclient.create_encapsulation_profile(encap_dict) - n1kvclient.create_network_segment(network, profile) - - def _send_update_network_request(self, context, network, add_segments, - del_segments): - """ - Send update network request to VSM. - - :param context: neutron api request context - :param network: network dictionary - :param add_segments: List of segments bindings - that need to be deleted - :param del_segments: List of segments bindings - that need to be deleted - """ - LOG.debug(_('_send_update_network_request: %s'), network['id']) - db_session = context.session - profile = n1kv_db_v2.get_network_profile( - db_session, network[n1kv.PROFILE_ID]) - n1kvclient = n1kv_client.Client() - body = {'description': network['name'], - 'id': network['id'], - 'networkSegmentPool': profile['id'], - 'vlan': network[providernet.SEGMENTATION_ID], - 'mode': 'access', - 'segmentType': profile['segment_type'], - 'addSegments': [], - 'delSegments': []} - if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_TRUNK: - self._populate_member_segments(context, network, add_segments, - n1kv.SEGMENT_ADD) - self._populate_member_segments(context, network, del_segments, - n1kv.SEGMENT_DEL) - body['mode'] = c_const.NETWORK_TYPE_TRUNK - body['segmentType'] = profile['sub_type'] - body['addSegments'] = network['add_segment_list'] - body['delSegments'] = network['del_segment_list'] - LOG.debug(_('add_segments=%s'), body['addSegments']) - LOG.debug(_('del_segments=%s'), body['delSegments']) - if profile['sub_type'] == c_const.NETWORK_TYPE_OVERLAY: - encap_profile = (network['id'] + - c_const.ENCAPSULATION_PROFILE_SUFFIX) - encap_dict = {'name': encap_profile, - 'addMappings': ( - self._get_encap_segments(context, - add_segments)), - 'delMappings': ( - self._get_encap_segments(context, - del_segments))} - n1kvclient.update_encapsulation_profile(context, encap_profile, - encap_dict) - n1kvclient.update_network_segment(network['id'], body) - - def _send_delete_network_request(self, context, network): - """ - Send delete network request to VSM. - - Delete bridge domain if network is of type Overlay. - Delete encapsulation profile if network is of type OVERLAY Trunk. - :param context: neutron api request context - :param network: network dictionary - """ - LOG.debug(_('_send_delete_network_request: %s'), network['id']) - n1kvclient = n1kv_client.Client() - session = context.session - if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_OVERLAY: - name = network['id'] + c_const.BRIDGE_DOMAIN_SUFFIX - n1kvclient.delete_bridge_domain(name) - elif network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_TRUNK: - profile = self.get_network_profile( - context, network[n1kv.PROFILE_ID]) - if profile['sub_type'] == c_const.NETWORK_TYPE_OVERLAY: - profile_name = (network['id'] + - c_const.ENCAPSULATION_PROFILE_SUFFIX) - n1kvclient.delete_encapsulation_profile(profile_name) - elif (network[providernet.NETWORK_TYPE] == - c_const.NETWORK_TYPE_MULTI_SEGMENT): - encap_dict = n1kv_db_v2.get_multi_segment_encap_dict(session, - network['id']) - for profile in encap_dict: - profile_dict = {'name': profile, - 'addSegments': [], - 'delSegments': []} - for segment_pair in encap_dict[profile]: - mapping_dict = {} - (segment1, segment2) = segment_pair - self._extend_mapping_dict(context, - mapping_dict, segment1) - self._extend_mapping_dict(context, - mapping_dict, segment2) - profile_dict['delSegments'].append(mapping_dict) - n1kvclient.update_encapsulation_profile(context, profile, - profile_dict) - n1kvclient.delete_network_segment(network['id']) - - def _send_create_subnet_request(self, context, subnet): - """ - Send create subnet request to VSM. - - :param context: neutron api request context - :param subnet: subnet dictionary - """ - LOG.debug(_('_send_create_subnet_request: %s'), subnet['id']) - n1kvclient = n1kv_client.Client() - n1kvclient.create_ip_pool(subnet) - - def _send_update_subnet_request(self, subnet): - """ - Send update subnet request to VSM. - - :param subnet: subnet dictionary - """ - LOG.debug(_('_send_update_subnet_request: %s'), subnet['name']) - n1kvclient = n1kv_client.Client() - n1kvclient.update_ip_pool(subnet) - - def _send_delete_subnet_request(self, context, subnet): - """ - Send delete subnet request to VSM. - - :param context: neutron api request context - :param subnet: subnet dictionary - """ - LOG.debug(_('_send_delete_subnet_request: %s'), subnet['name']) - body = {'ipPool': subnet['id'], 'deleteSubnet': True} - n1kvclient = n1kv_client.Client() - n1kvclient.update_network_segment(subnet['network_id'], body=body) - n1kvclient.delete_ip_pool(subnet['id']) - - def _send_create_port_request(self, - context, - port, - port_count, - policy_profile, - vm_network_name): - """ - Send create port request to VSM. - - Create a VM network for a network and policy profile combination. - If the VM network already exists, bind this port to the existing - VM network on the VSM. - :param context: neutron api request context - :param port: port dictionary - :param port_count: integer representing the number of ports in one - VM Network - :param policy_profile: object of type policy profile - :param vm_network_name: string representing the name of the VM - network - """ - LOG.debug(_('_send_create_port_request: %s'), port) - n1kvclient = n1kv_client.Client() - if port_count == 1: - n1kvclient.create_vm_network(port, - vm_network_name, - policy_profile) - else: - n1kvclient.create_n1kv_port(port, vm_network_name) - - def _send_update_port_request(self, port_id, mac_address, vm_network_name): - """ - Send update port request to VSM. - - :param port_id: UUID representing port to update - :param mac_address: string representing the mac address - :param vm_network_name: VM network name to which the port is bound - """ - LOG.debug(_('_send_update_port_request: %s'), port_id) - body = {'portId': port_id, - 'macAddress': mac_address} - n1kvclient = n1kv_client.Client() - n1kvclient.update_n1kv_port(vm_network_name, port_id, body) - - def _send_delete_port_request(self, context, port, vm_network): - """ - Send delete port request to VSM. - - Delete the port on the VSM. If it is the last port on the VM Network, - delete the VM Network. - :param context: neutron api request context - :param port: port object which is to be deleted - :param vm_network: VM network object with which the port is associated - """ - LOG.debug(_('_send_delete_port_request: %s'), port['id']) - n1kvclient = n1kv_client.Client() - n1kvclient.delete_n1kv_port(vm_network['name'], port['id']) - if vm_network['port_count'] == 0: - n1kvclient.delete_vm_network(vm_network['name']) - - def _get_segmentation_id(self, context, id): - """ - Retrieve segmentation ID for a given network. - - :param context: neutron api request context - :param id: UUID of the network - :returns: segmentation ID for the network - """ - session = context.session - binding = n1kv_db_v2.get_network_binding(session, id) - return binding.segmentation_id - - def create_network(self, context, network): - """ - Create network based on network profile. - - :param context: neutron api request context - :param network: network dictionary - :returns: network object - """ - (network_type, physical_network, - segmentation_id) = self._process_provider_create(context, - network['network']) - profile_id = self._process_network_profile(context, network['network']) - segment_pairs = None - LOG.debug(_('Create network: profile_id=%s'), profile_id) - session = context.session - with session.begin(subtransactions=True): - if not network_type: - # tenant network - (physical_network, network_type, segmentation_id, - multicast_ip) = n1kv_db_v2.alloc_network(session, - profile_id) - LOG.debug(_('Physical_network %(phy_net)s, ' - 'seg_type %(net_type)s, ' - 'seg_id %(seg_id)s, ' - 'multicast_ip %(multicast_ip)s'), - {'phy_net': physical_network, - 'net_type': network_type, - 'seg_id': segmentation_id, - 'multicast_ip': multicast_ip}) - if network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT: - segment_pairs = ( - self._parse_multi_segments(context, network['network'], - n1kv.SEGMENT_ADD)) - LOG.debug(_('Seg list %s '), segment_pairs) - elif network_type == c_const.NETWORK_TYPE_TRUNK: - network_profile = self.get_network_profile(context, - profile_id) - segment_pairs = ( - self._parse_trunk_segments(context, network['network'], - n1kv.SEGMENT_ADD, - physical_network, - network_profile['sub_type'] - )) - LOG.debug(_('Seg list %s '), segment_pairs) - else: - if not segmentation_id: - raise n_exc.TenantNetworksDisabled() - else: - # provider network - if network_type == c_const.NETWORK_TYPE_VLAN: - network_profile = self.get_network_profile(context, - profile_id) - seg_min, seg_max = self._get_segment_range( - network_profile['segment_range']) - if not seg_min <= segmentation_id <= seg_max: - raise cisco_exceptions.VlanIDOutsidePool - n1kv_db_v2.reserve_specific_vlan(session, - physical_network, - segmentation_id) - multicast_ip = "0.0.0.0" - net = super(N1kvNeutronPluginV2, self).create_network(context, - network) - n1kv_db_v2.add_network_binding(session, - net['id'], - network_type, - physical_network, - segmentation_id, - multicast_ip, - profile_id, - segment_pairs) - self._process_l3_create(context, net, network['network']) - self._extend_network_dict_provider(context, net) - self._extend_network_dict_profile(context, net) - try: - if network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT: - self._send_add_multi_segment_request(context, net['id'], - segment_pairs) - else: - self._send_create_network_request(context, net, segment_pairs) - except(cisco_exceptions.VSMError, - cisco_exceptions.VSMConnectionFailed): - super(N1kvNeutronPluginV2, self).delete_network(context, net['id']) - else: - LOG.debug(_("Created network: %s"), net['id']) - return net - - def update_network(self, context, id, network): - """ - Update network parameters. - - :param context: neutron api request context - :param id: UUID representing the network to update - :returns: updated network object - """ - self._check_provider_update(context, network['network']) - add_segments = [] - del_segments = [] - - session = context.session - with session.begin(subtransactions=True): - net = super(N1kvNeutronPluginV2, self).update_network(context, id, - network) - self._process_l3_update(context, net, network['network']) - binding = n1kv_db_v2.get_network_binding(session, id) - if binding.network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT: - add_segments = ( - self._parse_multi_segments(context, network['network'], - n1kv.SEGMENT_ADD)) - n1kv_db_v2.add_multi_segment_binding(session, - net['id'], add_segments) - del_segments = ( - self._parse_multi_segments(context, network['network'], - n1kv.SEGMENT_DEL)) - self._send_add_multi_segment_request(context, net['id'], - add_segments) - self._send_del_multi_segment_request(context, net['id'], - del_segments) - n1kv_db_v2.del_multi_segment_binding(session, - net['id'], del_segments) - elif binding.network_type == c_const.NETWORK_TYPE_TRUNK: - network_profile = self.get_network_profile(context, - binding.profile_id) - add_segments = ( - self._parse_trunk_segments(context, network['network'], - n1kv.SEGMENT_ADD, - binding.physical_network, - network_profile['sub_type'])) - n1kv_db_v2.add_trunk_segment_binding(session, - net['id'], add_segments) - del_segments = ( - self._parse_trunk_segments(context, network['network'], - n1kv.SEGMENT_DEL, - binding.physical_network, - network_profile['sub_type'])) - n1kv_db_v2.del_trunk_segment_binding(session, - net['id'], del_segments) - self._extend_network_dict_provider(context, net) - self._extend_network_dict_profile(context, net) - if binding.network_type != c_const.NETWORK_TYPE_MULTI_SEGMENT: - self._send_update_network_request(context, net, add_segments, - del_segments) - LOG.debug(_("Updated network: %s"), net['id']) - return net - - def delete_network(self, context, id): - """ - Delete a network. - - :param context: neutron api request context - :param id: UUID representing the network to delete - """ - session = context.session - with session.begin(subtransactions=True): - binding = n1kv_db_v2.get_network_binding(session, id) - network = self.get_network(context, id) - if n1kv_db_v2.is_trunk_member(session, id): - msg = _("Cannot delete network '%s' " - "that is member of a trunk segment") % network['name'] - raise n_exc.InvalidInput(error_message=msg) - if n1kv_db_v2.is_multi_segment_member(session, id): - msg = _("Cannot delete network '%s' that is a member of a " - "multi-segment network") % network['name'] - raise n_exc.InvalidInput(error_message=msg) - if binding.network_type == c_const.NETWORK_TYPE_OVERLAY: - n1kv_db_v2.release_vxlan(session, binding.segmentation_id) - elif binding.network_type == c_const.NETWORK_TYPE_VLAN: - n1kv_db_v2.release_vlan(session, binding.physical_network, - binding.segmentation_id) - self._process_l3_delete(context, id) - super(N1kvNeutronPluginV2, self).delete_network(context, id) - # the network_binding record is deleted via cascade from - # the network record, so explicit removal is not necessary - self._send_delete_network_request(context, network) - LOG.debug(_("Deleted network: %s"), id) - - def get_network(self, context, id, fields=None): - """ - Retrieve a Network. - - :param context: neutron api request context - :param id: UUID representing the network to fetch - :returns: requested network dictionary - """ - LOG.debug(_("Get network: %s"), id) - net = super(N1kvNeutronPluginV2, self).get_network(context, id, None) - self._extend_network_dict_provider(context, net) - self._extend_network_dict_profile(context, net) - self._extend_network_dict_member_segments(context, net) - return self._fields(net, fields) - - def get_networks(self, context, filters=None, fields=None): - """ - Retrieve a list of networks. - - :param context: neutron api request context - :param filters: a dictionary with keys that are valid keys for a - network object. Values in this dictiontary are an - iterable containing values that will be used for an - exact match comparison for that value. Each result - returned by this function will have matched one of the - values for each key in filters - :params fields: a list of strings that are valid keys in a network - dictionary. Only these fields will be returned. - :returns: list of network dictionaries. - """ - LOG.debug(_("Get networks")) - nets = super(N1kvNeutronPluginV2, self).get_networks(context, filters, - None) - for net in nets: - self._extend_network_dict_provider(context, net) - self._extend_network_dict_profile(context, net) - - return [self._fields(net, fields) for net in nets] - - def create_port(self, context, port): - """ - Create neutron port. - - Create a port. Use a default policy profile for ports created for dhcp - and router interface. Default policy profile name is configured in the - /etc/neutron/cisco_plugins.ini file. - - :param context: neutron api request context - :param port: port dictionary - :returns: port object - """ - p_profile = None - port_count = None - vm_network_name = None - profile_id_set = False - - # Set the network policy profile id for auto generated L3/DHCP ports - if ('device_id' in port['port'] and port['port']['device_owner'] in - [constants.DEVICE_OWNER_DHCP, constants.DEVICE_OWNER_ROUTER_INTF, - constants.DEVICE_OWNER_ROUTER_GW, - constants.DEVICE_OWNER_FLOATINGIP]): - p_profile_name = c_conf.CISCO_N1K.network_node_policy_profile - p_profile = self._get_policy_profile_by_name(p_profile_name) - if p_profile: - port['port']['n1kv:profile_id'] = p_profile['id'] - - if n1kv.PROFILE_ID in port['port']: - profile_id = port['port'].get(n1kv.PROFILE_ID) - profile_id_set = attributes.is_attr_set(profile_id) - - # Set the default policy profile id for ports if no id is set - if not profile_id_set: - p_profile_name = c_conf.CISCO_N1K.default_policy_profile - p_profile = self._get_policy_profile_by_name(p_profile_name) - if p_profile: - port['port']['n1kv:profile_id'] = p_profile['id'] - profile_id_set = True - - profile_id = self._process_policy_profile(context, - port['port']) - LOG.debug(_('Create port: profile_id=%s'), profile_id) - session = context.session - with session.begin(subtransactions=True): - pt = super(N1kvNeutronPluginV2, self).create_port(context, - port) - n1kv_db_v2.add_port_binding(session, pt['id'], profile_id) - self._extend_port_dict_profile(context, pt) - try: - vm_network = n1kv_db_v2.get_vm_network( - context.session, - profile_id, - pt['network_id']) - except cisco_exceptions.VMNetworkNotFound: - # Create a VM Network if no VM network exists. - vm_network_name = "%s%s_%s" % (c_const.VM_NETWORK_NAME_PREFIX, - profile_id, - pt['network_id']) - port_count = 1 - n1kv_db_v2.add_vm_network(context.session, - vm_network_name, - profile_id, - pt['network_id'], - port_count) - else: - # Update port count of the VM network. - vm_network_name = vm_network['name'] - port_count = vm_network['port_count'] + 1 - n1kv_db_v2.update_vm_network_port_count(context.session, - vm_network_name, - port_count) - self._process_portbindings_create_and_update(context, - port['port'], - pt) - # Extract policy profile for VM network create in VSM. - if not p_profile: - p_profile = n1kv_db_v2.get_policy_profile(session, profile_id) - try: - self._send_create_port_request(context, - pt, - port_count, - p_profile, - vm_network_name) - except(cisco_exceptions.VSMError, - cisco_exceptions.VSMConnectionFailed): - super(N1kvNeutronPluginV2, self).delete_port(context, pt['id']) - else: - LOG.debug(_("Created port: %s"), pt) - return pt - - def update_port(self, context, id, port): - """ - Update port parameters. - - :param context: neutron api request context - :param id: UUID representing the port to update - :returns: updated port object - """ - LOG.debug(_("Update port: %s"), id) - with context.session.begin(subtransactions=True): - updated_port = super(N1kvNeutronPluginV2, - self).update_port(context, id, port) - self._process_portbindings_create_and_update(context, - port['port'], - updated_port) - self._extend_port_dict_profile(context, updated_port) - return updated_port - - def delete_port(self, context, id, l3_port_check=True): - """ - Delete a port. - - :param context: neutron api request context - :param id: UUID representing the port to delete - """ - # if needed, check to see if this is a port owned by - # and l3-router. If so, we should prevent deletion. - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - with context.session.begin(subtransactions=True): - port = self.get_port(context, id) - vm_network = n1kv_db_v2.get_vm_network(context.session, - port[n1kv.PROFILE_ID], - port['network_id']) - vm_network['port_count'] -= 1 - n1kv_db_v2.update_vm_network_port_count(context.session, - vm_network['name'], - vm_network['port_count']) - if vm_network['port_count'] == 0: - n1kv_db_v2.delete_vm_network(context.session, - port[n1kv.PROFILE_ID], - port['network_id']) - self.disassociate_floatingips(context, id) - super(N1kvNeutronPluginV2, self).delete_port(context, id) - self._send_delete_port_request(context, port, vm_network) - - def get_port(self, context, id, fields=None): - """ - Retrieve a port. - :param context: neutron api request context - :param id: UUID representing the port to retrieve - :param fields: a list of strings that are valid keys in a port - dictionary. Only these fields will be returned. - :returns: port dictionary - """ - LOG.debug(_("Get port: %s"), id) - port = super(N1kvNeutronPluginV2, self).get_port(context, id, None) - self._extend_port_dict_profile(context, port) - return self._fields(port, fields) - - def get_ports(self, context, filters=None, fields=None): - """ - Retrieve a list of ports. - - :param context: neutron api request context - :param filters: a dictionary with keys that are valid keys for a - port object. Values in this dictiontary are an - iterable containing values that will be used for an - exact match comparison for that value. Each result - returned by this function will have matched one of the - values for each key in filters - :params fields: a list of strings that are valid keys in a port - dictionary. Only these fields will be returned. - :returns: list of port dictionaries - """ - LOG.debug(_("Get ports")) - ports = super(N1kvNeutronPluginV2, self).get_ports(context, filters, - None) - for port in ports: - self._extend_port_dict_profile(context, port) - - return [self._fields(port, fields) for port in ports] - - def create_subnet(self, context, subnet): - """ - Create subnet for a given network. - - :param context: neutron api request context - :param subnet: subnet dictionary - :returns: subnet object - """ - LOG.debug(_('Create subnet')) - sub = super(N1kvNeutronPluginV2, self).create_subnet(context, subnet) - try: - self._send_create_subnet_request(context, sub) - except(cisco_exceptions.VSMError, - cisco_exceptions.VSMConnectionFailed): - super(N1kvNeutronPluginV2, self).delete_subnet(context, sub['id']) - else: - LOG.debug(_("Created subnet: %s"), sub['id']) - return sub - - def update_subnet(self, context, id, subnet): - """ - Update a subnet. - - :param context: neutron api request context - :param id: UUID representing subnet to update - :returns: updated subnet object - """ - LOG.debug(_('Update subnet')) - sub = super(N1kvNeutronPluginV2, self).update_subnet(context, - id, - subnet) - self._send_update_subnet_request(sub) - return sub - - def delete_subnet(self, context, id): - """ - Delete a subnet. - - :param context: neutron api request context - :param id: UUID representing subnet to delete - :returns: deleted subnet object - """ - LOG.debug(_('Delete subnet: %s'), id) - subnet = self.get_subnet(context, id) - self._send_delete_subnet_request(context, subnet) - return super(N1kvNeutronPluginV2, self).delete_subnet(context, id) - - def get_subnet(self, context, id, fields=None): - """ - Retrieve a subnet. - - :param context: neutron api request context - :param id: UUID representing subnet to retrieve - :params fields: a list of strings that are valid keys in a subnet - dictionary. Only these fields will be returned. - :returns: subnet object - """ - LOG.debug(_("Get subnet: %s"), id) - subnet = super(N1kvNeutronPluginV2, self).get_subnet(context, id, - None) - return self._fields(subnet, fields) - - def get_subnets(self, context, filters=None, fields=None): - """ - Retrieve a list of subnets. - - :param context: neutron api request context - :param filters: a dictionary with keys that are valid keys for a - subnet object. Values in this dictiontary are an - iterable containing values that will be used for an - exact match comparison for that value. Each result - returned by this function will have matched one of the - values for each key in filters - :params fields: a list of strings that are valid keys in a subnet - dictionary. Only these fields will be returned. - :returns: list of dictionaries of subnets - """ - LOG.debug(_("Get subnets")) - subnets = super(N1kvNeutronPluginV2, self).get_subnets(context, - filters, - None) - return [self._fields(subnet, fields) for subnet in subnets] - - def create_network_profile(self, context, network_profile): - """ - Create a network profile. - - Create a network profile, which represents a pool of networks - belonging to one type (VLAN or Overlay). On creation of network - profile, we retrieve the admin tenant-id which we use to replace - the previously stored fake tenant-id in tenant-profile bindings. - :param context: neutron api request context - :param network_profile: network profile dictionary - :returns: network profile object - """ - self._replace_fake_tenant_id_with_real(context) - with context.session.begin(subtransactions=True): - net_p = super(N1kvNeutronPluginV2, - self).create_network_profile(context, - network_profile) - try: - self._send_create_logical_network_request(net_p, - context.tenant_id) - except(cisco_exceptions.VSMError, - cisco_exceptions.VSMConnectionFailed): - n1kv_db_v2.delete_profile_binding(context.session, - context.tenant_id, - net_p['id']) - try: - self._send_create_network_profile_request(context, net_p) - except(cisco_exceptions.VSMError, - cisco_exceptions.VSMConnectionFailed): - n1kv_db_v2.delete_profile_binding(context.session, - context.tenant_id, - net_p['id']) - self._send_delete_logical_network_request(net_p) - return net_p - - def delete_network_profile(self, context, id): - """ - Delete a network profile. - - :param context: neutron api request context - :param id: UUID of the network profile to delete - :returns: deleted network profile object - """ - with context.session.begin(subtransactions=True): - net_p = super(N1kvNeutronPluginV2, - self).delete_network_profile(context, id) - self._send_delete_network_profile_request(net_p) - self._send_delete_logical_network_request(net_p) - - def update_network_profile(self, context, net_profile_id, network_profile): - """ - Update a network profile. - - :param context: neutron api request context - :param net_profile_id: UUID of the network profile to update - :param network_profile: dictionary containing network profile object - """ - session = context.session - with session.begin(subtransactions=True): - net_p = (super(N1kvNeutronPluginV2, self). - update_network_profile(context, - net_profile_id, - network_profile)) - self._send_update_network_profile_request(net_p) - return net_p - - def create_router(self, context, router): - """ - Handle creation of router. - - Schedule router to L3 agent as part of the create handling. - :param context: neutron api request context - :param router: router dictionary - :returns: router object - """ - session = context.session - with session.begin(subtransactions=True): - rtr = (super(N1kvNeutronPluginV2, self). - create_router(context, router)) - LOG.debug(_("Scheduling router %s"), rtr['id']) - self.schedule_router(context, rtr['id']) - return rtr diff --git a/neutron/plugins/cisco/network_plugin.py b/neutron/plugins/cisco/network_plugin.py deleted file mode 100644 index ee35fec81..000000000 --- a/neutron/plugins/cisco/network_plugin.py +++ /dev/null @@ -1,176 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. - -import logging - -import webob.exc as wexc - -from neutron.api import extensions as neutron_extensions -from neutron.api.v2 import base -from neutron.db import db_base_plugin_v2 -from neutron.openstack.common import importutils -from neutron.plugins.cisco.common import cisco_exceptions as cexc -from neutron.plugins.cisco.common import config -from neutron.plugins.cisco.db import network_db_v2 as cdb -from neutron.plugins.cisco import extensions - -LOG = logging.getLogger(__name__) - - -class PluginV2(db_base_plugin_v2.NeutronDbPluginV2): - """Meta-Plugin with v2 API support for multiple sub-plugins.""" - _supported_extension_aliases = ["credential", "Cisco qos"] - _methods_to_delegate = ['create_network', - 'delete_network', 'update_network', 'get_network', - 'get_networks', - 'create_port', 'delete_port', - 'update_port', 'get_port', 'get_ports', - 'create_subnet', - 'delete_subnet', 'update_subnet', - 'get_subnet', 'get_subnets', ] - - CISCO_FAULT_MAP = { - cexc.CredentialAlreadyExists: wexc.HTTPBadRequest, - cexc.CredentialNameNotFound: wexc.HTTPNotFound, - cexc.CredentialNotFound: wexc.HTTPNotFound, - cexc.NetworkSegmentIDNotFound: wexc.HTTPNotFound, - cexc.NetworkVlanBindingAlreadyExists: wexc.HTTPBadRequest, - cexc.NexusComputeHostNotConfigured: wexc.HTTPNotFound, - cexc.NexusConfigFailed: wexc.HTTPBadRequest, - cexc.NexusConnectFailed: wexc.HTTPServiceUnavailable, - cexc.NexusPortBindingNotFound: wexc.HTTPNotFound, - cexc.NoMoreNics: wexc.HTTPBadRequest, - cexc.PortIdForNexusSvi: wexc.HTTPBadRequest, - cexc.PortVnicBindingAlreadyExists: wexc.HTTPBadRequest, - cexc.PortVnicNotFound: wexc.HTTPNotFound, - cexc.QosNameAlreadyExists: wexc.HTTPBadRequest, - cexc.QosNotFound: wexc.HTTPNotFound, - cexc.SubnetNotSpecified: wexc.HTTPBadRequest, - cexc.VlanIDNotAvailable: wexc.HTTPNotFound, - cexc.VlanIDNotFound: wexc.HTTPNotFound, - } - - @property - def supported_extension_aliases(self): - if not hasattr(self, '_aliases'): - aliases = self._supported_extension_aliases[:] - if hasattr(self._model, "supported_extension_aliases"): - aliases.extend(self._model.supported_extension_aliases) - self._aliases = aliases - return self._aliases - - def __init__(self): - """Load the model class.""" - self._model_name = config.CISCO.model_class - self._model = importutils.import_object(self._model_name) - native_bulk_attr_name = ("_%s__native_bulk_support" - % self._model.__class__.__name__) - self.__native_bulk_support = getattr(self._model, - native_bulk_attr_name, False) - - neutron_extensions.append_api_extensions_path(extensions.__path__) - - # Extend the fault map - self._extend_fault_map() - - LOG.debug(_("Plugin initialization complete")) - - def __getattribute__(self, name): - """Delegate core API calls to the model class. - - Core API calls are delegated directly to the configured model class. - Note: Bulking calls will be handled by this class, and turned into - non-bulking calls to be considered for delegation. - """ - methods = object.__getattribute__(self, "_methods_to_delegate") - if name in methods: - return getattr(object.__getattribute__(self, "_model"), - name) - else: - return object.__getattribute__(self, name) - - def __getattr__(self, name): - """Delegate calls to the extensions. - - This delegates the calls to the extensions explicitly implemented by - the model. - """ - if hasattr(self._model, name): - return getattr(self._model, name) - else: - # Must make sure we re-raise the error that led us here, since - # otherwise getattr() and even hasattr() doesn't work correctly. - raise AttributeError( - _("'%(model)s' object has no attribute '%(name)s'") % - {'model': self._model_name, 'name': name}) - - def _extend_fault_map(self): - """Extend the Neutron Fault Map for Cisco exceptions. - - Map exceptions which are specific to the Cisco Plugin - to standard HTTP exceptions. - - """ - base.FAULT_MAP.update(self.CISCO_FAULT_MAP) - - """ - Extension API implementation - """ - def get_all_qoss(self, tenant_id): - """Get all QoS levels.""" - LOG.debug(_("get_all_qoss() called")) - qoslist = cdb.get_all_qoss(tenant_id) - return qoslist - - def get_qos_details(self, tenant_id, qos_id): - """Get QoS Details.""" - LOG.debug(_("get_qos_details() called")) - return cdb.get_qos(tenant_id, qos_id) - - def create_qos(self, tenant_id, qos_name, qos_desc): - """Create a QoS level.""" - LOG.debug(_("create_qos() called")) - qos = cdb.add_qos(tenant_id, qos_name, str(qos_desc)) - return qos - - def delete_qos(self, tenant_id, qos_id): - """Delete a QoS level.""" - LOG.debug(_("delete_qos() called")) - return cdb.remove_qos(tenant_id, qos_id) - - def rename_qos(self, tenant_id, qos_id, new_name): - """Rename QoS level.""" - LOG.debug(_("rename_qos() called")) - return cdb.update_qos(tenant_id, qos_id, new_name) - - def get_all_credentials(self): - """Get all credentials.""" - LOG.debug(_("get_all_credentials() called")) - credential_list = cdb.get_all_credentials() - return credential_list - - def get_credential_details(self, credential_id): - """Get a particular credential.""" - LOG.debug(_("get_credential_details() called")) - return cdb.get_credential(credential_id) - - def rename_credential(self, credential_id, new_name, new_password): - """Rename the particular credential resource.""" - LOG.debug(_("rename_credential() called")) - return cdb.update_credential(credential_id, new_name, - new_password=new_password) diff --git a/neutron/plugins/cisco/nexus/__init__.py b/neutron/plugins/cisco/nexus/__init__.py deleted file mode 100644 index 963eb547f..000000000 --- a/neutron/plugins/cisco/nexus/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Edgar Magana, Cisco Systems, Inc. -""" -Init module for Nexus Driver -""" diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py b/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py deleted file mode 100644 index bef145f03..000000000 --- a/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py +++ /dev/null @@ -1,196 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Debojyoti Dutta, Cisco Systems, Inc. -# @author: Edgar Magana, Cisco Systems Inc. -# -""" -Implements a Nexus-OS NETCONF over SSHv2 API Client -""" - -import logging - -from ncclient import manager - -from neutron.openstack.common import excutils -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_credentials_v2 as cred -from neutron.plugins.cisco.common import cisco_exceptions as cexc -from neutron.plugins.cisco.common import config as conf -from neutron.plugins.cisco.db import nexus_db_v2 -from neutron.plugins.cisco.nexus import cisco_nexus_snippets as snipp - -LOG = logging.getLogger(__name__) - - -class CiscoNEXUSDriver(): - """Nexus Driver Main Class.""" - def __init__(self): - cisco_switches = conf.get_device_dictionary() - self.nexus_switches = dict(((key[1], key[2]), val) - for key, val in cisco_switches.items() - if key[0] == 'NEXUS_SWITCH') - self.credentials = {} - self.connections = {} - - def _edit_config(self, nexus_host, target='running', config='', - allowed_exc_strs=None): - """Modify switch config for a target config type. - - :param nexus_host: IP address of switch to configure - :param target: Target config type - :param config: Configuration string in XML format - :param allowed_exc_strs: Exceptions which have any of these strings - as a subset of their exception message - (str(exception)) can be ignored - - :raises: NexusConfigFailed - - """ - if not allowed_exc_strs: - allowed_exc_strs = [] - mgr = self.nxos_connect(nexus_host) - try: - mgr.edit_config(target, config=config) - except Exception as e: - for exc_str in allowed_exc_strs: - if exc_str in str(e): - break - else: - # Raise a Neutron exception. Include a description of - # the original ncclient exception. No need to preserve T/B - raise cexc.NexusConfigFailed(config=config, exc=e) - - def get_credential(self, nexus_ip): - if nexus_ip not in self.credentials: - nexus_username = cred.Store.get_username(nexus_ip) - nexus_password = cred.Store.get_password(nexus_ip) - self.credentials[nexus_ip] = { - const.USERNAME: nexus_username, - const.PASSWORD: nexus_password - } - return self.credentials[nexus_ip] - - def nxos_connect(self, nexus_host): - """Make SSH connection to the Nexus Switch.""" - if getattr(self.connections.get(nexus_host), 'connected', None): - return self.connections[nexus_host] - - nexus_ssh_port = int(self.nexus_switches[nexus_host, 'ssh_port']) - nexus_creds = self.get_credential(nexus_host) - nexus_user = nexus_creds[const.USERNAME] - nexus_password = nexus_creds[const.PASSWORD] - try: - man = manager.connect(host=nexus_host, - port=nexus_ssh_port, - username=nexus_user, - password=nexus_password) - self.connections[nexus_host] = man - except Exception as e: - # Raise a Neutron exception. Include a description of - # the original ncclient exception. No need to preserve T/B. - raise cexc.NexusConnectFailed(nexus_host=nexus_host, exc=e) - - return self.connections[nexus_host] - - def create_xml_snippet(self, cutomized_config): - """Create XML snippet. - - Creates the Proper XML structure for the Nexus Switch Configuration. - """ - conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (cutomized_config) - return conf_xml_snippet - - def create_vlan(self, nexus_host, vlanid, vlanname): - """Create a VLAN on Nexus Switch given the VLAN ID and Name.""" - confstr = self.create_xml_snippet( - snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname)) - self._edit_config(nexus_host, target='running', config=confstr) - - # Enable VLAN active and no-shutdown states. Some versions of - # Nexus switch do not allow state changes for the extended VLAN - # range (1006-4094), but these errors can be ignored (default - # values are appropriate). - state_config = [snipp.CMD_VLAN_ACTIVE_SNIPPET, - snipp.CMD_VLAN_NO_SHUTDOWN_SNIPPET] - for snippet in state_config: - try: - confstr = self.create_xml_snippet(snippet % vlanid) - self._edit_config( - nexus_host, - target='running', - config=confstr, - allowed_exc_strs=["Can't modify state for extended", - "Command is only allowed on VLAN"]) - except cexc.NexusConfigFailed: - with excutils.save_and_reraise_exception(): - self.delete_vlan(nexus_host, vlanid) - - def delete_vlan(self, nexus_host, vlanid): - """Delete a VLAN on Nexus Switch given the VLAN ID.""" - confstr = snipp.CMD_NO_VLAN_CONF_SNIPPET % vlanid - confstr = self.create_xml_snippet(confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def enable_vlan_on_trunk_int(self, nexus_host, vlanid, etype, interface): - """Enable a VLAN on a trunk interface.""" - # If one or more VLANs are already configured on this interface, - # include the 'add' keyword. - if nexus_db_v2.get_port_switch_bindings('%s:%s' % (etype, interface), - nexus_host): - snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET - else: - snippet = snipp.CMD_INT_VLAN_SNIPPET - confstr = snippet % (etype, interface, vlanid, etype) - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def disable_vlan_on_trunk_int(self, nexus_host, vlanid, etype, interface): - """Disable a VLAN on a trunk interface.""" - confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (etype, interface, - vlanid, etype) - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name, - etype, nexus_port): - """Create VLAN and trunk it on the specified ports.""" - self.create_vlan(nexus_host, vlan_id, vlan_name) - LOG.debug(_("NexusDriver created VLAN: %s"), vlan_id) - if nexus_port: - self.enable_vlan_on_trunk_int(nexus_host, vlan_id, - etype, nexus_port) - - def delete_and_untrunk_vlan(self, nexus_host, vlan_id, etype, nexus_port): - """Delete VLAN and untrunk it from the specified ports.""" - self.delete_vlan(nexus_host, vlan_id) - if nexus_port: - self.disable_vlan_on_trunk_int(nexus_host, vlan_id, - etype, nexus_port) - - def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip): - confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip) - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def delete_vlan_svi(self, nexus_host, vlan_id): - confstr = snipp.CMD_NO_VLAN_SVI_SNIPPET % vlan_id - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py b/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py deleted file mode 100644 index e9e34811a..000000000 --- a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py +++ /dev/null @@ -1,347 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Edgar Magana, Cisco Systems, Inc. -# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com) -# - -""" -PlugIn for Nexus OS driver -""" - -import logging - -from neutron.openstack.common import excutils -from neutron.openstack.common import importutils -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_exceptions as cisco_exc -from neutron.plugins.cisco.common import config as conf -from neutron.plugins.cisco.db import network_db_v2 as cdb -from neutron.plugins.cisco.db import nexus_db_v2 as nxos_db -from neutron.plugins.cisco import l2device_plugin_base - - -LOG = logging.getLogger(__name__) - - -class NexusPlugin(l2device_plugin_base.L2DevicePluginBase): - """Nexus PlugIn Main Class.""" - _networks = {} - - def __init__(self): - """Extract configuration parameters from the configuration file.""" - self._client = importutils.import_object(conf.CISCO.nexus_driver) - LOG.debug(_("Loaded driver %s"), conf.CISCO.nexus_driver) - self._nexus_switches = conf.get_device_dictionary() - - def create_network(self, network, attachment): - """Create or update a network when an attachment is changed. - - This method is not invoked at the usual plugin create_network() time. - Instead, it is invoked on create/update port. - - :param network: Network on which the port operation is happening - :param attachment: Details about the owner of the port - - Create a VLAN in the appropriate switch/port, and configure the - appropriate interfaces for this VLAN. - """ - LOG.debug(_("NexusPlugin:create_network() called")) - # Grab the switch IPs and ports for this host - host_connections = [] - host = attachment['host_name'] - for switch_type, switch_ip, attr in self._nexus_switches: - if str(attr) == str(host): - port = self._nexus_switches[switch_type, switch_ip, attr] - # Get ether type for port, assume an ethernet type - # if none specified. - if ':' in port: - etype, port_id = port.split(':') - else: - etype, port_id = 'ethernet', port - host_connections.append((switch_ip, etype, port_id)) - if not host_connections: - raise cisco_exc.NexusComputeHostNotConfigured(host=host) - - vlan_id = network[const.NET_VLAN_ID] - vlan_name = network[const.NET_VLAN_NAME] - auto_create = True - auto_trunk = True - if cdb.is_provider_vlan(vlan_id): - vlan_name = ''.join([conf.CISCO.provider_vlan_name_prefix, - str(vlan_id)]) - auto_create = conf.CISCO.provider_vlan_auto_create - auto_trunk = conf.CISCO.provider_vlan_auto_trunk - - # Check if this network is already in the DB - for switch_ip, etype, port_id in host_connections: - vlan_created = False - vlan_trunked = False - eport_id = '%s:%s' % (etype, port_id) - # Check for switch vlan bindings - try: - # This vlan has already been created on this switch - # via another operation, like SVI bindings. - nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) - vlan_created = True - auto_create = False - except cisco_exc.NexusPortBindingNotFound: - # No changes, proceed as normal - pass - - try: - nxos_db.get_port_vlan_switch_binding(eport_id, vlan_id, - switch_ip) - except cisco_exc.NexusPortBindingNotFound: - if auto_create and auto_trunk: - # Create vlan and trunk vlan on the port - LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name) - self._client.create_and_trunk_vlan( - switch_ip, vlan_id, vlan_name, etype, port_id) - vlan_created = True - vlan_trunked = True - elif auto_create: - # Create vlan but do not trunk it on the port - LOG.debug(_("Nexus: create vlan %s"), vlan_name) - self._client.create_vlan(switch_ip, vlan_id, vlan_name) - vlan_created = True - elif auto_trunk: - # Only trunk vlan on the port - LOG.debug(_("Nexus: trunk vlan %s"), vlan_name) - self._client.enable_vlan_on_trunk_int( - switch_ip, vlan_id, etype, port_id) - vlan_trunked = True - - try: - instance = attachment[const.INSTANCE_ID] - nxos_db.add_nexusport_binding(eport_id, str(vlan_id), - switch_ip, instance) - except Exception: - with excutils.save_and_reraise_exception(): - # Add binding failed, roll back any vlan creation/enabling - if vlan_created and vlan_trunked: - LOG.debug(_("Nexus: delete & untrunk vlan %s"), - vlan_name) - self._client.delete_and_untrunk_vlan(switch_ip, - vlan_id, - etype, port_id) - elif vlan_created: - LOG.debug(_("Nexus: delete vlan %s"), vlan_name) - self._client.delete_vlan(switch_ip, vlan_id) - elif vlan_trunked: - LOG.debug(_("Nexus: untrunk vlan %s"), vlan_name) - self._client.disable_vlan_on_trunk_int(switch_ip, - vlan_id, - etype, - port_id) - - net_id = network[const.NET_ID] - new_net_dict = {const.NET_ID: net_id, - const.NET_NAME: network[const.NET_NAME], - const.NET_PORTS: {}, - const.NET_VLAN_NAME: vlan_name, - const.NET_VLAN_ID: vlan_id} - self._networks[net_id] = new_net_dict - return new_net_dict - - def add_router_interface(self, vlan_name, vlan_id, subnet_id, - gateway_ip, router_id): - """Create VLAN SVI on the Nexus switch.""" - # Find a switch to create the SVI on - switch_ip = self._find_switch_for_svi() - if not switch_ip: - raise cisco_exc.NoNexusSviSwitch() - - # Check if this vlan exists on the switch already - try: - nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) - except cisco_exc.NexusPortBindingNotFound: - # Create vlan and trunk vlan on the port - self._client.create_and_trunk_vlan( - switch_ip, vlan_id, vlan_name, etype=None, nexus_port=None) - # Check if a router interface has already been created - try: - nxos_db.get_nexusvm_bindings(vlan_id, router_id) - raise cisco_exc.SubnetInterfacePresent(subnet_id=subnet_id, - router_id=router_id) - except cisco_exc.NexusPortBindingNotFound: - self._client.create_vlan_svi(switch_ip, vlan_id, gateway_ip) - nxos_db.add_nexusport_binding('router', str(vlan_id), - switch_ip, router_id) - - return True - - def remove_router_interface(self, vlan_id, router_id): - """Remove VLAN SVI from the Nexus Switch.""" - # Grab switch_ip from database - switch_ip = nxos_db.get_nexusvm_bindings(vlan_id, - router_id)[0].switch_ip - - # Delete the SVI interface from the switch - self._client.delete_vlan_svi(switch_ip, vlan_id) - - # Invoke delete_port to delete this row - # And delete vlan if required - return self.delete_port(router_id, vlan_id) - - def _find_switch_for_svi(self): - """Get a switch to create the SVI on.""" - LOG.debug(_("Grabbing a switch to create SVI")) - nexus_switches = self._client.nexus_switches - if conf.CISCO.svi_round_robin: - LOG.debug(_("Using round robin to create SVI")) - switch_dict = dict( - (switch_ip, 0) for switch_ip, _ in nexus_switches) - try: - bindings = nxos_db.get_nexussvi_bindings() - # Build a switch dictionary with weights - for binding in bindings: - switch_ip = binding.switch_ip - if switch_ip not in switch_dict: - switch_dict[switch_ip] = 1 - else: - switch_dict[switch_ip] += 1 - # Search for the lowest value in the dict - if switch_dict: - switch_ip = min(switch_dict, key=switch_dict.get) - return switch_ip - except cisco_exc.NexusPortBindingNotFound: - pass - - LOG.debug(_("No round robin or zero weights, using first switch")) - # Return the first switch in the config - return conf.first_device_ip - - def delete_network(self, tenant_id, net_id, **kwargs): - """Delete network. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:delete_network() called")) # pragma no cover - - def update_network(self, tenant_id, net_id, **kwargs): - """Update the properties of a particular Virtual Network. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:update_network() called")) # pragma no cover - - def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): - """Create port. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:create_port() called")) # pragma no cover - - def delete_port(self, device_id, vlan_id): - """Delete port. - - Delete port bindings from the database and scan whether the network - is still required on the interfaces trunked. - """ - LOG.debug(_("NexusPlugin:delete_port() called")) - # Delete DB row(s) for this port - try: - rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id) - except cisco_exc.NexusPortBindingNotFound: - return - - auto_delete = True - auto_untrunk = True - if cdb.is_provider_vlan(vlan_id): - auto_delete = conf.CISCO.provider_vlan_auto_create - auto_untrunk = conf.CISCO.provider_vlan_auto_trunk - LOG.debug(_("delete_network(): provider vlan %s"), vlan_id) - - instance_id = False - for row in rows: - instance_id = row['instance_id'] - switch_ip = row.switch_ip - etype, nexus_port = '', '' - if row['port_id'] == 'router': - etype, nexus_port = 'vlan', row['port_id'] - auto_untrunk = False - else: - etype, nexus_port = row['port_id'].split(':') - - nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id, - row.switch_ip, - row.instance_id) - # Check whether there are any remaining instances using this - # vlan on this Nexus port. - try: - nxos_db.get_port_vlan_switch_binding(row.port_id, - row.vlan_id, - row.switch_ip) - except cisco_exc.NexusPortBindingNotFound: - try: - if nexus_port and auto_untrunk: - # Untrunk the vlan from this Nexus interface - self._client.disable_vlan_on_trunk_int( - switch_ip, row.vlan_id, etype, nexus_port) - - # Check whether there are any remaining instances - # using this vlan on the Nexus switch. - if auto_delete: - try: - nxos_db.get_nexusvlan_binding(row.vlan_id, - row.switch_ip) - except cisco_exc.NexusPortBindingNotFound: - # Delete this vlan from this switch - self._client.delete_vlan(switch_ip, row.vlan_id) - except Exception: - # The delete vlan operation on the Nexus failed, - # so this delete_port request has failed. For - # consistency, roll back the Nexus database to what - # it was before this request. - with excutils.save_and_reraise_exception(): - nxos_db.add_nexusport_binding(row.port_id, - row.vlan_id, - row.switch_ip, - row.instance_id) - - return instance_id - - def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs): - """Update port. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:update_port() called")) # pragma no cover - - def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, - **kwargs): - """Plug interfaces. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:plug_interface() called")) # pragma no cover - - def unplug_interface(self, tenant_id, net_id, port_id, **kwargs): - """Unplug interface. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:unplug_interface() called") - ) # pragma no cover diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py b/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py deleted file mode 100644 index 90d265443..000000000 --- a/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py +++ /dev/null @@ -1,180 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 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. -# -# @author: Edgar Magana, Cisco Systems, Inc. -# @author: Arvind Somya (asomya@cisco.com) Cisco Systems, Inc. - -""" -Nexus-OS XML-based configuration snippets -""" - -import logging - - -LOG = logging.getLogger(__name__) - - -# The following are standard strings, messages used to communicate with Nexus, -EXEC_CONF_SNIPPET = """ - - - <__XML__MODE__exec_configure>%s - - - -""" - -CMD_VLAN_CONF_SNIPPET = """ - - - <__XML__PARAM_value>%s - <__XML__MODE_vlan> - - %s - - - - -""" - -CMD_VLAN_ACTIVE_SNIPPET = """ - - - <__XML__PARAM_value>%s - <__XML__MODE_vlan> - - active - - - - -""" - -CMD_VLAN_NO_SHUTDOWN_SNIPPET = """ - - - <__XML__PARAM_value>%s - <__XML__MODE_vlan> - - - - - - -""" - -CMD_NO_VLAN_CONF_SNIPPET = """ - - - - <__XML__PARAM_value>%s - - - -""" - -CMD_INT_VLAN_HEADER = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - """ - -CMD_VLAN_ID = """ - %s""" - -CMD_VLAN_ADD_ID = """ - %s - """ % CMD_VLAN_ID - -CMD_INT_VLAN_TRAILER = """ - - - - - - - -""" - -CMD_INT_VLAN_SNIPPET = (CMD_INT_VLAN_HEADER + - CMD_VLAN_ID + - CMD_INT_VLAN_TRAILER) - -CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER + - CMD_VLAN_ADD_ID + - CMD_INT_VLAN_TRAILER) - -CMD_NO_VLAN_INT_SNIPPET = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - - - - %s - - - - - - - - -""" - -FILTER_SHOW_VLAN_BRIEF_SNIPPET = """ - - - - - -""" - -CMD_VLAN_SVI_SNIPPET = """ - - - %s - <__XML__MODE_vlan> - - - - -
-
%s
-
-
- -
-
-""" - -CMD_NO_VLAN_SVI_SNIPPET = """ - - - - %s - - - -""" diff --git a/neutron/plugins/cisco/test/__init__.py b/neutron/plugins/cisco/test/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/cisco/test/nexus/__init__.py b/neutron/plugins/cisco/test/nexus/__init__.py deleted file mode 100644 index a68ed41ea..000000000 --- a/neutron/plugins/cisco/test/nexus/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 OpenStack Foundation. -# 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 __builtin__ -setattr(__builtin__, '_', lambda x: x) diff --git a/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py b/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py deleted file mode 100644 index b40cbef14..000000000 --- a/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py +++ /dev/null @@ -1,101 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Rohit Agarwalla, Cisco Systems, Inc. - - -class CiscoNEXUSFakeDriver(): - """Nexus Driver Fake Class.""" - - def __init__(self): - pass - - def nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user, - nexus_password): - """Make the fake connection to the Nexus Switch.""" - pass - - def create_xml_snippet(self, cutomized_config): - """Create XML snippet. - - Creates the Proper XML structure for the Nexus Switch - Configuration. - """ - pass - - def enable_vlan(self, mgr, vlanid, vlanname): - """Create a VLAN on Nexus Switch given the VLAN ID and Name.""" - pass - - def disable_vlan(self, mgr, vlanid): - """Delete a VLAN on Nexus Switch given the VLAN ID.""" - pass - - def disable_switch_port(self, mgr, interface): - """Disable trunk mode an interface on Nexus Switch.""" - pass - - def enable_vlan_on_trunk_int(self, mgr, etype, interface, vlanid): - """Enable vlan on trunk interface. - - Enable trunk mode vlan access an interface on Nexus Switch given - VLANID. - """ - pass - - def disable_vlan_on_trunk_int(self, mgr, interface, vlanid): - """Disables vlan in trunk interface. - - Enables trunk mode vlan access an interface on Nexus Switch given - VLANID. - """ - pass - - def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user, - nexus_password, nexus_ports, nexus_ssh_port, vlan_ids): - """Create VLAN and enable it on interface. - - Creates a VLAN and Enable on trunk mode an interface on Nexus Switch - given the VLAN ID and Name and Interface Number. - """ - pass - - def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password, - nexus_ports, nexus_ssh_port): - """Delete VLAN. - - Delete a VLAN and Disables trunk mode an interface on Nexus Switch - given the VLAN ID and Interface Number. - """ - pass - - def build_vlans_cmd(self): - """Build a string with all the VLANs on the same Switch.""" - pass - - def add_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password, - nexus_ports, nexus_ssh_port, vlan_ids=None): - """Add a vlan from interfaces on the Nexus switch given the VLAN ID.""" - pass - - def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password, - nexus_ports, nexus_ssh_port): - """Remove vlan from interfaces. - - Removes a vlan from interfaces on the Nexus switch given the VLAN ID. - """ - pass diff --git a/neutron/plugins/embrane/README b/neutron/plugins/embrane/README deleted file mode 100644 index 15ad1abbd..000000000 --- a/neutron/plugins/embrane/README +++ /dev/null @@ -1,9 +0,0 @@ -Embrane Neutron Plugin - -This plugin interfaces OpenStack Neutron with Embrane's heleos platform, which -provides layer 3-7 network services for cloud environments. - -L2 connectivity is leveraged by one of the supported existing plugins. - -For more details on use, configuration and implementation please refer to: -http://wiki.openstack.org/wiki/Neutron/EmbraneNeutronPlugin \ No newline at end of file diff --git a/neutron/plugins/embrane/__init__.py b/neutron/plugins/embrane/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/agent/__init__.py b/neutron/plugins/embrane/agent/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/agent/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/agent/dispatcher.py b/neutron/plugins/embrane/agent/dispatcher.py deleted file mode 100644 index 121abe9ac..000000000 --- a/neutron/plugins/embrane/agent/dispatcher.py +++ /dev/null @@ -1,134 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from eventlet import greenthread -from eventlet import queue -from heleosapi import constants as h_con -from heleosapi import exceptions as h_exc - -from neutron.openstack.common import log as logging -from neutron.plugins.embrane.agent.operations import router_operations -from neutron.plugins.embrane.common import constants as p_con -from neutron.plugins.embrane.common import contexts as ctx - -LOG = logging.getLogger(__name__) - - -class Dispatcher(object): - - def __init__(self, plugin, async=True): - self._async = async - self._plugin = plugin - self.sync_items = dict() - - def dispatch_l3(self, d_context, args=(), kwargs={}): - item = d_context.item - event = d_context.event - n_context = d_context.n_context - chain = d_context.chain - - item_id = item["id"] - handlers = router_operations.handlers - if event in handlers: - for f in handlers[event]: - first_run = False - if item_id not in self.sync_items: - self.sync_items[item_id] = (queue.Queue(),) - first_run = True - self.sync_items[item_id][0].put( - ctx.OperationContext(event, n_context, item, chain, f, - args, kwargs)) - t = None - if first_run: - t = greenthread.spawn(self._consume_l3, - item_id, - self.sync_items[item_id][0], - self._plugin, - self._async) - self.sync_items[item_id] += (t,) - if not self._async: - t = self.sync_items[item_id][1] - t.wait() - - def _consume_l3(self, sync_item, sync_queue, plugin, a_sync): - current_state = None - while True: - try: - # If the DVA is deleted, the thread (and the associated queue) - # can die as well - if current_state == p_con.Status.DELETED: - del self.sync_items[sync_item] - return - try: - # If synchronous op, empty the queue as fast as possible - operation_context = sync_queue.get( - block=a_sync, - timeout=p_con.QUEUE_TIMEOUT) - except queue.Empty: - del self.sync_items[sync_item] - return - # Execute the preliminary operations - (operation_context.chain and - operation_context.chain.execute_all()) - # Execute the main operation, a transient state is maintained - # so that the consumer can decide if it has - # to be burned to the DB - transient_state = None - try: - dva_state = operation_context.function( - plugin._esm_api, - operation_context.n_context.tenant_id, - operation_context.item, - *operation_context.args, - **operation_context.kwargs) - if dva_state == p_con.Status.DELETED: - transient_state = dva_state - else: - if not dva_state: - transient_state = p_con.Status.ERROR - elif dva_state == h_con.DvaState.POWER_ON: - transient_state = p_con.Status.ACTIVE - else: - transient_state = p_con.Status.READY - - except (h_exc.PendingDva, h_exc.DvaNotFound, - h_exc.BrokenInterface, h_exc.DvaCreationFailed, - h_exc.DvaCreationPending, h_exc.BrokenDva, - h_exc.ConfigurationFailed) as ex: - LOG.warning(p_con.error_map[type(ex)] % ex.message) - transient_state = p_con.Status.ERROR - except h_exc.DvaDeleteFailed as ex: - LOG.warning(p_con.error_map[type(ex)] % ex.message) - transient_state = p_con.Status.DELETED - finally: - # if the returned transient state is None, no operations - # are required on the DVA status - if transient_state: - if transient_state == p_con.Status.DELETED: - current_state = plugin._delete_router( - operation_context.n_context, - operation_context.item["id"]) - # Error state cannot be reverted - elif transient_state != p_con.Status.ERROR: - current_state = plugin._update_neutron_state( - operation_context.n_context, - operation_context.item, - transient_state) - except Exception: - LOG.exception(_("Unhandled exception occurred")) diff --git a/neutron/plugins/embrane/agent/operations/__init__.py b/neutron/plugins/embrane/agent/operations/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/agent/operations/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/agent/operations/router_operations.py b/neutron/plugins/embrane/agent/operations/router_operations.py deleted file mode 100644 index a9d35bfd4..000000000 --- a/neutron/plugins/embrane/agent/operations/router_operations.py +++ /dev/null @@ -1,156 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -import functools - -from heleosapi import exceptions as h_exc - -from neutron.openstack.common import log as logging -from neutron.plugins.embrane.common import constants as p_con - -LOG = logging.getLogger(__name__) -handlers = dict() - - -def handler(event, handler): - def wrap(f): - if event not in handler.keys(): - new_func_list = [f] - handler[event] = new_func_list - else: - handler[event].append(f) - - @functools.wraps(f) - def wrapped_f(*args, **kwargs): - return f(*args, **kwargs) - return wrapped_f - return wrap - - -@handler(p_con.Events.CREATE_ROUTER, handlers) -def _create_dva_and_assign_address(api, tenant_id, neutron_router, - flavor, utif_info=None, - ip_allocation_info=None): - """Creates a new router, and assign the gateway interface if any.""" - - dva = api.create_router(tenant_id=tenant_id, - router_id=neutron_router["id"], - name=neutron_router["name"], - flavor=flavor, - up=neutron_router["admin_state_up"]) - try: - if utif_info: - api.grow_interface(utif_info, neutron_router["admin_state_up"], - tenant_id, neutron_router["id"]) - if ip_allocation_info: - dva = api.allocate_address(neutron_router["id"], - neutron_router["admin_state_up"], - ip_allocation_info) - except h_exc.PreliminaryOperationsFailed as ex: - raise h_exc.BrokenInterface(err_msg=ex.message) - - state = api.extract_dva_state(dva) - return state - - -@handler(p_con.Events.UPDATE_ROUTER, handlers) -def _update_dva_and_assign_address(api, tenant_id, neutron_router, - utif_info=None, ip_allocation_info=None, - routes_info=[]): - name = neutron_router["name"] - up = neutron_router["admin_state_up"] - r_id = neutron_router["id"] - if ip_allocation_info or routes_info: - up = True - dva = api.update_dva(tenant_id=tenant_id, router_id=r_id, name=name, - up=up, utif_info=utif_info) - if ip_allocation_info: - api.allocate_address(r_id, up, ip_allocation_info) - - if routes_info: - api.delete_extra_routes(r_id, up) - api.set_extra_routes(r_id, neutron_router["admin_state_up"], - routes_info) - - return api.extract_dva_state(dva) - - -@handler(p_con.Events.DELETE_ROUTER, handlers) -def _delete_dva(api, tenant_id, neutron_router): - try: - api.delete_dva(tenant_id, neutron_router["id"]) - except h_exc.DvaNotFound: - LOG.warning(_("The router %s had no physical representation," - "likely already deleted"), neutron_router["id"]) - return p_con.Status.DELETED - - -@handler(p_con.Events.GROW_ROUTER_IF, handlers) -def _grow_dva_iface_and_assign_address(api, tenant_id, neutron_router, - utif_info=None, - ip_allocation_info=None): - try: - dva = api.grow_interface(utif_info, neutron_router["admin_state_up"], - tenant_id, neutron_router["id"]) - if ip_allocation_info: - dva = api.allocate_address(neutron_router["id"], - neutron_router["admin_state_up"], - ip_allocation_info) - except h_exc.PreliminaryOperationsFailed as ex: - raise h_exc.BrokenInterface(err_msg=ex.message) - - state = api.extract_dva_state(dva) - return state - - -@handler(p_con.Events.SHRINK_ROUTER_IF, handlers) -def _shrink_dva_iface(api, tenant_id, neutron_router, port_id): - try: - dva = api.shrink_interface(tenant_id, neutron_router["id"], - neutron_router["admin_state_up"], port_id) - except h_exc.InterfaceNotFound: - LOG.warning(_("Interface %s not found in the heleos back-end," - "likely already deleted"), port_id) - return (p_con.Status.ACTIVE if neutron_router["admin_state_up"] else - p_con.Status.READY) - except h_exc.PreliminaryOperationsFailed as ex: - raise h_exc.BrokenInterface(err_msg=ex.message) - state = api.extract_dva_state(dva) - return state - - -@handler(p_con.Events.SET_NAT_RULE, handlers) -def _create_nat_rule(api, tenant_id, neutron_router, nat_info=None): - - dva = api.create_nat_entry(neutron_router["id"], - neutron_router["admin_state_up"], nat_info) - - state = api.extract_dva_state(dva) - return state - - -@handler(p_con.Events.RESET_NAT_RULE, handlers) -def _delete_nat_rule(api, tenant_id, neutron_router, floating_ip_id): - - dva = api.remove_nat_entry(neutron_router["id"], - neutron_router["admin_state_up"], - floating_ip_id) - - state = api.extract_dva_state(dva) - return state diff --git a/neutron/plugins/embrane/base_plugin.py b/neutron/plugins/embrane/base_plugin.py deleted file mode 100644 index 33d213888..000000000 --- a/neutron/plugins/embrane/base_plugin.py +++ /dev/null @@ -1,375 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from heleosapi import backend_operations as h_op -from heleosapi import constants as h_con -from heleosapi import exceptions as h_exc -from oslo.config import cfg -from sqlalchemy.orm import exc - -from neutron.common import constants as l3_constants -from neutron.common import exceptions as neutron_exc -from neutron.db import extraroute_db -from neutron.db import l3_db -from neutron.db import models_v2 -from neutron.extensions import l3 -from neutron.openstack.common import log as logging -from neutron.plugins.embrane.agent import dispatcher -from neutron.plugins.embrane.common import config # noqa -from neutron.plugins.embrane.common import constants as p_con -from neutron.plugins.embrane.common import contexts as embrane_ctx -from neutron.plugins.embrane.common import operation -from neutron.plugins.embrane.common import utils - -LOG = logging.getLogger(__name__) -conf = cfg.CONF.heleos - - -class EmbranePlugin(object): - """Embrane Neutron plugin. - - uses the heleos(c) platform and a support L2 plugin to leverage networking - in cloud environments. - - """ - _l3super = extraroute_db.ExtraRoute_db_mixin - - def __init__(self): - pass - - def _run_embrane_config(self): - # read configurations - config_esm_mgmt = conf.esm_mgmt - config_admin_username = conf.admin_username - config_admin_password = conf.admin_password - config_router_image_id = conf.router_image - config_security_zones = {h_con.SzType.IB: conf.inband_id, - h_con.SzType.OOB: conf.oob_id, - h_con.SzType.MGMT: conf.mgmt_id, - h_con.SzType.DUMMY: conf.dummy_utif_id} - config_resource_pool = conf.resource_pool_id - self._embrane_async = conf.async_requests - self._esm_api = h_op.BackendOperations( - esm_mgmt=config_esm_mgmt, - admin_username=config_admin_username, - admin_password=config_admin_password, - router_image_id=config_router_image_id, - security_zones=config_security_zones, - resource_pool=config_resource_pool) - self._dispatcher = dispatcher.Dispatcher(self, self._embrane_async) - - def _make_router_dict(self, *args, **kwargs): - return self._l3super._make_router_dict(self, *args, **kwargs) - - def _delete_router(self, context, router_id): - self._l3super.delete_router(self, context, router_id) - - def _update_db_router_state(self, context, neutron_router, dva_state): - if not dva_state: - new_state = p_con.Status.ERROR - elif dva_state == h_con.DvaState.POWER_ON: - new_state = p_con.Status.ACTIVE - else: - new_state = p_con.Status.READY - self._set_db_router_state(context, neutron_router, new_state) - return new_state - - def _set_db_router_state(self, context, neutron_router, new_state): - return utils.set_db_item_state(context, neutron_router, new_state) - - def _update_db_interfaces_state(self, context, neutron_router): - router_ports = self.get_ports(context, - {"device_id": [neutron_router["id"]]}) - self._esm_api.update_ports_status(neutron_router["id"], router_ports) - for port in router_ports: - db_port = self._get_port(context, port["id"]) - db_port["status"] = port["status"] - context.session.merge(db_port) - - def _update_neutron_state(self, context, neutron_router, state): - try: - self._update_db_interfaces_state(context, neutron_router) - except Exception: - LOG.exception(_("Unhandled exception occurred")) - return self._set_db_router_state(context, neutron_router, state) - - def _retrieve_prefix_from_port(self, context, neutron_port): - subnet_id = neutron_port["fixed_ips"][0]["subnet_id"] - subnet = utils.retrieve_subnet(context, subnet_id) - prefix = subnet["cidr"].split("/")[1] - return prefix - - # L3 extension - def create_router(self, context, router): - r = router["router"] - self._get_tenant_id_for_create(context, r) - db_router = self._l3super.create_router(self, context, router) - neutron_router = self._get_router(context, db_router['id']) - gw_port = neutron_router.gw_port - # For now, only small flavor is used - utif_info = (self._plugin_support.retrieve_utif_info(context, - gw_port) - if gw_port else None) - ip_allocation_info = (utils.retrieve_ip_allocation_info(context, - gw_port) - if gw_port else None) - neutron_router = self._l3super._get_router(self, context, - neutron_router["id"]) - neutron_router["status"] = p_con.Status.CREATING - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.CREATE_ROUTER, neutron_router, context, None), - args=(h_con.Flavor.SMALL, utif_info, ip_allocation_info)) - return self._make_router_dict(neutron_router) - - def update_router(self, context, id, router): - db_router = self._l3super.update_router(self, context, id, router) - neutron_router = self._get_router(context, db_router['id']) - gw_port = neutron_router.gw_port - utif_info = (self._plugin_support.retrieve_utif_info(context, - gw_port) - if gw_port else None) - ip_allocation_info = (utils.retrieve_ip_allocation_info(context, - gw_port) - if gw_port else None) - - routes_info = router["router"].get("routes") - - neutron_router = self._l3super._get_router(self, context, id) - state_change = operation.Operation( - self._set_db_router_state, - args=(context, neutron_router, p_con.Status.UPDATING)) - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.UPDATE_ROUTER, neutron_router, context, - state_change), - args=(utif_info, ip_allocation_info, routes_info)) - return self._make_router_dict(neutron_router) - - def get_router(self, context, id, fields=None): - """Ensures that id does exist in the ESM.""" - neutron_router = self._get_router(context, id) - - try: - if neutron_router["status"] != p_con.Status.CREATING: - self._esm_api.get_dva(id) - except h_exc.DvaNotFound: - - LOG.error(_("The following routers have not physical match: %s"), - id) - self._set_db_router_state(context, neutron_router, - p_con.Status.ERROR) - - LOG.debug(_("Requested router: %s"), neutron_router) - return self._make_router_dict(neutron_router, fields) - - def get_routers(self, context, filters=None, fields=None, sorts=None, - limit=None, marker=None, page_reverse=False): - """Retrieves the router list defined by the incoming filters.""" - router_query = self._apply_filters_to_query( - self._model_query(context, l3_db.Router), - l3_db.Router, filters) - id_list = [x["id"] for x in router_query - if x["status"] != p_con.Status.CREATING] - try: - self._esm_api.get_dvas(id_list) - except h_exc.DvaNotFound: - LOG.error(_("The following routers have not physical match: %s"), - repr(id_list)) - error_routers = [] - for id in id_list: - try: - error_routers.append(self._get_router(context, id)) - except l3.RouterNotFound: - pass - for error_router in error_routers: - self._set_db_router_state(context, error_router, - p_con.Status.ERROR) - return [self._make_router_dict(router, fields) - for router in router_query] - - def delete_router(self, context, id): - """Deletes the DVA with the specific router id.""" - # Copy of the parent validation code, shouldn't the base modules - # provide functions for validating operations? - device_owner_router_intf = l3_constants.DEVICE_OWNER_ROUTER_INTF - fips = self.get_floatingips_count(context.elevated(), - filters={"router_id": [id]}) - if fips: - raise l3.RouterInUse(router_id=id) - - device_filter = {"device_id": [id], - "device_owner": [device_owner_router_intf]} - ports = self.get_ports_count(context.elevated(), - filters=device_filter) - if ports: - raise l3.RouterInUse(router_id=id) - neutron_router = self._get_router(context, id) - state_change = operation.Operation(self._set_db_router_state, - args=(context, neutron_router, - p_con.Status.DELETING)) - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.DELETE_ROUTER, neutron_router, context, - state_change), args=()) - LOG.debug(_("Deleting router=%s"), neutron_router) - return neutron_router - - def add_router_interface(self, context, router_id, interface_info): - """Grows DVA interface in the specified subnet.""" - neutron_router = self._get_router(context, router_id) - rport_qry = context.session.query(models_v2.Port) - ports = rport_qry.filter_by( - device_id=router_id).all() - if len(ports) >= p_con.UTIF_LIMIT: - raise neutron_exc.BadRequest( - resource=router_id, - msg=("this router doesn't support more than " - + str(p_con.UTIF_LIMIT) + " interfaces")) - neutron_router_iface = self._l3super.add_router_interface( - self, context, router_id, interface_info) - port = self._get_port(context, neutron_router_iface["port_id"]) - utif_info = self._plugin_support.retrieve_utif_info(context, port) - ip_allocation_info = utils.retrieve_ip_allocation_info(context, - port) - state_change = operation.Operation(self._set_db_router_state, - args=(context, neutron_router, - p_con.Status.UPDATING)) - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.GROW_ROUTER_IF, neutron_router, context, - state_change), - args=(utif_info, ip_allocation_info)) - return neutron_router_iface - - def remove_router_interface(self, context, router_id, interface_info): - port_id = None - if "port_id" in interface_info: - port_id = interface_info["port_id"] - elif "subnet_id" in interface_info: - subnet_id = interface_info["subnet_id"] - subnet = utils.retrieve_subnet(context, subnet_id) - rport_qry = context.session.query(models_v2.Port) - ports = rport_qry.filter_by( - device_id=router_id, - device_owner=l3_constants.DEVICE_OWNER_ROUTER_INTF, - network_id=subnet["network_id"]) - for p in ports: - if p["fixed_ips"][0]["subnet_id"] == subnet_id: - port_id = p["id"] - break - neutron_router = self._get_router(context, router_id) - self._l3super.remove_router_interface(self, context, router_id, - interface_info) - state_change = operation.Operation(self._set_db_router_state, - args=(context, neutron_router, - p_con.Status.UPDATING)) - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.SHRINK_ROUTER_IF, neutron_router, context, - state_change), - args=(port_id,)) - - def create_floatingip(self, context, floatingip): - result = self._l3super.create_floatingip( - self, context, floatingip) - - if result["port_id"]: - neutron_router = self._get_router(context, result["router_id"]) - db_fixed_port = self._get_port(context, result["port_id"]) - fixed_prefix = self._retrieve_prefix_from_port(context, - db_fixed_port) - db_floating_port = neutron_router["gw_port"] - floating_prefix = self._retrieve_prefix_from_port( - context, db_floating_port) - nat_info = utils.retrieve_nat_info(context, result, - fixed_prefix, - floating_prefix, - neutron_router) - state_change = operation.Operation( - self._set_db_router_state, - args=(context, neutron_router, p_con.Status.UPDATING)) - - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.SET_NAT_RULE, neutron_router, context, - state_change), - args=(nat_info,)) - return result - - def update_floatingip(self, context, id, floatingip): - db_fip = self._l3super.get_floatingip(self, context, id) - result = self._l3super.update_floatingip(self, context, id, - floatingip) - - if db_fip["port_id"] and db_fip["port_id"] != result["port_id"]: - neutron_router = self._get_router(context, db_fip["router_id"]) - fip_id = db_fip["id"] - state_change = operation.Operation( - self._set_db_router_state, - args=(context, neutron_router, p_con.Status.UPDATING)) - - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.RESET_NAT_RULE, neutron_router, context, - state_change), - args=(fip_id,)) - if result["port_id"]: - neutron_router = self._get_router(context, result["router_id"]) - db_fixed_port = self._get_port(context, result["port_id"]) - fixed_prefix = self._retrieve_prefix_from_port(context, - db_fixed_port) - db_floating_port = neutron_router["gw_port"] - floating_prefix = self._retrieve_prefix_from_port( - context, db_floating_port) - nat_info = utils.retrieve_nat_info(context, result, - fixed_prefix, - floating_prefix, - neutron_router) - state_change = operation.Operation( - self._set_db_router_state, - args=(context, neutron_router, p_con.Status.UPDATING)) - - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.SET_NAT_RULE, neutron_router, context, - state_change), - args=(nat_info,)) - return result - - def disassociate_floatingips(self, context, port_id): - try: - fip_qry = context.session.query(l3_db.FloatingIP) - floating_ip = fip_qry.filter_by(fixed_port_id=port_id).one() - router_id = floating_ip["router_id"] - except exc.NoResultFound: - return - self._l3super.disassociate_floatingips(self, context, port_id) - if router_id: - neutron_router = self._get_router(context, router_id) - fip_id = floating_ip["id"] - state_change = operation.Operation( - self._set_db_router_state, - args=(context, neutron_router, p_con.Status.UPDATING)) - - self._dispatcher.dispatch_l3( - d_context=embrane_ctx.DispatcherContext( - p_con.Events.RESET_NAT_RULE, neutron_router, context, - state_change), - args=(fip_id,)) diff --git a/neutron/plugins/embrane/common/__init__.py b/neutron/plugins/embrane/common/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/common/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/common/config.py b/neutron/plugins/embrane/common/config.py deleted file mode 100644 index 54c9153f3..000000000 --- a/neutron/plugins/embrane/common/config.py +++ /dev/null @@ -1,49 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from oslo.config import cfg - - -heleos_opts = [ - cfg.StrOpt('esm_mgmt', - help=_('ESM management root address')), - cfg.StrOpt('admin_username', default='admin', - help=_('ESM admin username.')), - cfg.StrOpt('admin_password', - secret=True, - help=_('ESM admin password.')), - cfg.StrOpt('router_image', - help=_('Router image id (Embrane FW/VPN)')), - cfg.StrOpt('inband_id', - help=_('In band Security Zone id')), - cfg.StrOpt('oob_id', - help=_('Out of band Security Zone id')), - cfg.StrOpt('mgmt_id', - help=_('Management Security Zone id')), - cfg.StrOpt('dummy_utif_id', - help=_('Dummy user traffic Security Zone id')), - cfg.StrOpt('resource_pool_id', default='default', - help=_('Shared resource pool id')), - cfg.BoolOpt('async_requests', default=True, - help=_('Define if the requests have ' - 'run asynchronously or not')), -] - - -cfg.CONF.register_opts(heleos_opts, "heleos") diff --git a/neutron/plugins/embrane/common/constants.py b/neutron/plugins/embrane/common/constants.py deleted file mode 100644 index 65f3818a2..000000000 --- a/neutron/plugins/embrane/common/constants.py +++ /dev/null @@ -1,72 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from heleosapi import exceptions as h_exc - -from neutron.plugins.common import constants - - -# Router specific constants -UTIF_LIMIT = 7 -QUEUE_TIMEOUT = 300 - - -class Status: - # Transient - CREATING = constants.PENDING_CREATE - UPDATING = constants.PENDING_UPDATE - DELETING = constants.PENDING_DELETE - # Final - ACTIVE = constants.ACTIVE - ERROR = constants.ERROR - READY = constants.INACTIVE - DELETED = "DELETED" # not visible - - -class Events: - CREATE_ROUTER = "create_router" - UPDATE_ROUTER = "update_router" - DELETE_ROUTER = "delete_router" - GROW_ROUTER_IF = "grow_router_if" - SHRINK_ROUTER_IF = "shrink_router_if" - SET_NAT_RULE = "set_nat_rule" - RESET_NAT_RULE = "reset_nat_rule" - -_DVA_PENDING_ERROR_MSG = _("Dva is pending for the following reason: %s") -_DVA_NOT_FOUNT_ERROR_MSG = _("Dva can't be found to execute the operation, " - "probably was cancelled through the heleos UI") -_DVA_BROKEN_ERROR_MSG = _("Dva seems to be broken for reason %s") -_DVA_BROKEN_INTERFACE_ERROR_MSG = _("Dva interface seems to be broken " - "for reason %s") -_DVA_CREATION_FAILED_ERROR_MSG = _("Dva creation failed reason %s") -_DVA_CREATION_PENDING_ERROR_MSG = _("Dva creation is in pending state " - "for reason %s") -_CFG_FAILED_ERROR_MSG = _("Dva configuration failed for reason %s") -_DVA_DEL_FAILED_ERROR_MSG = _("Failed to delete the backend " - "router for reason %s. Please remove " - "it manually through the heleos UI") - -error_map = {h_exc.PendingDva: _DVA_PENDING_ERROR_MSG, - h_exc.DvaNotFound: _DVA_NOT_FOUNT_ERROR_MSG, - h_exc.BrokenDva: _DVA_BROKEN_ERROR_MSG, - h_exc.BrokenInterface: _DVA_BROKEN_INTERFACE_ERROR_MSG, - h_exc.DvaCreationFailed: _DVA_CREATION_FAILED_ERROR_MSG, - h_exc.DvaCreationPending: _DVA_CREATION_PENDING_ERROR_MSG, - h_exc.ConfigurationFailed: _CFG_FAILED_ERROR_MSG, - h_exc.DvaDeleteFailed: _DVA_DEL_FAILED_ERROR_MSG} diff --git a/neutron/plugins/embrane/common/contexts.py b/neutron/plugins/embrane/common/contexts.py deleted file mode 100644 index f35a02427..000000000 --- a/neutron/plugins/embrane/common/contexts.py +++ /dev/null @@ -1,40 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - - -class DispatcherContext(object): - - def __init__(self, event, item, neutron_context, chain=None): - self.event = event - self.item = item - self.n_context = neutron_context - self.chain = chain - - -class OperationContext(DispatcherContext): - """Operational context. - - contains all the parameters needed to execute a status aware operation - - """ - def __init__(self, event, context, item, chain, function, args, kwargs): - super(OperationContext, self).__init__(event, item, context, chain) - self.function = function - self.args = args - self.kwargs = kwargs diff --git a/neutron/plugins/embrane/common/exceptions.py b/neutron/plugins/embrane/common/exceptions.py deleted file mode 100644 index f7cfa7b24..000000000 --- a/neutron/plugins/embrane/common/exceptions.py +++ /dev/null @@ -1,28 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from neutron.common import exceptions as neutron_exec - - -class EmbranePluginException(neutron_exec.NeutronException): - message = _("An unexpected error occurred:%(err_msg)s") - - -class UnsupportedException(EmbranePluginException): - message = _("%(err_msg)s") diff --git a/neutron/plugins/embrane/common/operation.py b/neutron/plugins/embrane/common/operation.py deleted file mode 100644 index 39fa413e2..000000000 --- a/neutron/plugins/embrane/common/operation.py +++ /dev/null @@ -1,51 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - - -class Operation(object): - """Defines a series of operations which shall be executed in order. - - the operations expected are procedures, return values are discarded - - """ - - def __init__(self, procedure, args=(), kwargs={}, nextop=None): - self._procedure = procedure - self.args = args[:] - self.kwargs = dict(kwargs) - self.nextop = nextop - - def execute(self): - args = self.args - self._procedure(*args, **self.kwargs) - return self.nextop - - def execute_all(self): - nextop = self.execute() - while nextop: - nextop = self.execute_all() - - def has_next(self): - return self.nextop is not None - - def add_bottom_operation(self, operation): - op = self - while op.has_next(): - op = op.nextop - op.nextop = operation diff --git a/neutron/plugins/embrane/common/utils.py b/neutron/plugins/embrane/common/utils.py deleted file mode 100644 index 5fa20eb59..000000000 --- a/neutron/plugins/embrane/common/utils.py +++ /dev/null @@ -1,73 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from heleosapi import info as h_info - -from neutron.common import constants -from neutron.db import models_v2 -from neutron.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - - -def set_db_item_state(context, neutron_item, new_state): - with context.session.begin(subtransactions=True): - if neutron_item["status"] != new_state: - neutron_item["status"] = new_state - context.session.merge(neutron_item) - - -def retrieve_subnet(context, subnet_id): - return (context.session.query( - models_v2.Subnet).filter(models_v2.Subnet.id == subnet_id).one()) - - -def retrieve_ip_allocation_info(context, neutron_port): - """Retrieves ip allocation info for a specific port if any.""" - - try: - subnet_id = neutron_port["fixed_ips"][0]["subnet_id"] - except (KeyError, IndexError): - LOG.info(_("No ip allocation set")) - return - subnet = retrieve_subnet(context, subnet_id) - allocated_ip = neutron_port["fixed_ips"][0]["ip_address"] - is_gw_port = (neutron_port["device_owner"] == - constants.DEVICE_OWNER_ROUTER_GW) - gateway_ip = subnet["gateway_ip"] - - ip_allocation_info = h_info.IpAllocationInfo( - is_gw=is_gw_port, - ip_version=subnet["ip_version"], - prefix=subnet["cidr"].split("/")[1], - ip_address=allocated_ip, - port_id=neutron_port["id"], - gateway_ip=gateway_ip) - - return ip_allocation_info - - -def retrieve_nat_info(context, fip, fixed_prefix, floating_prefix, router): - nat_info = h_info.NatInfo(source_address=fip["floating_ip_address"], - source_prefix=floating_prefix, - destination_address=fip["fixed_ip_address"], - destination_prefix=fixed_prefix, - floating_ip_id=fip["id"], - fixed_port_id=fip["port_id"]) - return nat_info diff --git a/neutron/plugins/embrane/l2base/__init__.py b/neutron/plugins/embrane/l2base/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/l2base/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/l2base/fake/__init__.py b/neutron/plugins/embrane/l2base/fake/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/l2base/fake/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/l2base/fake/fake_l2_plugin.py b/neutron/plugins/embrane/l2base/fake/fake_l2_plugin.py deleted file mode 100644 index 5cf68df28..000000000 --- a/neutron/plugins/embrane/l2base/fake/fake_l2_plugin.py +++ /dev/null @@ -1,24 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from neutron.db import db_base_plugin_v2 - - -class FakeL2Plugin(db_base_plugin_v2.NeutronDbPluginV2): - supported_extension_aliases = [] diff --git a/neutron/plugins/embrane/l2base/fake/fakeplugin_support.py b/neutron/plugins/embrane/l2base/fake/fakeplugin_support.py deleted file mode 100644 index 7818d28de..000000000 --- a/neutron/plugins/embrane/l2base/fake/fakeplugin_support.py +++ /dev/null @@ -1,45 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from heleosapi import info as h_info - -from neutron.common import constants -from neutron import manager -from neutron.plugins.embrane.l2base import support_base as base - - -class FakePluginSupport(base.SupportBase): - - def __init__(self): - super(FakePluginSupport, self).__init__() - - def retrieve_utif_info(self, context, neutron_port): - plugin = manager.NeutronManager.get_plugin() - network_id = neutron_port["network_id"] - network = plugin._get_network(context, network_id) - is_gw = (neutron_port["device_owner"] == - constants.DEVICE_OWNER_ROUTER_GW) - result = h_info.UtifInfo(vlan=0, - network_name=network["name"], - network_id=network["id"], - is_gw=is_gw, - owner_tenant=network["tenant_id"], - port_id=neutron_port["id"], - mac_address=neutron_port["mac_address"]) - return result diff --git a/neutron/plugins/embrane/l2base/openvswitch/__init__.py b/neutron/plugins/embrane/l2base/openvswitch/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/l2base/openvswitch/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/l2base/openvswitch/openvswitch_support.py b/neutron/plugins/embrane/l2base/openvswitch/openvswitch_support.py deleted file mode 100644 index f37a6b81a..000000000 --- a/neutron/plugins/embrane/l2base/openvswitch/openvswitch_support.py +++ /dev/null @@ -1,58 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from heleosapi import info as h_info - -from neutron.common import constants -from neutron import manager -from neutron.plugins.embrane.l2base import support_base as base -from neutron.plugins.embrane.l2base import support_exceptions as exc -from neutron.plugins.openvswitch import ovs_db_v2 - - -class OpenvswitchSupport(base.SupportBase): - """OpenVSwitch plugin support. - - Obtains the informations needed to build the user security zones - - """ - - def __init__(self): - super(OpenvswitchSupport, self).__init__() - - def retrieve_utif_info(self, context, neutron_port): - plugin = manager.NeutronManager.get_plugin() - session = context.session - network_id = neutron_port["network_id"] - network_binding = ovs_db_v2.get_network_binding(session, network_id) - if not network_binding["segmentation_id"]: - raise exc.UtifInfoError( - err_msg=_("No segmentation_id found for the network, " - "please be sure that tenant_network_type is vlan")) - network = plugin._get_network(context, network_id) - is_gw = (neutron_port["device_owner"] == - constants.DEVICE_OWNER_ROUTER_GW) - result = h_info.UtifInfo(vlan=network_binding["segmentation_id"], - network_name=network["name"], - network_id=network["id"], - is_gw=is_gw, - owner_tenant=network["tenant_id"], - port_id=neutron_port["id"], - mac_address=neutron_port["mac_address"]) - return result diff --git a/neutron/plugins/embrane/l2base/support_base.py b/neutron/plugins/embrane/l2base/support_base.py deleted file mode 100644 index a2b7e5342..000000000 --- a/neutron/plugins/embrane/l2base/support_base.py +++ /dev/null @@ -1,50 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class SupportBase(object): - """abstract support class. - - Defines the methods a plugin support should implement to be used as - the L2 base for Embrane plugin. - - """ - - @abc.abstractmethod - def __init__(self): - pass - - @abc.abstractmethod - def retrieve_utif_info(self, context, neutron_port=None, network=None): - """Retrieve specific network info. - - each plugin support, querying its own DB, can collect all the - information needed by the ESM in order to create the - user traffic security zone. - - :param interface_info: the foo parameter - :param context: neutron request context - :returns: heleosapi.info.UtifInfo -- specific network info - :raises: UtifInfoError - """ diff --git a/neutron/plugins/embrane/l2base/support_exceptions.py b/neutron/plugins/embrane/l2base/support_exceptions.py deleted file mode 100644 index 1c5c01322..000000000 --- a/neutron/plugins/embrane/l2base/support_exceptions.py +++ /dev/null @@ -1,25 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from neutron.plugins.embrane.common import exceptions as embrane_exc - - -class UtifInfoError(embrane_exc.EmbranePluginException): - message = _("Cannot retrieve utif info for the following reason: " - "%(err_msg)s") diff --git a/neutron/plugins/embrane/plugins/__init__.py b/neutron/plugins/embrane/plugins/__init__.py deleted file mode 100644 index 1fac4725b..000000000 --- a/neutron/plugins/embrane/plugins/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. diff --git a/neutron/plugins/embrane/plugins/embrane_fake_plugin.py b/neutron/plugins/embrane/plugins/embrane_fake_plugin.py deleted file mode 100644 index 69d972c54..000000000 --- a/neutron/plugins/embrane/plugins/embrane_fake_plugin.py +++ /dev/null @@ -1,34 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from neutron.db import extraroute_db -from neutron.plugins.embrane import base_plugin as base -from neutron.plugins.embrane.l2base.fake import fake_l2_plugin as l2 -from neutron.plugins.embrane.l2base.fake import fakeplugin_support as sup - - -class EmbraneFakePlugin(base.EmbranePlugin, extraroute_db.ExtraRoute_db_mixin, - l2.FakeL2Plugin): - _plugin_support = sup.FakePluginSupport() - - def __init__(self): - '''First run plugin specific initialization, then Embrane's.''' - self.supported_extension_aliases += ["extraroute", "router"] - l2.FakeL2Plugin.__init__(self) - self._run_embrane_config() diff --git a/neutron/plugins/embrane/plugins/embrane_ovs_plugin.py b/neutron/plugins/embrane/plugins/embrane_ovs_plugin.py deleted file mode 100644 index d4d5ac180..000000000 --- a/neutron/plugins/embrane/plugins/embrane_ovs_plugin.py +++ /dev/null @@ -1,38 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. - -from neutron.plugins.embrane import base_plugin as base -from neutron.plugins.embrane.l2base.openvswitch import openvswitch_support -from neutron.plugins.openvswitch import ovs_neutron_plugin as l2 - - -class EmbraneOvsPlugin(base.EmbranePlugin, l2.OVSNeutronPluginV2): - '''EmbraneOvsPlugin. - - This plugin uses OpenVSwitch specific L2 plugin for providing L2 networks - and the base EmbranePlugin for L3. - - ''' - _plugin_support = openvswitch_support.OpenvswitchSupport() - - def __init__(self): - '''First run plugin specific initialization, then Embrane's.''' - self._supported_extension_aliases.remove("l3_agent_scheduler") - l2.OVSNeutronPluginV2.__init__(self) - self._run_embrane_config() diff --git a/neutron/plugins/hyperv/__init__.py b/neutron/plugins/hyperv/__init__.py deleted file mode 100644 index 7ef4e09fa..000000000 --- a/neutron/plugins/hyperv/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. diff --git a/neutron/plugins/hyperv/agent/__init__.py b/neutron/plugins/hyperv/agent/__init__.py deleted file mode 100644 index 7ef4e09fa..000000000 --- a/neutron/plugins/hyperv/agent/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. diff --git a/neutron/plugins/hyperv/agent/hyperv_neutron_agent.py b/neutron/plugins/hyperv/agent/hyperv_neutron_agent.py deleted file mode 100644 index 07a5ed776..000000000 --- a/neutron/plugins/hyperv/agent/hyperv_neutron_agent.py +++ /dev/null @@ -1,475 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -#Copyright 2013 Cloudbase Solutions SRL -#Copyright 2013 Pedro Navarro Perez -#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. -# @author: Pedro Navarro Perez -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -import platform -import re -import sys -import time - -import eventlet -eventlet.monkey_patch() - -from oslo.config import cfg - -from neutron.agent.common import config -from neutron.agent import rpc as agent_rpc -from neutron.agent import securitygroups_rpc as sg_rpc -from neutron.common import config as common_config -from neutron.common import constants as n_const -from neutron.common import rpc_compat -from neutron.common import topics -from neutron import context -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall -from neutron.plugins.common import constants as p_const -from neutron.plugins.hyperv.agent import utils -from neutron.plugins.hyperv.agent import utilsfactory -from neutron.plugins.hyperv.common import constants - -LOG = logging.getLogger(__name__) - -agent_opts = [ - cfg.ListOpt( - 'physical_network_vswitch_mappings', - default=[], - help=_('List of : ' - 'where the physical networks can be expressed with ' - 'wildcards, e.g.: ."*:external"')), - cfg.StrOpt( - 'local_network_vswitch', - default='private', - help=_('Private vswitch name used for local networks')), - cfg.IntOpt('polling_interval', default=2, - help=_("The number of seconds the agent will wait between " - "polling for local device changes.")), - cfg.BoolOpt('enable_metrics_collection', - default=False, - help=_('Enables metrics collections for switch ports by using ' - 'Hyper-V\'s metric APIs. Collected data can by ' - 'retrieved by other apps and services, e.g.: ' - 'Ceilometer. Requires Hyper-V / Windows Server 2012 ' - 'and above')), - cfg.IntOpt('metrics_max_retries', - default=100, - help=_('Specifies the maximum number of retries to enable ' - 'Hyper-V\'s port metrics collection. The agent will try ' - 'to enable the feature once every polling_interval ' - 'period for at most metrics_max_retries or until it ' - 'succeedes.')) -] - - -CONF = cfg.CONF -CONF.register_opts(agent_opts, "AGENT") -config.register_agent_state_opts_helper(cfg.CONF) - - -class HyperVSecurityAgent(rpc_compat.RpcCallback, - sg_rpc.SecurityGroupAgentRpcMixin): - # Set RPC API version to 1.1 by default. - RPC_API_VERSION = '1.1' - - def __init__(self, context, plugin_rpc): - super(HyperVSecurityAgent, self).__init__() - self.context = context - self.plugin_rpc = plugin_rpc - - if sg_rpc.is_firewall_enabled(): - self.init_firewall() - self._setup_rpc() - - def _setup_rpc(self): - self.topic = topics.AGENT - self.endpoints = [HyperVSecurityCallbackMixin(self)] - consumers = [[topics.SECURITY_GROUP, topics.UPDATE]] - - self.connection = agent_rpc.create_consumers(self.endpoints, - self.topic, - consumers) - - -class HyperVSecurityCallbackMixin(rpc_compat.RpcCallback, - sg_rpc.SecurityGroupAgentRpcCallbackMixin): - # Set RPC API version to 1.1 by default. - RPC_API_VERSION = '1.1' - - def __init__(self, sg_agent): - super(HyperVSecurityCallbackMixin, self).__init__() - self.sg_agent = sg_agent - - -class HyperVPluginApi(agent_rpc.PluginApi, - sg_rpc.SecurityGroupServerRpcApiMixin): - pass - - -class HyperVNeutronAgent(rpc_compat.RpcCallback): - # Set RPC API version to 1.0 by default. - RPC_API_VERSION = '1.0' - - def __init__(self): - super(HyperVNeutronAgent, self).__init__() - self._utils = utilsfactory.get_hypervutils() - self._polling_interval = CONF.AGENT.polling_interval - self._load_physical_network_mappings() - self._network_vswitch_map = {} - self._port_metric_retries = {} - self._set_agent_state() - self._setup_rpc() - - def _set_agent_state(self): - self.agent_state = { - 'binary': 'neutron-hyperv-agent', - 'host': cfg.CONF.host, - 'topic': n_const.L2_AGENT_TOPIC, - 'configurations': {'vswitch_mappings': - self._physical_network_mappings}, - 'agent_type': n_const.AGENT_TYPE_HYPERV, - 'start_flag': True} - - def _report_state(self): - try: - self.state_rpc.report_state(self.context, - self.agent_state) - self.agent_state.pop('start_flag', None) - except Exception as ex: - LOG.exception(_("Failed reporting state! %s"), ex) - - def _setup_rpc(self): - self.agent_id = 'hyperv_%s' % platform.node() - self.topic = topics.AGENT - self.plugin_rpc = HyperVPluginApi(topics.PLUGIN) - - self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) - - # RPC network init - self.context = context.get_admin_context_without_session() - # Handle updates from service - self.endpoints = [self] - # Define the listening consumers for the agent - consumers = [[topics.PORT, topics.UPDATE], - [topics.NETWORK, topics.DELETE], - [topics.PORT, topics.DELETE], - [constants.TUNNEL, topics.UPDATE]] - self.connection = agent_rpc.create_consumers(self.endpoints, - self.topic, - consumers) - - self.sec_groups_agent = HyperVSecurityAgent( - self.context, self.plugin_rpc) - report_interval = CONF.AGENT.report_interval - if report_interval: - heartbeat = loopingcall.FixedIntervalLoopingCall( - self._report_state) - heartbeat.start(interval=report_interval) - - def _load_physical_network_mappings(self): - self._physical_network_mappings = {} - for mapping in CONF.AGENT.physical_network_vswitch_mappings: - parts = mapping.split(':') - if len(parts) != 2: - LOG.debug(_('Invalid physical network mapping: %s'), mapping) - else: - pattern = re.escape(parts[0].strip()).replace('\\*', '.*') - vswitch = parts[1].strip() - self._physical_network_mappings[pattern] = vswitch - - def _get_vswitch_for_physical_network(self, phys_network_name): - for pattern in self._physical_network_mappings: - if phys_network_name is None: - phys_network_name = '' - if re.match(pattern, phys_network_name): - return self._physical_network_mappings[pattern] - # Not found in the mappings, the vswitch has the same name - return phys_network_name - - def _get_network_vswitch_map_by_port_id(self, port_id): - for network_id, map in self._network_vswitch_map.iteritems(): - if port_id in map['ports']: - return (network_id, map) - - def network_delete(self, context, network_id=None): - LOG.debug(_("network_delete received. " - "Deleting network %s"), network_id) - # The network may not be defined on this agent - if network_id in self._network_vswitch_map: - self._reclaim_local_network(network_id) - else: - LOG.debug(_("Network %s not defined on agent."), network_id) - - def port_delete(self, context, port_id=None): - LOG.debug(_("port_delete received")) - self._port_unbound(port_id) - - def port_update(self, context, port=None, network_type=None, - segmentation_id=None, physical_network=None): - LOG.debug(_("port_update received")) - if CONF.SECURITYGROUP.enable_security_group: - if 'security_groups' in port: - self.sec_groups_agent.refresh_firewall() - - self._treat_vif_port( - port['id'], port['network_id'], - network_type, physical_network, - segmentation_id, port['admin_state_up']) - - def _get_vswitch_name(self, network_type, physical_network): - if network_type != p_const.TYPE_LOCAL: - vswitch_name = self._get_vswitch_for_physical_network( - physical_network) - else: - vswitch_name = CONF.AGENT.local_network_vswitch - return vswitch_name - - def _provision_network(self, port_id, - net_uuid, network_type, - physical_network, - segmentation_id): - LOG.info(_("Provisioning network %s"), net_uuid) - - vswitch_name = self._get_vswitch_name(network_type, physical_network) - - if network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT]: - #Nothing to do - pass - elif network_type == p_const.TYPE_LOCAL: - #TODO(alexpilotti): Check that the switch type is private - #or create it if not existing - pass - else: - raise utils.HyperVException( - msg=(_("Cannot provision unknown network type %(network_type)s" - " for network %(net_uuid)s") % - dict(network_type=network_type, net_uuid=net_uuid))) - - map = { - 'network_type': network_type, - 'vswitch_name': vswitch_name, - 'ports': [], - 'vlan_id': segmentation_id} - self._network_vswitch_map[net_uuid] = map - - def _reclaim_local_network(self, net_uuid): - LOG.info(_("Reclaiming local network %s"), net_uuid) - del self._network_vswitch_map[net_uuid] - - def _port_bound(self, port_id, - net_uuid, - network_type, - physical_network, - segmentation_id): - LOG.debug(_("Binding port %s"), port_id) - - if net_uuid not in self._network_vswitch_map: - self._provision_network( - port_id, net_uuid, network_type, - physical_network, segmentation_id) - - map = self._network_vswitch_map[net_uuid] - map['ports'].append(port_id) - - self._utils.connect_vnic_to_vswitch(map['vswitch_name'], port_id) - - if network_type == p_const.TYPE_VLAN: - LOG.info(_('Binding VLAN ID %(segmentation_id)s ' - 'to switch port %(port_id)s'), - dict(segmentation_id=segmentation_id, port_id=port_id)) - self._utils.set_vswitch_port_vlan_id( - segmentation_id, - port_id) - elif network_type == p_const.TYPE_FLAT: - #Nothing to do - pass - elif network_type == p_const.TYPE_LOCAL: - #Nothing to do - pass - else: - LOG.error(_('Unsupported network type %s'), network_type) - - if CONF.AGENT.enable_metrics_collection: - self._utils.enable_port_metrics_collection(port_id) - self._port_metric_retries[port_id] = CONF.AGENT.metrics_max_retries - - def _port_unbound(self, port_id): - (net_uuid, map) = self._get_network_vswitch_map_by_port_id(port_id) - if net_uuid not in self._network_vswitch_map: - LOG.info(_('Network %s is not avalailable on this agent'), - net_uuid) - return - - LOG.debug(_("Unbinding port %s"), port_id) - self._utils.disconnect_switch_port(map['vswitch_name'], port_id, True) - - if not map['ports']: - self._reclaim_local_network(net_uuid) - - def _port_enable_control_metrics(self): - if not CONF.AGENT.enable_metrics_collection: - return - - for port_id in self._port_metric_retries.keys(): - if self._utils.can_enable_control_metrics(port_id): - self._utils.enable_control_metrics(port_id) - LOG.info(_('Port metrics enabled for port: %s'), port_id) - del self._port_metric_retries[port_id] - elif self._port_metric_retries[port_id] < 1: - self._utils.enable_control_metrics(port_id) - LOG.error(_('Port metrics raw enabling for port: %s'), port_id) - del self._port_metric_retries[port_id] - else: - self._port_metric_retries[port_id] -= 1 - - def _update_ports(self, registered_ports): - ports = self._utils.get_vnic_ids() - if ports == registered_ports: - return - added = ports - registered_ports - removed = registered_ports - ports - return {'current': ports, - 'added': added, - 'removed': removed} - - def _treat_vif_port(self, port_id, network_id, network_type, - physical_network, segmentation_id, - admin_state_up): - if self._utils.vnic_port_exists(port_id): - if admin_state_up: - self._port_bound(port_id, network_id, network_type, - physical_network, segmentation_id) - else: - self._port_unbound(port_id) - else: - LOG.debug(_("No port %s defined on agent."), port_id) - - def _treat_devices_added(self, devices): - resync = False - for device in devices: - LOG.info(_("Adding port %s"), device) - try: - device_details = self.plugin_rpc.get_device_details( - self.context, - device, - self.agent_id) - except Exception as e: - LOG.debug( - _("Unable to get port details for " - "device %(device)s: %(e)s"), - {'device': device, 'e': e}) - resync = True - continue - if 'port_id' in device_details: - LOG.info( - _("Port %(device)s updated. Details: %(device_details)s"), - {'device': device, 'device_details': device_details}) - self._treat_vif_port( - device_details['port_id'], - device_details['network_id'], - device_details['network_type'], - device_details['physical_network'], - device_details['segmentation_id'], - device_details['admin_state_up']) - - # check if security groups is enabled. - # if not, teardown the security group rules - if CONF.SECURITYGROUP.enable_security_group: - self.sec_groups_agent.prepare_devices_filter([device]) - else: - self._utils.remove_all_security_rules( - device_details['port_id']) - self.plugin_rpc.update_device_up(self.context, - device, - self.agent_id, - cfg.CONF.host) - return resync - - def _treat_devices_removed(self, devices): - resync = False - for device in devices: - LOG.info(_("Removing port %s"), device) - try: - self.plugin_rpc.update_device_down(self.context, - device, - self.agent_id, - cfg.CONF.host) - except Exception as e: - LOG.debug( - _("Removing port failed for device %(device)s: %(e)s"), - dict(device=device, e=e)) - resync = True - continue - self._port_unbound(device) - return resync - - def _process_network_ports(self, port_info): - resync_a = False - resync_b = False - if 'added' in port_info: - resync_a = self._treat_devices_added(port_info['added']) - if 'removed' in port_info: - resync_b = self._treat_devices_removed(port_info['removed']) - # If one of the above operations fails => resync with plugin - return (resync_a | resync_b) - - def daemon_loop(self): - sync = True - ports = set() - - while True: - try: - start = time.time() - if sync: - LOG.info(_("Agent out of sync with plugin!")) - ports.clear() - sync = False - - port_info = self._update_ports(ports) - - # notify plugin about port deltas - if port_info: - LOG.debug(_("Agent loop has new devices!")) - # If treat devices fails - must resync with plugin - sync = self._process_network_ports(port_info) - ports = port_info['current'] - - self._port_enable_control_metrics() - except Exception as e: - LOG.exception(_("Error in agent event loop: %s"), e) - sync = True - - # sleep till end of polling interval - elapsed = (time.time() - start) - if (elapsed < self._polling_interval): - time.sleep(self._polling_interval - elapsed) - else: - LOG.debug(_("Loop iteration exceeded interval " - "(%(polling_interval)s vs. %(elapsed)s)"), - {'polling_interval': self._polling_interval, - 'elapsed': elapsed}) - - -def main(): - common_config.init(sys.argv[1:]) - common_config.setup_logging(cfg.CONF) - - plugin = HyperVNeutronAgent() - - # Start everything. - LOG.info(_("Agent initialized successfully, now running... ")) - plugin.daemon_loop() diff --git a/neutron/plugins/hyperv/agent/security_groups_driver.py b/neutron/plugins/hyperv/agent/security_groups_driver.py deleted file mode 100644 index 755ab5270..000000000 --- a/neutron/plugins/hyperv/agent/security_groups_driver.py +++ /dev/null @@ -1,146 +0,0 @@ -#Copyright 2014 Cloudbase Solutions SRL -#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. -# @author: Claudiu Belu, Cloudbase Solutions Srl - -from neutron.agent import firewall -from neutron.openstack.common import log as logging -from neutron.plugins.hyperv.agent import utilsfactory -from neutron.plugins.hyperv.agent import utilsv2 - -LOG = logging.getLogger(__name__) - - -class HyperVSecurityGroupsDriver(firewall.FirewallDriver): - """Security Groups Driver. - - Security Groups implementation for Hyper-V VMs. - """ - - _ACL_PROP_MAP = { - 'direction': {'ingress': utilsv2.HyperVUtilsV2._ACL_DIR_IN, - 'egress': utilsv2.HyperVUtilsV2._ACL_DIR_OUT}, - 'ethertype': {'IPv4': utilsv2.HyperVUtilsV2._ACL_TYPE_IPV4, - 'IPv6': utilsv2.HyperVUtilsV2._ACL_TYPE_IPV6}, - 'protocol': {'icmp': utilsv2.HyperVUtilsV2._ICMP_PROTOCOL}, - 'default': "ANY", - 'address_default': {'IPv4': '0.0.0.0/0', 'IPv6': '::/0'} - } - - def __init__(self): - self._utils = utilsfactory.get_hypervutils() - self._security_ports = {} - - def prepare_port_filter(self, port): - LOG.debug('Creating port %s rules' % len(port['security_group_rules'])) - - # newly created port, add default rules. - if port['device'] not in self._security_ports: - LOG.debug('Creating default reject rules.') - self._utils.create_default_reject_all_rules(port['id']) - - self._security_ports[port['device']] = port - self._create_port_rules(port['id'], port['security_group_rules']) - - def _create_port_rules(self, port_id, rules): - for rule in rules: - param_map = self._create_param_map(rule) - try: - self._utils.create_security_rule(port_id, **param_map) - except Exception as ex: - LOG.error(_('Hyper-V Exception: %(hyperv_exeption)s while ' - 'adding rule: %(rule)s'), - dict(hyperv_exeption=ex, rule=rule)) - - def _remove_port_rules(self, port_id, rules): - for rule in rules: - param_map = self._create_param_map(rule) - try: - self._utils.remove_security_rule(port_id, **param_map) - except Exception as ex: - LOG.error(_('Hyper-V Exception: %(hyperv_exeption)s while ' - 'removing rule: %(rule)s'), - dict(hyperv_exeption=ex, rule=rule)) - - def _create_param_map(self, rule): - if 'port_range_min' in rule and 'port_range_max' in rule: - local_port = '%s-%s' % (rule['port_range_min'], - rule['port_range_max']) - else: - local_port = self._ACL_PROP_MAP['default'] - - return { - 'direction': self._ACL_PROP_MAP['direction'][rule['direction']], - 'acl_type': self._ACL_PROP_MAP['ethertype'][rule['ethertype']], - 'local_port': local_port, - 'protocol': self._get_rule_protocol(rule), - 'remote_address': self._get_rule_remote_address(rule) - } - - def apply_port_filter(self, port): - LOG.info(_('Aplying port filter.')) - - def update_port_filter(self, port): - LOG.info(_('Updating port rules.')) - - if port['device'] not in self._security_ports: - self.prepare_port_filter(port) - return - - old_port = self._security_ports[port['device']] - rules = old_port['security_group_rules'] - param_port_rules = port['security_group_rules'] - - new_rules = [r for r in param_port_rules if r not in rules] - remove_rules = [r for r in rules if r not in param_port_rules] - - LOG.info(_("Creating %(new)s new rules, removing %(old)s " - "old rules."), - {'new': len(new_rules), - 'old': len(remove_rules)}) - - self._remove_port_rules(old_port['id'], remove_rules) - self._create_port_rules(port['id'], new_rules) - - self._security_ports[port['device']] = port - - def remove_port_filter(self, port): - LOG.info(_('Removing port filter')) - self._security_ports.pop(port['device'], None) - - @property - def ports(self): - return self._security_ports - - def _get_rule_remote_address(self, rule): - if rule['direction'] is 'ingress': - ip_prefix = 'source_ip_prefix' - else: - ip_prefix = 'dest_ip_prefix' - - if ip_prefix in rule: - return rule[ip_prefix] - return self._ACL_PROP_MAP['address_default'][rule['ethertype']] - - def _get_rule_protocol(self, rule): - protocol = self._get_rule_prop_or_default(rule, 'protocol') - if protocol in self._ACL_PROP_MAP['protocol'].keys(): - return self._ACL_PROP_MAP['protocol'][protocol] - - return protocol - - def _get_rule_prop_or_default(self, rule, prop): - if prop in rule: - return rule[prop] - return self._ACL_PROP_MAP['default'] diff --git a/neutron/plugins/hyperv/agent/utils.py b/neutron/plugins/hyperv/agent/utils.py deleted file mode 100644 index 31439f0b0..000000000 --- a/neutron/plugins/hyperv/agent/utils.py +++ /dev/null @@ -1,256 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# Copyright 2013 Pedro Navarro Perez -# 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. -# @author: Pedro Navarro Perez -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -import sys -import time - -from oslo.config import cfg - -from neutron.common import exceptions as n_exc -from neutron.openstack.common import log as logging - -# Check needed for unit testing on Unix -if sys.platform == 'win32': - import wmi - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class HyperVException(n_exc.NeutronException): - message = _('HyperVException: %(msg)s') - -WMI_JOB_STATE_STARTED = 4096 -WMI_JOB_STATE_RUNNING = 4 -WMI_JOB_STATE_COMPLETED = 7 - - -class HyperVUtils(object): - - _ETHERNET_SWITCH_PORT = 'Msvm_SwitchPort' - - _wmi_namespace = '//./root/virtualization' - - def __init__(self): - self._wmi_conn = None - - @property - def _conn(self): - if self._wmi_conn is None: - self._wmi_conn = wmi.WMI(moniker=self._wmi_namespace) - return self._wmi_conn - - def get_switch_ports(self, vswitch_name): - vswitch = self._get_vswitch(vswitch_name) - vswitch_ports = vswitch.associators( - wmi_result_class=self._ETHERNET_SWITCH_PORT) - return set(p.Name for p in vswitch_ports) - - def vnic_port_exists(self, port_id): - try: - self._get_vnic_settings(port_id) - except Exception: - return False - return True - - def get_vnic_ids(self): - return set( - p.ElementName - for p in self._conn.Msvm_SyntheticEthernetPortSettingData() - if p.ElementName is not None) - - def _get_vnic_settings(self, vnic_name): - vnic_settings = self._conn.Msvm_SyntheticEthernetPortSettingData( - ElementName=vnic_name) - if not vnic_settings: - raise HyperVException(msg=_('Vnic not found: %s') % vnic_name) - return vnic_settings[0] - - def connect_vnic_to_vswitch(self, vswitch_name, switch_port_name): - vnic_settings = self._get_vnic_settings(switch_port_name) - if not vnic_settings.Connection or not vnic_settings.Connection[0]: - port = self.get_port_by_id(switch_port_name, vswitch_name) - if port: - port_path = port.Path_() - else: - port_path = self._create_switch_port( - vswitch_name, switch_port_name) - vnic_settings.Connection = [port_path] - self._modify_virt_resource(vnic_settings) - - def _get_vm_from_res_setting_data(self, res_setting_data): - sd = res_setting_data.associators( - wmi_result_class='Msvm_VirtualSystemSettingData') - vm = sd[0].associators( - wmi_result_class='Msvm_ComputerSystem') - return vm[0] - - def _modify_virt_resource(self, res_setting_data): - vm = self._get_vm_from_res_setting_data(res_setting_data) - - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job_path, ret_val) = vs_man_svc.ModifyVirtualSystemResources( - vm.Path_(), [res_setting_data.GetText_(1)]) - self._check_job_status(ret_val, job_path) - - def _check_job_status(self, ret_val, jobpath): - """Poll WMI job state for completion.""" - if not ret_val: - return - elif ret_val not in [WMI_JOB_STATE_STARTED, WMI_JOB_STATE_RUNNING]: - raise HyperVException(msg=_('Job failed with error %d') % ret_val) - - job_wmi_path = jobpath.replace('\\', '/') - job = wmi.WMI(moniker=job_wmi_path) - - while job.JobState == WMI_JOB_STATE_RUNNING: - time.sleep(0.1) - job = wmi.WMI(moniker=job_wmi_path) - if job.JobState != WMI_JOB_STATE_COMPLETED: - job_state = job.JobState - if job.path().Class == "Msvm_ConcreteJob": - err_sum_desc = job.ErrorSummaryDescription - err_desc = job.ErrorDescription - err_code = job.ErrorCode - data = {'job_state': job_state, - 'err_sum_desc': err_sum_desc, - 'err_desc': err_desc, - 'err_code': err_code} - raise HyperVException( - msg=_("WMI job failed with status %(job_state)d. " - "Error details: %(err_sum_desc)s - %(err_desc)s - " - "Error code: %(err_code)d") % data) - else: - (error, ret_val) = job.GetError() - if not ret_val and error: - data = {'job_state': job_state, - 'error': error} - raise HyperVException( - msg=_("WMI job failed with status %(job_state)d. " - "Error details: %(error)s") % data) - else: - raise HyperVException( - msg=_("WMI job failed with status %d. " - "No error description available") % job_state) - - desc = job.Description - elap = job.ElapsedTime - LOG.debug(_("WMI job succeeded: %(desc)s, Elapsed=%(elap)s"), - {'desc': desc, 'elap': elap}) - - def _create_switch_port(self, vswitch_name, switch_port_name): - """Creates a switch port.""" - switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0] - vswitch_path = self._get_vswitch(vswitch_name).path_() - (new_port, ret_val) = switch_svc.CreateSwitchPort( - Name=switch_port_name, - FriendlyName=switch_port_name, - ScopeOfResidence="", - VirtualSwitch=vswitch_path) - if ret_val != 0: - raise HyperVException( - msg=_('Failed creating port for %s') % vswitch_name) - return new_port - - def disconnect_switch_port( - self, vswitch_name, switch_port_name, delete_port): - """Disconnects the switch port.""" - switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0] - switch_port_path = self._get_switch_port_path_by_name( - switch_port_name) - if not switch_port_path: - # Port not found. It happens when the VM was already deleted. - return - - (ret_val, ) = switch_svc.DisconnectSwitchPort( - SwitchPort=switch_port_path) - if ret_val != 0: - data = {'switch_port_name': switch_port_name, - 'vswitch_name': vswitch_name, - 'ret_val': ret_val} - raise HyperVException( - msg=_('Failed to disconnect port %(switch_port_name)s ' - 'from switch %(vswitch_name)s ' - 'with error %(ret_val)s') % data) - if delete_port: - (ret_val, ) = switch_svc.DeleteSwitchPort( - SwitchPort=switch_port_path) - if ret_val != 0: - data = {'switch_port_name': switch_port_name, - 'vswitch_name': vswitch_name, - 'ret_val': ret_val} - raise HyperVException( - msg=_('Failed to delete port %(switch_port_name)s ' - 'from switch %(vswitch_name)s ' - 'with error %(ret_val)s') % data) - - def _get_vswitch(self, vswitch_name): - vswitch = self._conn.Msvm_VirtualSwitch(ElementName=vswitch_name) - if not vswitch: - raise HyperVException(msg=_('VSwitch not found: %s') % - vswitch_name) - return vswitch[0] - - def _get_vswitch_external_port(self, vswitch): - vswitch_ports = vswitch.associators( - wmi_result_class=self._ETHERNET_SWITCH_PORT) - for vswitch_port in vswitch_ports: - lan_endpoints = vswitch_port.associators( - wmi_result_class='Msvm_SwitchLanEndpoint') - if lan_endpoints: - ext_port = lan_endpoints[0].associators( - wmi_result_class='Msvm_ExternalEthernetPort') - if ext_port: - return vswitch_port - - def set_vswitch_port_vlan_id(self, vlan_id, switch_port_name): - vlan_endpoint_settings = self._conn.Msvm_VLANEndpointSettingData( - ElementName=switch_port_name)[0] - if vlan_endpoint_settings.AccessVLAN != vlan_id: - vlan_endpoint_settings.AccessVLAN = vlan_id - vlan_endpoint_settings.put() - - def _get_switch_port_path_by_name(self, switch_port_name): - vswitch = self._conn.Msvm_SwitchPort(ElementName=switch_port_name) - if vswitch: - return vswitch[0].path_() - - def get_vswitch_id(self, vswitch_name): - vswitch = self._get_vswitch(vswitch_name) - return vswitch.Name - - def get_port_by_id(self, port_id, vswitch_name): - vswitch = self._get_vswitch(vswitch_name) - switch_ports = vswitch.associators( - wmi_result_class=self._ETHERNET_SWITCH_PORT) - for switch_port in switch_ports: - if (switch_port.ElementName == port_id): - return switch_port - - def enable_port_metrics_collection(self, switch_port_name): - raise NotImplementedError(_("Metrics collection is not supported on " - "this version of Hyper-V")) - - def enable_control_metrics(self, switch_port_name): - raise NotImplementedError(_("Metrics collection is not supported on " - "this version of Hyper-V")) - - def can_enable_control_metrics(self, switch_port_name): - return False diff --git a/neutron/plugins/hyperv/agent/utilsfactory.py b/neutron/plugins/hyperv/agent/utilsfactory.py deleted file mode 100644 index 5698255c3..000000000 --- a/neutron/plugins/hyperv/agent/utilsfactory.py +++ /dev/null @@ -1,72 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Claudiu Belu, Cloudbase Solutions Srl - -import sys - -from oslo.config import cfg - -from neutron.openstack.common import log as logging -from neutron.plugins.hyperv.agent import utils -from neutron.plugins.hyperv.agent import utilsv2 - -# Check needed for unit testing on Unix -if sys.platform == 'win32': - import wmi - -hyper_opts = [ - cfg.BoolOpt('force_hyperv_utils_v1', - default=False, - help=_('Force V1 WMI utility classes')), -] - -CONF = cfg.CONF -CONF.register_opts(hyper_opts, 'hyperv') - -LOG = logging.getLogger(__name__) - - -def _get_windows_version(): - return wmi.WMI(moniker='//./root/cimv2').Win32_OperatingSystem()[0].Version - - -def _check_min_windows_version(major, minor, build=0): - version_str = _get_windows_version() - return map(int, version_str.split('.')) >= [major, minor, build] - - -def get_hypervutils(): - # V1 virtualization namespace features are supported up to - # Windows Server / Hyper-V Server 2012 - # V2 virtualization namespace features are supported starting with - # Windows Server / Hyper-V Server 2012 - # Windows Server / Hyper-V Server 2012 R2 uses the V2 namespace and - # introduces additional features - - force_v1_flag = CONF.hyperv.force_hyperv_utils_v1 - if _check_min_windows_version(6, 3): - if force_v1_flag: - LOG.warning(_('V1 virtualization namespace no longer supported on ' - 'Windows Server / Hyper-V Server 2012 R2 or above.')) - cls = utilsv2.HyperVUtilsV2R2 - elif not force_v1_flag and _check_min_windows_version(6, 2): - cls = utilsv2.HyperVUtilsV2 - else: - cls = utils.HyperVUtils - LOG.debug(_("Loading class: %(module_name)s.%(class_name)s"), - {'module_name': cls.__module__, 'class_name': cls.__name__}) - return cls() diff --git a/neutron/plugins/hyperv/agent/utilsv2.py b/neutron/plugins/hyperv/agent/utilsv2.py deleted file mode 100644 index a55839487..000000000 --- a/neutron/plugins/hyperv/agent/utilsv2.py +++ /dev/null @@ -1,439 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Alessandro Pilotti, Cloudbase Solutions Srl -# @author: Claudiu Belu, Cloudbase Solutions Srl - -from neutron.plugins.hyperv.agent import utils - - -class HyperVUtilsV2(utils.HyperVUtils): - - _EXTERNAL_PORT = 'Msvm_ExternalEthernetPort' - _ETHERNET_SWITCH_PORT = 'Msvm_EthernetSwitchPort' - _PORT_ALLOC_SET_DATA = 'Msvm_EthernetPortAllocationSettingData' - _PORT_VLAN_SET_DATA = 'Msvm_EthernetSwitchPortVlanSettingData' - _PORT_SECURITY_SET_DATA = 'Msvm_EthernetSwitchPortSecuritySettingData' - _PORT_ALLOC_ACL_SET_DATA = 'Msvm_EthernetSwitchPortAclSettingData' - _PORT_EXT_ACL_SET_DATA = _PORT_ALLOC_ACL_SET_DATA - _LAN_ENDPOINT = 'Msvm_LANEndpoint' - _STATE_DISABLED = 3 - _OPERATION_MODE_ACCESS = 1 - - _VIRTUAL_SYSTEM_SETTING_DATA = 'Msvm_VirtualSystemSettingData' - _VM_SUMMARY_ENABLED_STATE = 100 - _HYPERV_VM_STATE_ENABLED = 2 - - _ACL_DIR_IN = 1 - _ACL_DIR_OUT = 2 - - _ACL_TYPE_IPV4 = 2 - _ACL_TYPE_IPV6 = 3 - - _ACL_ACTION_ALLOW = 1 - _ACL_ACTION_DENY = 2 - _ACL_ACTION_METER = 3 - - _METRIC_ENABLED = 2 - _NET_IN_METRIC_NAME = 'Filtered Incoming Network Traffic' - _NET_OUT_METRIC_NAME = 'Filtered Outgoing Network Traffic' - - _ACL_APPLICABILITY_LOCAL = 1 - _ACL_APPLICABILITY_REMOTE = 2 - - _ACL_DEFAULT = 'ANY' - _IPV4_ANY = '0.0.0.0/0' - _IPV6_ANY = '::/0' - _TCP_PROTOCOL = 'tcp' - _UDP_PROTOCOL = 'udp' - _ICMP_PROTOCOL = '1' - _MAX_WEIGHT = 65500 - - # 2 directions x 2 address types = 4 ACLs - _REJECT_ACLS_COUNT = 4 - - _wmi_namespace = '//./root/virtualization/v2' - - def __init__(self): - super(HyperVUtilsV2, self).__init__() - - def connect_vnic_to_vswitch(self, vswitch_name, switch_port_name): - vnic = self._get_vnic_settings(switch_port_name) - vswitch = self._get_vswitch(vswitch_name) - - port, found = self._get_switch_port_allocation(switch_port_name, True) - port.HostResource = [vswitch.path_()] - port.Parent = vnic.path_() - if not found: - vm = self._get_vm_from_res_setting_data(vnic) - self._add_virt_resource(vm, port) - else: - self._modify_virt_resource(port) - - def _modify_virt_resource(self, res_setting_data): - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job_path, out_set_data, ret_val) = vs_man_svc.ModifyResourceSettings( - ResourceSettings=[res_setting_data.GetText_(1)]) - self._check_job_status(ret_val, job_path) - - def _add_virt_resource(self, vm, res_setting_data): - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job_path, out_set_data, ret_val) = vs_man_svc.AddResourceSettings( - vm.path_(), [res_setting_data.GetText_(1)]) - self._check_job_status(ret_val, job_path) - - def _remove_virt_resource(self, res_setting_data): - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job, ret_val) = vs_man_svc.RemoveResourceSettings( - ResourceSettings=[res_setting_data.path_()]) - self._check_job_status(ret_val, job) - - def _add_virt_feature(self, element, res_setting_data): - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job_path, out_set_data, ret_val) = vs_man_svc.AddFeatureSettings( - element.path_(), [res_setting_data.GetText_(1)]) - self._check_job_status(ret_val, job_path) - - def _remove_virt_feature(self, feature_resource): - self._remove_multiple_virt_features([feature_resource]) - - def _remove_multiple_virt_features(self, feature_resources): - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job_path, ret_val) = vs_man_svc.RemoveFeatureSettings( - FeatureSettings=[f.path_() for f in feature_resources]) - self._check_job_status(ret_val, job_path) - - def disconnect_switch_port( - self, vswitch_name, switch_port_name, delete_port): - """Disconnects the switch port.""" - sw_port, found = self._get_switch_port_allocation(switch_port_name) - if not sw_port: - # Port not found. It happens when the VM was already deleted. - return - - if delete_port: - self._remove_virt_resource(sw_port) - else: - sw_port.EnabledState = self._STATE_DISABLED - self._modify_virt_resource(sw_port) - - def _get_vswitch(self, vswitch_name): - vswitch = self._conn.Msvm_VirtualEthernetSwitch( - ElementName=vswitch_name) - if not len(vswitch): - raise utils.HyperVException(msg=_('VSwitch not found: %s') % - vswitch_name) - return vswitch[0] - - def _get_vswitch_external_port(self, vswitch): - vswitch_ports = vswitch.associators( - wmi_result_class=self._ETHERNET_SWITCH_PORT) - for vswitch_port in vswitch_ports: - lan_endpoints = vswitch_port.associators( - wmi_result_class=self._LAN_ENDPOINT) - if len(lan_endpoints): - lan_endpoints = lan_endpoints[0].associators( - wmi_result_class=self._LAN_ENDPOINT) - if len(lan_endpoints): - ext_port = lan_endpoints[0].associators( - wmi_result_class=self._EXTERNAL_PORT) - if ext_port: - return vswitch_port - - def set_vswitch_port_vlan_id(self, vlan_id, switch_port_name): - port_alloc, found = self._get_switch_port_allocation(switch_port_name) - if not found: - raise utils.HyperVException( - msg=_('Port Allocation not found: %s') % switch_port_name) - - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - vlan_settings = self._get_vlan_setting_data_from_port_alloc(port_alloc) - if vlan_settings: - # Removing the feature because it cannot be modified - # due to a wmi exception. - (job_path, ret_val) = vs_man_svc.RemoveFeatureSettings( - FeatureSettings=[vlan_settings.path_()]) - self._check_job_status(ret_val, job_path) - - (vlan_settings, found) = self._get_vlan_setting_data(switch_port_name) - vlan_settings.AccessVlanId = vlan_id - vlan_settings.OperationMode = self._OPERATION_MODE_ACCESS - (job_path, out, ret_val) = vs_man_svc.AddFeatureSettings( - port_alloc.path_(), [vlan_settings.GetText_(1)]) - self._check_job_status(ret_val, job_path) - - def _get_vlan_setting_data_from_port_alloc(self, port_alloc): - return self._get_first_item(port_alloc.associators( - wmi_result_class=self._PORT_VLAN_SET_DATA)) - - def _get_vlan_setting_data(self, switch_port_name, create=True): - return self._get_setting_data( - self._PORT_VLAN_SET_DATA, - switch_port_name, create) - - def _get_switch_port_allocation(self, switch_port_name, create=False): - return self._get_setting_data( - self._PORT_ALLOC_SET_DATA, - switch_port_name, create) - - def _get_setting_data(self, class_name, element_name, create=True): - element_name = element_name.replace("'", '"') - q = self._conn.query("SELECT * FROM %(class_name)s WHERE " - "ElementName = '%(element_name)s'" % - {"class_name": class_name, - "element_name": element_name}) - data = self._get_first_item(q) - found = data is not None - if not data and create: - data = self._get_default_setting_data(class_name) - data.ElementName = element_name - return data, found - - def _get_default_setting_data(self, class_name): - return self._conn.query("SELECT * FROM %s WHERE InstanceID " - "LIKE '%%\\Default'" % class_name)[0] - - def _get_first_item(self, obj): - if obj: - return obj[0] - - def enable_port_metrics_collection(self, switch_port_name): - port, found = self._get_switch_port_allocation(switch_port_name, False) - if not found: - return - - # Add the ACLs only if they don't already exist - acls = port.associators(wmi_result_class=self._PORT_ALLOC_ACL_SET_DATA) - for acl_type in [self._ACL_TYPE_IPV4, self._ACL_TYPE_IPV6]: - for acl_dir in [self._ACL_DIR_IN, self._ACL_DIR_OUT]: - _acls = self._filter_acls( - acls, self._ACL_ACTION_METER, acl_dir, acl_type) - - if not _acls: - acl = self._create_acl( - acl_dir, acl_type, self._ACL_ACTION_METER) - self._add_virt_feature(port, acl) - - def enable_control_metrics(self, switch_port_name): - port, found = self._get_switch_port_allocation(switch_port_name, False) - if not found: - return - - metric_svc = self._conn.Msvm_MetricService()[0] - metric_names = [self._NET_IN_METRIC_NAME, self._NET_OUT_METRIC_NAME] - - for metric_name in metric_names: - metric_def = self._conn.CIM_BaseMetricDefinition(Name=metric_name) - if metric_def: - metric_svc.ControlMetrics( - Subject=port.path_(), - Definition=metric_def[0].path_(), - MetricCollectionEnabled=self._METRIC_ENABLED) - - def can_enable_control_metrics(self, switch_port_name): - port, found = self._get_switch_port_allocation(switch_port_name, False) - if not found: - return False - - if not self._is_port_vm_started(port): - return False - - # all 4 meter ACLs must be existent first. (2 x direction) - acls = port.associators(wmi_result_class=self._PORT_ALLOC_ACL_SET_DATA) - acls = [a for a in acls if a.Action == self._ACL_ACTION_METER] - if len(acls) < 2: - return False - return True - - def _is_port_vm_started(self, port): - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - vmsettings = port.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA) - #See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx - (ret_val, summary_info) = vs_man_svc.GetSummaryInformation( - [self._VM_SUMMARY_ENABLED_STATE], - [v.path_() for v in vmsettings]) - if ret_val or not summary_info: - raise utils.HyperVException(msg=_('Cannot get VM summary data ' - 'for: %s') % port.ElementName) - - return summary_info[0].EnabledState is self._HYPERV_VM_STATE_ENABLED - - def create_security_rule(self, switch_port_name, direction, acl_type, - local_port, protocol, remote_address): - port, found = self._get_switch_port_allocation(switch_port_name, False) - if not found: - return - - # Add the ACLs only if they don't already exist - acls = port.associators(wmi_result_class=self._PORT_EXT_ACL_SET_DATA) - weight = self._get_new_weight(acls) - self._bind_security_rule( - port, direction, acl_type, self._ACL_ACTION_ALLOW, local_port, - protocol, remote_address, weight) - - def remove_security_rule(self, switch_port_name, direction, acl_type, - local_port, protocol, remote_address): - port, found = self._get_switch_port_allocation(switch_port_name, False) - if not found: - # Port not found. It happens when the VM was already deleted. - return - - acls = port.associators(wmi_result_class=self._PORT_EXT_ACL_SET_DATA) - filtered_acls = self._filter_security_acls( - acls, self._ACL_ACTION_ALLOW, direction, acl_type, local_port, - protocol, remote_address) - - for acl in filtered_acls: - self._remove_virt_feature(acl) - - def remove_all_security_rules(self, switch_port_name): - port, found = self._get_switch_port_allocation(switch_port_name, False) - if not found: - # Port not found. It happens when the VM was already deleted. - return - - acls = port.associators(wmi_result_class=self._PORT_EXT_ACL_SET_DATA) - filtered_acls = [a for a in acls if - a.Action is not self._ACL_ACTION_METER] - - if filtered_acls: - self._remove_multiple_virt_features(filtered_acls) - - def create_default_reject_all_rules(self, switch_port_name): - port, found = self._get_switch_port_allocation(switch_port_name, False) - if not found: - raise utils.HyperVException( - msg=_('Port Allocation not found: %s') % switch_port_name) - - acls = port.associators(wmi_result_class=self._PORT_EXT_ACL_SET_DATA) - filtered_acls = [v for v in acls if v.Action == self._ACL_ACTION_DENY] - - if len(filtered_acls) >= self._REJECT_ACLS_COUNT: - return - - for acl in filtered_acls: - self._remove_virt_feature(acl) - - weight = 0 - ipv4_pair = (self._ACL_TYPE_IPV4, self._IPV4_ANY) - ipv6_pair = (self._ACL_TYPE_IPV6, self._IPV6_ANY) - for direction in [self._ACL_DIR_IN, self._ACL_DIR_OUT]: - for acl_type, address in [ipv4_pair, ipv6_pair]: - for protocol in [self._TCP_PROTOCOL, - self._UDP_PROTOCOL, - self._ICMP_PROTOCOL]: - self._bind_security_rule( - port, direction, acl_type, self._ACL_ACTION_DENY, - self._ACL_DEFAULT, protocol, address, weight) - weight += 1 - - def _bind_security_rule(self, port, direction, acl_type, action, - local_port, protocol, remote_address, weight): - acls = port.associators(wmi_result_class=self._PORT_EXT_ACL_SET_DATA) - filtered_acls = self._filter_security_acls( - acls, action, direction, acl_type, local_port, protocol, - remote_address) - - for acl in filtered_acls: - self._remove_virt_feature(acl) - - acl = self._create_security_acl( - direction, acl_type, action, local_port, protocol, remote_address, - weight) - - self._add_virt_feature(port, acl) - - def _create_acl(self, direction, acl_type, action): - acl = self._get_default_setting_data(self._PORT_ALLOC_ACL_SET_DATA) - acl.set(Direction=direction, - AclType=acl_type, - Action=action, - Applicability=self._ACL_APPLICABILITY_LOCAL) - return acl - - def _create_security_acl(self, direction, acl_type, action, local_port, - protocol, remote_ip_address, weight): - acl = self._create_acl(direction, acl_type, action) - (remote_address, remote_prefix_length) = remote_ip_address.split('/') - acl.set(Applicability=self._ACL_APPLICABILITY_REMOTE, - RemoteAddress=remote_address, - RemoteAddressPrefixLength=remote_prefix_length) - return acl - - def _filter_acls(self, acls, action, direction, acl_type, remote_addr=""): - return [v for v in acls - if v.Action == action and - v.Direction == direction and - v.AclType == acl_type and - v.RemoteAddress == remote_addr] - - def _filter_security_acls(self, acls, acl_action, direction, acl_type, - local_port, protocol, remote_addr=""): - (remote_address, remote_prefix_length) = remote_addr.split('/') - remote_prefix_length = int(remote_prefix_length) - - return [v for v in acls - if v.Direction == direction and - v.Action in [self._ACL_ACTION_ALLOW, self._ACL_ACTION_DENY] and - v.AclType == acl_type and - v.RemoteAddress == remote_address and - v.RemoteAddressPrefixLength == remote_prefix_length] - - def _get_new_weight(self, acls): - return 0 - - -class HyperVUtilsV2R2(HyperVUtilsV2): - _PORT_EXT_ACL_SET_DATA = 'Msvm_EthernetSwitchPortExtendedAclSettingData' - _MAX_WEIGHT = 65500 - - # 2 directions x 2 address types x 3 protocols = 12 ACLs - _REJECT_ACLS_COUNT = 12 - - def _create_security_acl(self, direction, acl_type, action, local_port, - protocol, remote_addr, weight): - acl = self._get_default_setting_data(self._PORT_EXT_ACL_SET_DATA) - acl.set(Direction=direction, - Action=action, - LocalPort=str(local_port), - Protocol=protocol, - RemoteIPAddress=remote_addr, - IdleSessionTimeout=0, - Weight=weight) - return acl - - def _filter_security_acls(self, acls, action, direction, acl_type, - local_port, protocol, remote_addr=""): - return [v for v in acls - if v.Action == action and - v.Direction == direction and - v.LocalPort == str(local_port) and - v.Protocol == protocol and - v.RemoteIPAddress == remote_addr] - - def _get_new_weight(self, acls): - acls = [a for a in acls if a.Action is not self._ACL_ACTION_DENY] - if not acls: - return self._MAX_WEIGHT - 1 - - weights = [a.Weight for a in acls] - min_weight = min(weights) - for weight in range(min_weight, self._MAX_WEIGHT): - if weight not in weights: - return weight - - return min_weight - 1 diff --git a/neutron/plugins/hyperv/agent_notifier_api.py b/neutron/plugins/hyperv/agent_notifier_api.py deleted file mode 100644 index 058d96c4c..000000000 --- a/neutron/plugins/hyperv/agent_notifier_api.py +++ /dev/null @@ -1,80 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.openstack.common import log as logging -from neutron.plugins.hyperv.common import constants - -LOG = logging.getLogger(__name__) - - -class AgentNotifierApi(rpc_compat.RpcProxy): - '''Agent side of the openvswitch rpc API. - - API version history: - 1.0 - Initial version. - - ''' - - BASE_RPC_API_VERSION = '1.0' - - def __init__(self, topic): - super(AgentNotifierApi, self).__init__( - topic=topic, default_version=self.BASE_RPC_API_VERSION) - self.topic_network_delete = topics.get_topic_name(topic, - topics.NETWORK, - topics.DELETE) - self.topic_port_update = topics.get_topic_name(topic, - topics.PORT, - topics.UPDATE) - self.topic_port_delete = topics.get_topic_name(topic, - topics.PORT, - topics.DELETE) - self.topic_tunnel_update = topics.get_topic_name(topic, - constants.TUNNEL, - topics.UPDATE) - - def network_delete(self, context, network_id): - self.fanout_cast(context, - self.make_msg('network_delete', - network_id=network_id), - topic=self.topic_network_delete) - - def port_update(self, context, port, network_type, segmentation_id, - physical_network): - self.fanout_cast(context, - self.make_msg('port_update', - port=port, - network_type=network_type, - segmentation_id=segmentation_id, - physical_network=physical_network), - topic=self.topic_port_update) - - def port_delete(self, context, port_id): - self.fanout_cast(context, - self.make_msg('port_delete', - port_id=port_id), - topic=self.topic_port_delete) - - def tunnel_update(self, context, tunnel_ip, tunnel_id): - self.fanout_cast(context, - self.make_msg('tunnel_update', - tunnel_ip=tunnel_ip, - tunnel_id=tunnel_id), - topic=self.topic_tunnel_update) diff --git a/neutron/plugins/hyperv/common/__init__.py b/neutron/plugins/hyperv/common/__init__.py deleted file mode 100644 index 7ef4e09fa..000000000 --- a/neutron/plugins/hyperv/common/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. diff --git a/neutron/plugins/hyperv/common/constants.py b/neutron/plugins/hyperv/common/constants.py deleted file mode 100644 index b36d9b559..000000000 --- a/neutron/plugins/hyperv/common/constants.py +++ /dev/null @@ -1,23 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -# Topic for tunnel notifications between the plugin and agent -TUNNEL = 'tunnel' - -# Special vlan_id value in ovs_vlan_allocations table indicating flat network -FLAT_VLAN_ID = -1 diff --git a/neutron/plugins/hyperv/db.py b/neutron/plugins/hyperv/db.py deleted file mode 100644 index 159275a85..000000000 --- a/neutron/plugins/hyperv/db.py +++ /dev/null @@ -1,219 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -from six import moves -from sqlalchemy.orm import exc - -from neutron.common import exceptions as n_exc -import neutron.db.api as db_api -from neutron.db import models_v2 -from neutron.openstack.common import log as logging -from neutron.plugins.hyperv.common import constants -from neutron.plugins.hyperv import model as hyperv_model - -LOG = logging.getLogger(__name__) - - -class HyperVPluginDB(object): - def initialize(self): - db_api.configure_db() - - def reserve_vlan(self, session): - with session.begin(subtransactions=True): - alloc_q = session.query(hyperv_model.VlanAllocation) - alloc_q = alloc_q.filter_by(allocated=False) - alloc = alloc_q.first() - if alloc: - LOG.debug(_("Reserving vlan %(vlan_id)s on physical network " - "%(physical_network)s from pool"), - {'vlan_id': alloc.vlan_id, - 'physical_network': alloc.physical_network}) - alloc.allocated = True - return (alloc.physical_network, alloc.vlan_id) - raise n_exc.NoNetworkAvailable() - - def reserve_flat_net(self, session): - with session.begin(subtransactions=True): - alloc_q = session.query(hyperv_model.VlanAllocation) - alloc_q = alloc_q.filter_by(allocated=False, - vlan_id=constants.FLAT_VLAN_ID) - alloc = alloc_q.first() - if alloc: - LOG.debug(_("Reserving flat physical network " - "%(physical_network)s from pool"), - {'physical_network': alloc.physical_network}) - alloc.allocated = True - return alloc.physical_network - raise n_exc.NoNetworkAvailable() - - def reserve_specific_vlan(self, session, physical_network, vlan_id): - with session.begin(subtransactions=True): - try: - alloc_q = session.query(hyperv_model.VlanAllocation) - alloc_q = alloc_q.filter_by( - physical_network=physical_network, - vlan_id=vlan_id) - alloc = alloc_q.one() - if alloc.allocated: - if vlan_id == constants.FLAT_VLAN_ID: - raise n_exc.FlatNetworkInUse( - physical_network=physical_network) - else: - raise n_exc.VlanIdInUse( - vlan_id=vlan_id, - physical_network=physical_network) - LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical " - "network %(physical_network)s from pool"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - alloc.allocated = True - except exc.NoResultFound: - raise n_exc.NoNetworkAvailable() - - def reserve_specific_flat_net(self, session, physical_network): - return self.reserve_specific_vlan(session, physical_network, - constants.FLAT_VLAN_ID) - - def add_network_binding(self, session, network_id, network_type, - physical_network, segmentation_id): - with session.begin(subtransactions=True): - binding = hyperv_model.NetworkBinding( - network_id, network_type, - physical_network, - segmentation_id) - session.add(binding) - - def get_port(self, port_id): - session = db_api.get_session() - try: - port = session.query(models_v2.Port).filter_by(id=port_id).one() - except exc.NoResultFound: - port = None - return port - - def get_network_binding(self, session, network_id): - session = session or db_api.get_session() - try: - binding_q = session.query(hyperv_model.NetworkBinding) - binding_q = binding_q.filter_by(network_id=network_id) - return binding_q.one() - except exc.NoResultFound: - return - - def set_port_status(self, port_id, status): - session = db_api.get_session() - try: - port = session.query(models_v2.Port).filter_by(id=port_id).one() - port['status'] = status - session.merge(port) - session.flush() - except exc.NoResultFound: - raise n_exc.PortNotFound(port_id=port_id) - - def release_vlan(self, session, physical_network, vlan_id): - with session.begin(subtransactions=True): - try: - alloc_q = session.query(hyperv_model.VlanAllocation) - alloc_q = alloc_q.filter_by(physical_network=physical_network, - vlan_id=vlan_id) - alloc = alloc_q.one() - alloc.allocated = False - #session.delete(alloc) - LOG.debug(_("Releasing vlan %(vlan_id)s on physical network " - "%(physical_network)s"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - except exc.NoResultFound: - LOG.warning(_("vlan_id %(vlan_id)s on physical network " - "%(physical_network)s not found"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - - def _add_missing_allocatable_vlans(self, session, vlan_ids, - physical_network): - for vlan_id in sorted(vlan_ids): - alloc = hyperv_model.VlanAllocation( - physical_network, vlan_id) - session.add(alloc) - - def _remove_non_allocatable_vlans(self, session, - physical_network, - vlan_ids, - allocations): - if physical_network in allocations: - for alloc in allocations[physical_network]: - try: - # see if vlan is allocatable - vlan_ids.remove(alloc.vlan_id) - except KeyError: - # it's not allocatable, so check if its allocated - if not alloc.allocated: - # it's not, so remove it from table - LOG.debug(_( - "Removing vlan %(vlan_id)s on " - "physical network " - "%(physical_network)s from pool"), - {'vlan_id': alloc.vlan_id, - 'physical_network': physical_network}) - session.delete(alloc) - del allocations[physical_network] - - def _remove_unconfigured_vlans(self, session, allocations): - for allocs in allocations.itervalues(): - for alloc in allocs: - if not alloc.allocated: - LOG.debug(_("Removing vlan %(vlan_id)s on physical " - "network %(physical_network)s from pool"), - {'vlan_id': alloc.vlan_id, - 'physical_network': alloc.physical_network}) - session.delete(alloc) - - def sync_vlan_allocations(self, network_vlan_ranges): - """Synchronize vlan_allocations table with configured VLAN ranges.""" - - session = db_api.get_session() - with session.begin(): - # get existing allocations for all physical networks - allocations = dict() - allocs_q = session.query(hyperv_model.VlanAllocation) - for alloc in allocs_q: - allocations.setdefault(alloc.physical_network, - set()).add(alloc) - - # process vlan ranges for each configured physical network - for physical_network, vlan_ranges in network_vlan_ranges.items(): - # determine current configured allocatable vlans for this - # physical network - vlan_ids = set() - for vlan_range in vlan_ranges: - vlan_ids |= set(moves.xrange(vlan_range[0], - vlan_range[1] + 1)) - - # remove from table unallocated vlans not currently allocatable - self._remove_non_allocatable_vlans(session, - physical_network, - vlan_ids, - allocations) - - # add missing allocatable vlans to table - self._add_missing_allocatable_vlans(session, vlan_ids, - physical_network) - - # remove from table unallocated vlans for any unconfigured physical - # networks - self._remove_unconfigured_vlans(session, allocations) diff --git a/neutron/plugins/hyperv/hyperv_neutron_plugin.py b/neutron/plugins/hyperv/hyperv_neutron_plugin.py deleted file mode 100644 index 4307e5133..000000000 --- a/neutron/plugins/hyperv/hyperv_neutron_plugin.py +++ /dev/null @@ -1,333 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -from oslo.config import cfg - -from neutron.api.v2 import attributes -from neutron.common import exceptions as n_exc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.db import agents_db -from neutron.db import db_base_plugin_v2 -from neutron.db import external_net_db -from neutron.db import l3_gwmode_db -from neutron.db import portbindings_base -from neutron.db import quota_db # noqa -from neutron.extensions import portbindings -from neutron.extensions import providernet as provider -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants as svc_constants -from neutron.plugins.common import utils as plugin_utils -from neutron.plugins.hyperv import agent_notifier_api -from neutron.plugins.hyperv.common import constants -from neutron.plugins.hyperv import db as hyperv_db -from neutron.plugins.hyperv import rpc_callbacks - - -DEFAULT_VLAN_RANGES = [] - -hyperv_opts = [ - cfg.StrOpt('tenant_network_type', default='local', - help=_("Network type for tenant networks " - "(local, flat, vlan or none)")), - cfg.ListOpt('network_vlan_ranges', - default=DEFAULT_VLAN_RANGES, - help=_("List of :: " - "or ")), -] - -cfg.CONF.register_opts(hyperv_opts, "HYPERV") - -LOG = logging.getLogger(__name__) - - -class BaseNetworkProvider(object): - def __init__(self): - self._db = hyperv_db.HyperVPluginDB() - - def create_network(self, session, attrs): - pass - - def delete_network(self, session, binding): - pass - - def extend_network_dict(self, network, binding): - pass - - -class LocalNetworkProvider(BaseNetworkProvider): - def create_network(self, session, attrs): - network_type = attrs.get(provider.NETWORK_TYPE) - segmentation_id = attrs.get(provider.SEGMENTATION_ID) - if attributes.is_attr_set(segmentation_id): - msg = _("segmentation_id specified " - "for %s network") % network_type - raise n_exc.InvalidInput(error_message=msg) - attrs[provider.SEGMENTATION_ID] = None - - physical_network = attrs.get(provider.PHYSICAL_NETWORK) - if attributes.is_attr_set(physical_network): - msg = _("physical_network specified " - "for %s network") % network_type - raise n_exc.InvalidInput(error_message=msg) - attrs[provider.PHYSICAL_NETWORK] = None - - def extend_network_dict(self, network, binding): - network[provider.PHYSICAL_NETWORK] = None - network[provider.SEGMENTATION_ID] = None - - -class FlatNetworkProvider(BaseNetworkProvider): - def create_network(self, session, attrs): - network_type = attrs.get(provider.NETWORK_TYPE) - segmentation_id = attrs.get(provider.SEGMENTATION_ID) - if attributes.is_attr_set(segmentation_id): - msg = _("segmentation_id specified " - "for %s network") % network_type - raise n_exc.InvalidInput(error_message=msg) - segmentation_id = constants.FLAT_VLAN_ID - attrs[provider.SEGMENTATION_ID] = segmentation_id - - physical_network = attrs.get(provider.PHYSICAL_NETWORK) - if not attributes.is_attr_set(physical_network): - physical_network = self._db.reserve_flat_net(session) - attrs[provider.PHYSICAL_NETWORK] = physical_network - else: - self._db.reserve_specific_flat_net(session, physical_network) - - def delete_network(self, session, binding): - self._db.release_vlan(session, binding.physical_network, - constants.FLAT_VLAN_ID) - - def extend_network_dict(self, network, binding): - network[provider.PHYSICAL_NETWORK] = binding.physical_network - - -class VlanNetworkProvider(BaseNetworkProvider): - def create_network(self, session, attrs): - segmentation_id = attrs.get(provider.SEGMENTATION_ID) - if attributes.is_attr_set(segmentation_id): - physical_network = attrs.get(provider.PHYSICAL_NETWORK) - if not attributes.is_attr_set(physical_network): - msg = _("physical_network not provided") - raise n_exc.InvalidInput(error_message=msg) - self._db.reserve_specific_vlan(session, physical_network, - segmentation_id) - else: - (physical_network, - segmentation_id) = self._db.reserve_vlan(session) - attrs[provider.SEGMENTATION_ID] = segmentation_id - attrs[provider.PHYSICAL_NETWORK] = physical_network - - def delete_network(self, session, binding): - self._db.release_vlan( - session, binding.physical_network, - binding.segmentation_id) - - def extend_network_dict(self, network, binding): - network[provider.PHYSICAL_NETWORK] = binding.physical_network - network[provider.SEGMENTATION_ID] = binding.segmentation_id - - -class HyperVNeutronPlugin(agents_db.AgentDbMixin, - db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - l3_gwmode_db.L3_NAT_db_mixin, - portbindings_base.PortBindingBaseMixin): - - # This attribute specifies whether the plugin supports or not - # bulk operations. Name mangling is used in order to ensure it - # is qualified by class - __native_bulk_support = True - supported_extension_aliases = ["provider", "external-net", "router", - "agent", "ext-gw-mode", "binding", "quotas"] - - def __init__(self, configfile=None): - self._db = hyperv_db.HyperVPluginDB() - self._db.initialize() - self.base_binding_dict = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_HYPERV} - portbindings_base.register_port_dict_function() - self._set_tenant_network_type() - - self._parse_network_vlan_ranges() - self._create_network_providers_map() - self._db.sync_vlan_allocations(self._network_vlan_ranges) - - self._setup_rpc() - - def _set_tenant_network_type(self): - tenant_network_type = cfg.CONF.HYPERV.tenant_network_type - if tenant_network_type not in [svc_constants.TYPE_LOCAL, - svc_constants.TYPE_FLAT, - svc_constants.TYPE_VLAN, - svc_constants.TYPE_NONE]: - msg = _( - "Invalid tenant_network_type: %s. " - "Agent terminated!") % tenant_network_type - raise n_exc.InvalidInput(error_message=msg) - self._tenant_network_type = tenant_network_type - - def _setup_rpc(self): - # RPC support - self.service_topics = {svc_constants.CORE: topics.PLUGIN, - svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN} - self.conn = rpc_compat.create_connection(new=True) - self.notifier = agent_notifier_api.AgentNotifierApi( - topics.AGENT) - self.endpoints = [rpc_callbacks.HyperVRpcCallbacks(self.notifier), - agents_db.AgentExtRpcCallback()] - for svc_topic in self.service_topics.values(): - self.conn.create_consumer(svc_topic, self.endpoints, fanout=False) - # Consume from all consumers in threads - self.conn.consume_in_threads() - - def _parse_network_vlan_ranges(self): - self._network_vlan_ranges = plugin_utils.parse_network_vlan_ranges( - cfg.CONF.HYPERV.network_vlan_ranges) - LOG.info(_("Network VLAN ranges: %s"), self._network_vlan_ranges) - - def _check_vlan_id_in_range(self, physical_network, vlan_id): - for r in self._network_vlan_ranges[physical_network]: - if vlan_id >= r[0] and vlan_id <= r[1]: - return True - return False - - def _create_network_providers_map(self): - self._network_providers_map = { - svc_constants.TYPE_LOCAL: LocalNetworkProvider(), - svc_constants.TYPE_FLAT: FlatNetworkProvider(), - svc_constants.TYPE_VLAN: VlanNetworkProvider() - } - - def _process_provider_create(self, context, session, attrs): - network_type = attrs.get(provider.NETWORK_TYPE) - network_type_set = attributes.is_attr_set(network_type) - if not network_type_set: - if self._tenant_network_type == svc_constants.TYPE_NONE: - raise n_exc.TenantNetworksDisabled() - network_type = self._tenant_network_type - attrs[provider.NETWORK_TYPE] = network_type - - if network_type not in self._network_providers_map: - msg = _("Network type %s not supported") % network_type - raise n_exc.InvalidInput(error_message=msg) - p = self._network_providers_map[network_type] - # Provider specific network creation - p.create_network(session, attrs) - - def create_network(self, context, network): - session = context.session - with session.begin(subtransactions=True): - network_attrs = network['network'] - self._process_provider_create(context, session, network_attrs) - - net = super(HyperVNeutronPlugin, self).create_network( - context, network) - - network_type = network_attrs[provider.NETWORK_TYPE] - physical_network = network_attrs[provider.PHYSICAL_NETWORK] - segmentation_id = network_attrs[provider.SEGMENTATION_ID] - - self._db.add_network_binding( - session, net['id'], network_type, - physical_network, segmentation_id) - - self._process_l3_create(context, net, network['network']) - self._extend_network_dict_provider(context, net) - - LOG.debug(_("Created network: %s"), net['id']) - return net - - def _extend_network_dict_provider(self, context, network): - binding = self._db.get_network_binding( - context.session, network['id']) - network[provider.NETWORK_TYPE] = binding.network_type - p = self._network_providers_map[binding.network_type] - p.extend_network_dict(network, binding) - - def update_network(self, context, id, network): - provider._raise_if_updates_provider_attributes(network['network']) - - session = context.session - with session.begin(subtransactions=True): - net = super(HyperVNeutronPlugin, self).update_network(context, id, - network) - self._process_l3_update(context, net, network['network']) - self._extend_network_dict_provider(context, net) - return net - - def delete_network(self, context, id): - session = context.session - with session.begin(subtransactions=True): - binding = self._db.get_network_binding(session, id) - self._process_l3_delete(context, id) - super(HyperVNeutronPlugin, self).delete_network(context, id) - p = self._network_providers_map[binding.network_type] - p.delete_network(session, binding) - # the network_binding record is deleted via cascade from - # the network record, so explicit removal is not necessary - self.notifier.network_delete(context, id) - - def get_network(self, context, id, fields=None): - net = super(HyperVNeutronPlugin, self).get_network(context, id, None) - self._extend_network_dict_provider(context, net) - return self._fields(net, fields) - - def get_networks(self, context, filters=None, fields=None): - nets = super(HyperVNeutronPlugin, self).get_networks( - context, filters, None) - for net in nets: - self._extend_network_dict_provider(context, net) - - return [self._fields(net, fields) for net in nets] - - def create_port(self, context, port): - port_data = port['port'] - port = super(HyperVNeutronPlugin, self).create_port(context, port) - self._process_portbindings_create_and_update(context, - port_data, - port) - return port - - def update_port(self, context, id, port): - original_port = super(HyperVNeutronPlugin, self).get_port( - context, id) - port_data = port['port'] - port = super(HyperVNeutronPlugin, self).update_port(context, id, port) - self._process_portbindings_create_and_update(context, - port_data, - port) - if original_port['admin_state_up'] != port['admin_state_up']: - binding = self._db.get_network_binding( - None, port['network_id']) - self.notifier.port_update(context, port, - binding.network_type, - binding.segmentation_id, - binding.physical_network) - return port - - def delete_port(self, context, id, l3_port_check=True): - # if needed, check to see if this is a port owned by - # and l3-router. If so, we should prevent deletion. - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - self.disassociate_floatingips(context, id) - - super(HyperVNeutronPlugin, self).delete_port(context, id) - self.notifier.port_delete(context, id) diff --git a/neutron/plugins/hyperv/model.py b/neutron/plugins/hyperv/model.py deleted file mode 100644 index 808d2e591..000000000 --- a/neutron/plugins/hyperv/model.py +++ /dev/null @@ -1,55 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String - -from neutron.db import model_base - - -class VlanAllocation(model_base.BASEV2): - """Represents allocation state of vlan_id on physical network.""" - __tablename__ = 'hyperv_vlan_allocations' - - physical_network = Column(String(64), nullable=False, primary_key=True) - vlan_id = Column(Integer, nullable=False, primary_key=True, - autoincrement=False) - allocated = Column(Boolean, nullable=False) - - def __init__(self, physical_network, vlan_id): - self.physical_network = physical_network - self.vlan_id = vlan_id - self.allocated = False - - -class NetworkBinding(model_base.BASEV2): - """Represents binding of virtual network to physical realization.""" - __tablename__ = 'hyperv_network_bindings' - - network_id = Column(String(36), - ForeignKey('networks.id', ondelete="CASCADE"), - primary_key=True) - network_type = Column(String(32), nullable=False) - physical_network = Column(String(64)) - segmentation_id = Column(Integer) - - def __init__(self, network_id, network_type, physical_network, - segmentation_id): - self.network_id = network_id - self.network_type = network_type - self.physical_network = physical_network - self.segmentation_id = segmentation_id diff --git a/neutron/plugins/hyperv/rpc_callbacks.py b/neutron/plugins/hyperv/rpc_callbacks.py deleted file mode 100644 index 874059a58..000000000 --- a/neutron/plugins/hyperv/rpc_callbacks.py +++ /dev/null @@ -1,94 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Cloudbase Solutions SRL -# 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. -# @author: Alessandro Pilotti, Cloudbase Solutions Srl - -from neutron.common import constants as q_const -from neutron.common import rpc_compat -from neutron.db import dhcp_rpc_base -from neutron.db import l3_rpc_base -from neutron.openstack.common import log as logging -from neutron.plugins.hyperv import db as hyperv_db - - -LOG = logging.getLogger(__name__) - - -class HyperVRpcCallbacks( - rpc_compat.RpcCallback, - dhcp_rpc_base.DhcpRpcCallbackMixin, - l3_rpc_base.L3RpcCallbackMixin): - - # Set RPC API version to 1.0 by default. - RPC_API_VERSION = '1.1' - - def __init__(self, notifier): - super(HyperVRpcCallbacks, self).__init__() - self.notifier = notifier - self._db = hyperv_db.HyperVPluginDB() - - def get_device_details(self, rpc_context, **kwargs): - """Agent requests device details.""" - agent_id = kwargs.get('agent_id') - device = kwargs.get('device') - LOG.debug(_("Device %(device)s details requested from %(agent_id)s"), - {'device': device, 'agent_id': agent_id}) - port = self._db.get_port(device) - if port: - binding = self._db.get_network_binding(None, port['network_id']) - entry = {'device': device, - 'network_id': port['network_id'], - 'port_id': port['id'], - 'admin_state_up': port['admin_state_up'], - 'network_type': binding.network_type, - 'segmentation_id': binding.segmentation_id, - 'physical_network': binding.physical_network} - # Set the port status to UP - self._db.set_port_status(port['id'], q_const.PORT_STATUS_ACTIVE) - else: - entry = {'device': device} - LOG.debug(_("%s can not be found in database"), device) - return entry - - def update_device_down(self, rpc_context, **kwargs): - """Device no longer exists on agent.""" - # TODO(garyk) - live migration and port status - agent_id = kwargs.get('agent_id') - device = kwargs.get('device') - LOG.debug(_("Device %(device)s no longer exists on %(agent_id)s"), - {'device': device, 'agent_id': agent_id}) - port = self._db.get_port(device) - if port: - entry = {'device': device, - 'exists': True} - # Set port status to DOWN - self._db.set_port_status(port['id'], q_const.PORT_STATUS_DOWN) - else: - entry = {'device': device, - 'exists': False} - LOG.debug(_("%s can not be found in database"), device) - return entry - - def tunnel_sync(self, rpc_context, **kwargs): - """Tunnel sync. - - Dummy function for ovs agent running on Linux to - work with Hyper-V plugin and agent. - """ - entry = dict() - entry['tunnels'] = {} - # Return the list of tunnels IP's to the agent - return entry diff --git a/neutron/plugins/ibm/README b/neutron/plugins/ibm/README deleted file mode 100644 index 732fd7776..000000000 --- a/neutron/plugins/ibm/README +++ /dev/null @@ -1,6 +0,0 @@ -IBM SDN-VE Neutron Plugin - -This plugin implements Neutron v2 APIs. - -For more details on how to use it please refer to the following page: -http://wiki.openstack.org/wiki/IBM-Neutron diff --git a/neutron/plugins/ibm/__init__.py b/neutron/plugins/ibm/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/ibm/agent/__init__.py b/neutron/plugins/ibm/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/ibm/agent/sdnve_neutron_agent.py b/neutron/plugins/ibm/agent/sdnve_neutron_agent.py deleted file mode 100644 index e1c8d3ed7..000000000 --- a/neutron/plugins/ibm/agent/sdnve_neutron_agent.py +++ /dev/null @@ -1,270 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. -# -# @author: Mohammad Banikazemi, IBM Corp. - - -import socket -import sys -import time - -import eventlet -eventlet.monkey_patch() - -from oslo.config import cfg - -from neutron.agent.linux import ip_lib -from neutron.agent.linux import ovs_lib -from neutron.agent import rpc as agent_rpc -from neutron.common import config as common_config -from neutron.common import constants as n_const -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.common import utils as n_utils -from neutron import context -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall -from neutron.plugins.ibm.common import config # noqa -from neutron.plugins.ibm.common import constants - - -LOG = logging.getLogger(__name__) - - -class SdnvePluginApi(agent_rpc.PluginApi): - - def sdnve_info(self, context, info): - return self.call(context, - self.make_msg('sdnve_info', info=info), - topic=self.topic) - - -class SdnveNeutronAgent(rpc_compat.RpcCallback): - - RPC_API_VERSION = '1.1' - - def __init__(self, integ_br, interface_mappings, - info, root_helper, polling_interval, - controller_ip, reset_br, out_of_band): - '''The agent initialization. - - Sets the following parameters and sets up the integration - bridge and physical interfaces if need be. - :param integ_br: name of the integration bridge. - :param interface_mappings: interfaces to physical networks. - :param info: local IP address of this hypervisor. - :param root_helper: utility to use when running shell cmds. - :param polling_interval: interval (secs) to poll DB. - :param controller_ip: Ip address of SDN-VE controller. - ''' - - super(SdnveNeutronAgent, self).__init__() - self.root_helper = root_helper - self.int_bridge_name = integ_br - self.controller_ip = controller_ip - self.interface_mappings = interface_mappings - self.polling_interval = polling_interval - self.info = info - self.reset_br = reset_br - self.out_of_band = out_of_band - - self.agent_state = { - 'binary': 'neutron-sdnve-agent', - 'host': cfg.CONF.host, - 'topic': n_const.L2_AGENT_TOPIC, - 'configurations': {'interface_mappings': interface_mappings, - 'reset_br': self.reset_br, - 'out_of_band': self.out_of_band, - 'controller_ip': self.controller_ip}, - 'agent_type': n_const.AGENT_TYPE_SDNVE, - 'start_flag': True} - - if self.int_bridge_name: - self.int_br = self.setup_integration_br(integ_br, reset_br, - out_of_band, - self.controller_ip) - self.setup_physical_interfaces(self.interface_mappings) - else: - self.int_br = None - - self.setup_rpc() - - def _report_state(self): - try: - self.state_rpc.report_state(self.context, - self.agent_state) - self.agent_state.pop('start_flag', None) - except Exception: - LOG.exception(_("Failed reporting state!")) - - def setup_rpc(self): - if self.int_br: - mac = self.int_br.get_local_port_mac() - self.agent_id = '%s%s' % ('sdnve', (mac.replace(":", ""))) - else: - nameaddr = socket.gethostbyname(socket.gethostname()) - self.agent_id = '%s%s' % ('sdnve_', (nameaddr.replace(".", "_"))) - - self.topic = topics.AGENT - self.plugin_rpc = SdnvePluginApi(topics.PLUGIN) - self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) - - self.context = context.get_admin_context_without_session() - self.endpoints = [self] - consumers = [[constants.INFO, topics.UPDATE]] - - self.connection = agent_rpc.create_consumers(self.endpoints, - self.topic, - consumers) - if self.polling_interval: - heartbeat = loopingcall.FixedIntervalLoopingCall( - self._report_state) - heartbeat.start(interval=self.polling_interval) - - # Plugin calls the agents through the following - def info_update(self, context, **kwargs): - LOG.debug(_("info_update received")) - info = kwargs.get('info', {}) - new_controller = info.get('new_controller') - out_of_band = info.get('out_of_band') - if self.int_br and new_controller: - LOG.debug(_("info_update received. New controller" - "is to be set to: %s"), new_controller) - self.int_br.run_vsctl(["set-controller", - self.int_bridge_name, - "tcp:" + new_controller]) - if out_of_band: - LOG.debug(_("info_update received. New controller" - "is set to be out of band")) - self.int_br.set_db_attribute("controller", - self.int_bridge_name, - "connection-mode", - "out-of-band") - - def setup_integration_br(self, bridge_name, reset_br, out_of_band, - controller_ip=None): - '''Sets up the integration bridge. - - Create the bridge and remove all existing flows if reset_br is True. - Otherwise, creates the bridge if not already existing. - :param bridge_name: the name of the integration bridge. - :param reset_br: A boolean to rest the bridge if True. - :param out_of_band: A boolean indicating controller is out of band. - :param controller_ip: IP address to use as the bridge controller. - :returns: the integration bridge - ''' - - int_br = ovs_lib.OVSBridge(bridge_name, self.root_helper) - if reset_br: - int_br.reset_bridge() - int_br.remove_all_flows() - else: - int_br.create() - - # set the controller - if controller_ip: - int_br.run_vsctl( - ["set-controller", bridge_name, "tcp:" + controller_ip]) - if out_of_band: - int_br.set_db_attribute("controller", bridge_name, - "connection-mode", "out-of-band") - - return int_br - - def setup_physical_interfaces(self, interface_mappings): - '''Sets up the physical network interfaces. - - Link physical interfaces to the integration bridge. - :param interface_mappings: map physical net names to interface names. - ''' - - for physical_network, interface in interface_mappings.iteritems(): - LOG.info(_("Mapping physical network %(physical_network)s to " - "interface %(interface)s"), - {'physical_network': physical_network, - 'interface': interface}) - # Connect the physical interface to the bridge - if not ip_lib.device_exists(interface, self.root_helper): - LOG.error(_("Interface %(interface)s for physical network " - "%(physical_network)s does not exist. Agent " - "terminated!"), - {'physical_network': physical_network, - 'interface': interface}) - raise SystemExit(1) - self.int_br.add_port(interface) - - def sdnve_info(self): - details = self.plugin_rpc.sdnve_info( - self.context, - {'info': self.info}) - return details - - def rpc_loop(self): - - while True: - start = time.time() - LOG.debug(_("Agent in the rpc loop.")) - - # sleep till end of polling interval - elapsed = (time.time() - start) - if (elapsed < self.polling_interval): - time.sleep(self.polling_interval - elapsed) - else: - LOG.info(_("Loop iteration exceeded interval " - "(%(polling_interval)s vs. %(elapsed)s)!"), - {'polling_interval': self.polling_interval, - 'elapsed': elapsed}) - - def daemon_loop(self): - self.rpc_loop() - - -def create_agent_config_map(config): - - interface_mappings = n_utils.parse_mappings( - config.SDNVE.interface_mappings) - - controller_ips = config.SDNVE.controller_ips - LOG.info(_("Controller IPs: %s"), controller_ips) - controller_ip = controller_ips[0] - - return { - 'integ_br': config.SDNVE.integration_bridge, - 'interface_mappings': interface_mappings, - 'controller_ip': controller_ip, - 'info': config.SDNVE.info, - 'root_helper': config.SDNVE_AGENT.root_helper, - 'polling_interval': config.SDNVE_AGENT.polling_interval, - 'reset_br': config.SDNVE.reset_bridge, - 'out_of_band': config.SDNVE.out_of_band} - - -def main(): - cfg.CONF.register_opts(ip_lib.OPTS) - common_config.init(sys.argv[1:]) - common_config.setup_logging(cfg.CONF) - - try: - agent_config = create_agent_config_map(cfg.CONF) - except ValueError as e: - LOG.exception(_("%s Agent terminated!"), e) - raise SystemExit(1) - - plugin = SdnveNeutronAgent(**agent_config) - - # Start everything. - LOG.info(_("Agent initialized successfully, now running... ")) - plugin.daemon_loop() diff --git a/neutron/plugins/ibm/common/__init__.py b/neutron/plugins/ibm/common/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/ibm/common/config.py b/neutron/plugins/ibm/common/config.py deleted file mode 100644 index 68e2dbd42..000000000 --- a/neutron/plugins/ibm/common/config.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. -# -# @author: Mohammad Banikazemi, IBM Corp. - - -from oslo.config import cfg - - -DEFAULT_INTERFACE_MAPPINGS = [] -DEFAULT_CONTROLLER_IPS = ['127.0.0.1'] - -sdnve_opts = [ - cfg.BoolOpt('use_fake_controller', default=False, - help=_("If set to True uses a fake controller.")), - cfg.StrOpt('base_url', default='/one/nb/v2/', - help=_("Base URL for SDN-VE controller REST API")), - cfg.ListOpt('controller_ips', default=DEFAULT_CONTROLLER_IPS, - help=_("List of IP addresses of SDN-VE controller(s)")), - cfg.StrOpt('info', default='sdnve_info_string', - help=_("SDN-VE RPC subject")), - cfg.StrOpt('port', default='8443', - help=_("SDN-VE controller port number")), - cfg.StrOpt('format', default='json', - help=_("SDN-VE request/response format")), - cfg.StrOpt('userid', default='admin', - help=_("SDN-VE administrator user id")), - cfg.StrOpt('password', default='admin', secret=True, - help=_("SDN-VE administrator password")), - cfg.StrOpt('integration_bridge', - help=_("Integration bridge to use")), - cfg.BoolOpt('reset_bridge', default=True, - help=_("Reset the integration bridge before use")), - cfg.BoolOpt('out_of_band', default=True, - help=_("Indicating if controller is out of band or not")), - cfg.ListOpt('interface_mappings', - default=DEFAULT_INTERFACE_MAPPINGS, - help=_("List of :")), - cfg.StrOpt('default_tenant_type', default='OVERLAY', - help=_("Tenant type: OVERLAY (default) or OF")), - cfg.StrOpt('overlay_signature', default='SDNVE-OVERLAY', - help=_("The string in tenant description that indicates " - "the tenant is a OVERLAY tenant")), - cfg.StrOpt('of_signature', default='SDNVE-OF', - help=_("The string in tenant description that indicates " - "the tenant is a OF tenant")), -] - -sdnve_agent_opts = [ - cfg.IntOpt('polling_interval', default=2, - help=_("Agent polling interval if necessary")), - cfg.StrOpt('root_helper', default='sudo', - help=_("Using root helper")), - cfg.BoolOpt('rpc', default=True, - help=_("Whether using rpc")), - -] - - -cfg.CONF.register_opts(sdnve_opts, "SDNVE") -cfg.CONF.register_opts(sdnve_agent_opts, "SDNVE_AGENT") diff --git a/neutron/plugins/ibm/common/constants.py b/neutron/plugins/ibm/common/constants.py deleted file mode 100644 index 3acf9baff..000000000 --- a/neutron/plugins/ibm/common/constants.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. -# -# @author: Mohammad Banikazemi, IBM Corp. - - -import httplib - -# Topic for info notifications between the plugin and agent -INFO = 'info' - -TENANT_TYPE_OF = 'OF' -TENANT_TYPE_OVERLAY = 'OVERLAY' - -HTTP_ACCEPTABLE = [httplib.OK, - httplib.CREATED, - httplib.ACCEPTED, - httplib.NO_CONTENT - ] diff --git a/neutron/plugins/ibm/common/exceptions.py b/neutron/plugins/ibm/common/exceptions.py deleted file mode 100644 index d2e5e7ed8..000000000 --- a/neutron/plugins/ibm/common/exceptions.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. -# -# @author: Mohammad Banikazemi, IBM Corp. - -from neutron.common import exceptions - - -class SdnveException(exceptions.NeutronException): - message = _("An unexpected error occurred in the SDN-VE Plugin. " - "Here is the error message: %(msg)s") - - -class BadInputException(exceptions.BadRequest): - message = _("The input does not contain nececessary info: %(msg)s") diff --git a/neutron/plugins/ibm/sdnve_api.py b/neutron/plugins/ibm/sdnve_api.py deleted file mode 100644 index 50e689c1c..000000000 --- a/neutron/plugins/ibm/sdnve_api.py +++ /dev/null @@ -1,388 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. -# -# @author: Mohammad Banikazemi, IBM Corp. - - -import httplib -import urllib - -import httplib2 -from keystoneclient.v2_0 import client as keyclient -from oslo.config import cfg - -from neutron.api.v2 import attributes -from neutron.openstack.common import log as logging -from neutron.plugins.ibm.common import config # noqa -from neutron.plugins.ibm.common import constants -from neutron import wsgi - -LOG = logging.getLogger(__name__) - -SDNVE_VERSION = '2.0' -SDNVE_ACTION_PREFIX = '/sdnve' -SDNVE_RETRIES = 0 -SDNVE_RETRIY_INTERVAL = 1 -SDNVE_TENANT_TYPE_OVERLAY = u'DOVE' -SDNVE_URL = 'https://%s:%s%s' - - -class RequestHandler(object): - '''Handles processing requests to and responses from controller.''' - - def __init__(self, controller_ips=None, port=None, ssl=None, - base_url=None, userid=None, password=None, - timeout=10, formats=None): - '''Initializes the RequestHandler for communication with controller - - Following keyword arguments are used; if not specified, default - values are used. - :param port: Username for authentication. - :param timeout: Time out for http requests. - :param userid: User id for accessing controller. - :param password: Password for accessing the controller. - :param base_url: The base url for the controller. - :param controller_ips: List of controller IP addresses. - :param formats: Supported formats. - ''' - self.port = port or cfg.CONF.SDNVE.port - self.timeout = timeout - self._s_meta = None - self.connection = None - self.httpclient = httplib2.Http( - disable_ssl_certificate_validation=True) - self.cookie = None - - userid = userid or cfg.CONF.SDNVE.userid - password = password or cfg.CONF.SDNVE.password - if (userid and password): - self.httpclient.add_credentials(userid, password) - - self.base_url = base_url or cfg.CONF.SDNVE.base_url - self.controller_ips = controller_ips or cfg.CONF.SDNVE.controller_ips - - LOG.info(_("The IP addr of available SDN-VE controllers: %s"), - self.controller_ips) - self.controller_ip = self.controller_ips[0] - LOG.info(_("The SDN-VE controller IP address: %s"), - self.controller_ip) - - self.new_controller = False - self.format = formats or cfg.CONF.SDNVE.format - - self.version = SDNVE_VERSION - self.action_prefix = SDNVE_ACTION_PREFIX - self.retries = SDNVE_RETRIES - self.retry_interval = SDNVE_RETRIY_INTERVAL - - def serialize(self, data): - '''Serializes a dictionary with a single key.''' - - if isinstance(data, dict): - return wsgi.Serializer().serialize(data, self.content_type()) - elif data: - raise TypeError(_("unable to serialize object type: '%s'") % - type(data)) - - def deserialize(self, data, status_code): - '''Deserializes an xml or json string into a dictionary.''' - - # NOTE(mb): Temporary fix for backend controller requirement - data = data.replace("router_external", "router:external") - - if status_code == httplib.NO_CONTENT: - return data - try: - deserialized_data = wsgi.Serializer( - metadata=self._s_meta).deserialize(data, self.content_type()) - deserialized_data = deserialized_data['body'] - except Exception: - deserialized_data = data - - return deserialized_data - - def content_type(self, format=None): - '''Returns the mime-type for either 'xml' or 'json'.''' - - return 'application/%s' % (format or self.format) - - def delete(self, url, body=None, headers=None, params=None): - return self.do_request("DELETE", url, body=body, - headers=headers, params=params) - - def get(self, url, body=None, headers=None, params=None): - return self.do_request("GET", url, body=body, - headers=headers, params=params) - - def post(self, url, body=None, headers=None, params=None): - return self.do_request("POST", url, body=body, - headers=headers, params=params) - - def put(self, url, body=None, headers=None, params=None): - return self.do_request("PUT", url, body=body, - headers=headers, params=params) - - def do_request(self, method, url, body=None, headers=None, - params=None, connection_type=None): - - status_code = -1 - replybody_deserialized = '' - - if body: - body = self.serialize(body) - - self.headers = headers or {'Content-Type': self.content_type()} - if self.cookie: - self.headers['cookie'] = self.cookie - - if self.controller_ip != self.controller_ips[0]: - controllers = [self.controller_ip] - else: - controllers = [] - controllers.extend(self.controller_ips) - - for controller_ip in controllers: - serverurl = SDNVE_URL % (controller_ip, self.port, self.base_url) - myurl = serverurl + url - if params and isinstance(params, dict): - myurl += '?' + urllib.urlencode(params, doseq=1) - - try: - LOG.debug(_("Sending request to SDN-VE. url: " - "%(myurl)s method: %(method)s body: " - "%(body)s header: %(header)s "), - {'myurl': myurl, 'method': method, - 'body': body, 'header': self.headers}) - resp, replybody = self.httpclient.request( - myurl, method=method, body=body, headers=self.headers) - LOG.debug(("Response recd from SDN-VE. resp: %(resp)s" - "body: %(body)s"), - {'resp': resp.status, 'body': replybody}) - status_code = resp.status - - except Exception as e: - LOG.error(_("Error: Could not reach server: %(url)s " - "Exception: %(excp)s."), - {'url': myurl, 'excp': e}) - self.cookie = None - continue - - if status_code not in constants.HTTP_ACCEPTABLE: - LOG.debug(_("Error message: %(reply)s -- Status: %(status)s"), - {'reply': replybody, 'status': status_code}) - else: - LOG.debug(_("Received response status: %s"), status_code) - - if resp.get('set-cookie'): - self.cookie = resp['set-cookie'] - replybody_deserialized = self.deserialize( - replybody, - status_code) - LOG.debug(_("Deserialized body: %s"), replybody_deserialized) - if controller_ip != self.controller_ip: - # bcast the change of controller - self.new_controller = True - self.controller_ip = controller_ip - - return (status_code, replybody_deserialized) - - return (httplib.REQUEST_TIMEOUT, 'Could not reach server(s)') - - -class Client(RequestHandler): - '''Client for SDNVE controller.''' - - def __init__(self): - '''Initialize a new SDNVE client.''' - super(Client, self).__init__() - - self.keystoneclient = KeystoneClient() - - resource_path = { - 'network': "ln/networks/", - 'subnet': "ln/subnets/", - 'port': "ln/ports/", - 'tenant': "ln/tenants/", - 'router': "ln/routers/", - 'floatingip': "ln/floatingips/", - } - - def process_request(self, body): - '''Processes requests according to requirements of controller.''' - if self.format == 'json': - body = dict( - (k.replace(':', '_'), v) for k, v in body.items() - if attributes.is_attr_set(v)) - return body - - def sdnve_list(self, resource, **params): - '''Fetches a list of resources.''' - - res = self.resource_path.get(resource, None) - if not res: - LOG.info(_("Bad resource for forming a list request")) - return 0, '' - - return self.get(res, params=params) - - def sdnve_show(self, resource, specific, **params): - '''Fetches information of a certain resource.''' - - res = self.resource_path.get(resource, None) - if not res: - LOG.info(_("Bad resource for forming a show request")) - return 0, '' - - return self.get(res + specific, params=params) - - def sdnve_create(self, resource, body): - '''Creates a new resource.''' - - res = self.resource_path.get(resource, None) - if not res: - LOG.info(_("Bad resource for forming a create request")) - return 0, '' - - body = self.process_request(body) - status, data = self.post(res, body=body) - return (status, data) - - def sdnve_update(self, resource, specific, body=None): - '''Updates a resource.''' - - res = self.resource_path.get(resource, None) - if not res: - LOG.info(_("Bad resource for forming a update request")) - return 0, '' - - body = self.process_request(body) - return self.put(res + specific, body=body) - - def sdnve_delete(self, resource, specific): - '''Deletes the specified resource.''' - - res = self.resource_path.get(resource, None) - if not res: - LOG.info(_("Bad resource for forming a delete request")) - return 0, '' - - return self.delete(res + specific) - - def _tenant_id_conversion(self, osid): - return osid - - def sdnve_get_tenant_byid(self, os_tenant_id): - sdnve_tenant_id = self._tenant_id_conversion(os_tenant_id) - resp, content = self.sdnve_show('tenant', sdnve_tenant_id) - if resp in constants.HTTP_ACCEPTABLE: - tenant_id = content.get('id') - tenant_type = content.get('network_type') - if tenant_type == SDNVE_TENANT_TYPE_OVERLAY: - tenant_type = constants.TENANT_TYPE_OVERLAY - return tenant_id, tenant_type - return None, None - - def sdnve_check_and_create_tenant(self, os_tenant_id, network_type=None): - - if not os_tenant_id: - return - tenant_id, tenant_type = self.sdnve_get_tenant_byid(os_tenant_id) - if tenant_id: - if not network_type: - return tenant_id - if tenant_type != network_type: - LOG.info(_("Non matching tenant and network types: " - "%(ttype)s %(ntype)s"), - {'ttype': tenant_type, 'ntype': network_type}) - return - return tenant_id - - # Have to create a new tenant - sdnve_tenant_id = self._tenant_id_conversion(os_tenant_id) - if not network_type: - network_type = self.keystoneclient.get_tenant_type(os_tenant_id) - if network_type == constants.TENANT_TYPE_OVERLAY: - network_type = SDNVE_TENANT_TYPE_OVERLAY - - pinn_desc = ("Created by SDN-VE Neutron Plugin, OS project name = " + - self.keystoneclient.get_tenant_name(os_tenant_id)) - - res, content = self.sdnve_create('tenant', - {'id': sdnve_tenant_id, - 'name': os_tenant_id, - 'network_type': network_type, - 'description': pinn_desc}) - if res not in constants.HTTP_ACCEPTABLE: - return - - return sdnve_tenant_id - - def sdnve_get_controller(self): - if self.new_controller: - self.new_controller = False - return self.controller_ip - - -class KeystoneClient(object): - - def __init__(self, username=None, tenant_name=None, password=None, - auth_url=None): - - keystone_conf = cfg.CONF.keystone_authtoken - keystone_auth_url = ('%s://%s:%s/v2.0/' % - (keystone_conf.auth_protocol, - keystone_conf.auth_host, - keystone_conf.auth_port)) - - username = username or keystone_conf.admin_user - tenant_name = tenant_name or keystone_conf.admin_tenant_name - password = password or keystone_conf.admin_password - auth_url = auth_url or keystone_auth_url - - self.overlay_signature = cfg.CONF.SDNVE.overlay_signature - self.of_signature = cfg.CONF.SDNVE.of_signature - self.default_tenant_type = cfg.CONF.SDNVE.default_tenant_type - - self.client = keyclient.Client(username=username, - password=password, - tenant_name=tenant_name, - auth_url=auth_url) - - def get_tenant_byid(self, id): - - try: - return self.client.tenants.get(id) - except Exception: - LOG.exception(_("Did not find tenant: %r"), id) - - def get_tenant_type(self, id): - - tenant = self.get_tenant_byid(id) - if tenant: - description = tenant.description - if description: - if (description.find(self.overlay_signature) >= 0): - return constants.TENANT_TYPE_OVERLAY - if (description.find(self.of_signature) >= 0): - return constants.TENANT_TYPE_OF - return self.default_tenant_type - - def get_tenant_name(self, id): - - tenant = self.get_tenant_byid(id) - if tenant: - return tenant.name - return 'not found' diff --git a/neutron/plugins/ibm/sdnve_api_fake.py b/neutron/plugins/ibm/sdnve_api_fake.py deleted file mode 100644 index 74cfc8386..000000000 --- a/neutron/plugins/ibm/sdnve_api_fake.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. -# -# @author: Mohammad Banikazemi, IBM Corp. - -from neutron.openstack.common import log as logging -from neutron.plugins.ibm.common import constants - -LOG = logging.getLogger(__name__) - -HTTP_OK = 200 - - -class FakeClient(): - - '''Fake Client for SDNVE controller.''' - - def __init__(self, **kwargs): - LOG.info(_('Fake SDNVE controller initialized')) - - def sdnve_list(self, resource, **_params): - LOG.info(_('Fake SDNVE controller: list')) - return (HTTP_OK, None) - - def sdnve_show(self, resource, specific, **_params): - LOG.info(_('Fake SDNVE controller: show')) - return (HTTP_OK, None) - - def sdnve_create(self, resource, body): - LOG.info(_('Fake SDNVE controller: create')) - return (HTTP_OK, None) - - def sdnve_update(self, resource, specific, body=None): - LOG.info(_('Fake SDNVE controller: update')) - return (HTTP_OK, None) - - def sdnve_delete(self, resource, specific): - LOG.info(_('Fake SDNVE controller: delete')) - return (HTTP_OK, None) - - def sdnve_get_tenant_byid(self, id): - LOG.info(_('Fake SDNVE controller: get tenant by id')) - return id, constants.TENANT_TYPE_OF - - def sdnve_check_and_create_tenant(self, id, network_type=None): - LOG.info(_('Fake SDNVE controller: check and create tenant')) - return id - - def sdnve_get_controller(self): - LOG.info(_('Fake SDNVE controller: get controller')) - return None diff --git a/neutron/plugins/ibm/sdnve_neutron_plugin.py b/neutron/plugins/ibm/sdnve_neutron_plugin.py deleted file mode 100644 index cf127f001..000000000 --- a/neutron/plugins/ibm/sdnve_neutron_plugin.py +++ /dev/null @@ -1,666 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. -# -# @author: Mohammad Banikazemi, IBM Corp. - - -import functools - -from oslo.config import cfg - -from neutron.common import constants as n_const -from neutron.common import exceptions as n_exc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.db import agents_db -from neutron.db import db_base_plugin_v2 -from neutron.db import external_net_db -from neutron.db import l3_gwmode_db -from neutron.db import portbindings_db -from neutron.db import quota_db # noqa -from neutron.extensions import portbindings -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.ibm.common import config # noqa -from neutron.plugins.ibm.common import constants -from neutron.plugins.ibm.common import exceptions as sdnve_exc -from neutron.plugins.ibm import sdnve_api as sdnve -from neutron.plugins.ibm import sdnve_api_fake as sdnve_fake - -LOG = logging.getLogger(__name__) - - -class SdnveRpcCallbacks(): - - def __init__(self, notifier): - self.notifier = notifier # used to notify the agent - - def sdnve_info(self, rpc_context, **kwargs): - '''Update new information.''' - info = kwargs.get('info') - # Notify all other listening agents - self.notifier.info_update(rpc_context, info) - return info - - -class AgentNotifierApi(rpc_compat.RpcProxy): - '''Agent side of the SDN-VE rpc API.''' - - BASE_RPC_API_VERSION = '1.0' - - def __init__(self, topic): - super(AgentNotifierApi, self).__init__( - topic=topic, default_version=self.BASE_RPC_API_VERSION) - - self.topic_info_update = topics.get_topic_name(topic, - constants.INFO, - topics.UPDATE) - - def info_update(self, context, info): - self.fanout_cast(context, - self.make_msg('info_update', - info=info), - topic=self.topic_info_update) - - -def _ha(func): - '''Supports the high availability feature of the controller.''' - - @functools.wraps(func) - def hawrapper(self, *args, **kwargs): - '''This wrapper sets the new controller if necessary - - When a controller is detected to be not responding, and a - new controller is chosen to be used in its place, this decorator - makes sure the existing integration bridges are set to point - to the new controller by calling the set_controller method. - ''' - ret_func = func(self, *args, **kwargs) - self.set_controller(args[0]) - return ret_func - return hawrapper - - -class SdnvePluginV2(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - portbindings_db.PortBindingMixin, - l3_gwmode_db.L3_NAT_db_mixin, - agents_db.AgentDbMixin, - ): - - ''' - Implement the Neutron abstractions using SDN-VE SDN Controller. - ''' - - __native_bulk_support = False - __native_pagination_support = False - __native_sorting_support = False - - supported_extension_aliases = ["binding", "router", "external-net", - "agent", "quotas"] - - def __init__(self, configfile=None): - self.base_binding_dict = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, - portbindings.VIF_DETAILS: {portbindings.CAP_PORT_FILTER: False}} - - super(SdnvePluginV2, self).__init__() - self.setup_rpc() - self.sdnve_controller_select() - if self.fake_controller: - self.sdnve_client = sdnve_fake.FakeClient() - else: - self.sdnve_client = sdnve.Client() - - def sdnve_controller_select(self): - self.fake_controller = cfg.CONF.SDNVE.use_fake_controller - - def setup_rpc(self): - # RPC support - self.topic = topics.PLUGIN - self.conn = rpc_compat.create_connection(new=True) - self.notifier = AgentNotifierApi(topics.AGENT) - self.endpoints = [SdnveRpcCallbacks(self.notifier), - agents_db.AgentExtRpcCallback()] - self.conn.create_consumer(self.topic, self.endpoints, - fanout=False) - # Consume from all consumers in threads - self.conn.consume_in_threads() - - def _update_base_binding_dict(self, tenant_type): - if tenant_type == constants.TENANT_TYPE_OVERLAY: - self.base_binding_dict[ - portbindings.VIF_TYPE] = portbindings.VIF_TYPE_BRIDGE - if tenant_type == constants.TENANT_TYPE_OF: - self.base_binding_dict[ - portbindings.VIF_TYPE] = portbindings.VIF_TYPE_OVS - - def set_controller(self, context): - LOG.info(_("Set a new controller if needed.")) - new_controller = self.sdnve_client.sdnve_get_controller() - if new_controller: - self.notifier.info_update( - context, - {'new_controller': new_controller}) - LOG.info(_("Set the controller to a new controller: %s"), - new_controller) - - def _process_request(self, request, current): - new_request = dict( - (k, v) for k, v in request.items() - if v != current.get(k)) - - msg = _("Original SDN-VE HTTP request: %(orig)s; New request: %(new)s") - LOG.debug(msg, {'orig': request, 'new': new_request}) - return new_request - - # - # Network - # - - @_ha - def create_network(self, context, network): - LOG.debug(_("Create network in progress: %r"), network) - session = context.session - - tenant_id = self._get_tenant_id_for_create(context, network['network']) - # Create a new SDN-VE tenant if need be - sdnve_tenant = self.sdnve_client.sdnve_check_and_create_tenant( - tenant_id) - if sdnve_tenant is None: - raise sdnve_exc.SdnveException( - msg=_('Create net failed: no SDN-VE tenant.')) - - with session.begin(subtransactions=True): - net = super(SdnvePluginV2, self).create_network(context, network) - self._process_l3_create(context, net, network['network']) - - # Create SDN-VE network - (res, data) = self.sdnve_client.sdnve_create('network', net) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).delete_network(context, net['id']) - raise sdnve_exc.SdnveException( - msg=(_('Create net failed in SDN-VE: %s') % res)) - - LOG.debug(_("Created network: %s"), net['id']) - return net - - @_ha - def update_network(self, context, id, network): - LOG.debug(_("Update network in progress: %r"), network) - session = context.session - - processed_request = {} - with session.begin(subtransactions=True): - original_network = super(SdnvePluginV2, self).get_network( - context, id) - processed_request['network'] = self._process_request( - network['network'], original_network) - net = super(SdnvePluginV2, self).update_network( - context, id, network) - self._process_l3_update(context, net, network['network']) - - if processed_request['network']: - (res, data) = self.sdnve_client.sdnve_update( - 'network', id, processed_request['network']) - if res not in constants.HTTP_ACCEPTABLE: - net = super(SdnvePluginV2, self).update_network( - context, id, {'network': original_network}) - raise sdnve_exc.SdnveException( - msg=(_('Update net failed in SDN-VE: %s') % res)) - - return net - - @_ha - def delete_network(self, context, id): - LOG.debug(_("Delete network in progress: %s"), id) - session = context.session - - with session.begin(subtransactions=True): - self._process_l3_delete(context, id) - super(SdnvePluginV2, self).delete_network(context, id) - - (res, data) = self.sdnve_client.sdnve_delete('network', id) - if res not in constants.HTTP_ACCEPTABLE: - LOG.error( - _("Delete net failed after deleting the network in DB: %s"), - res) - - @_ha - def get_network(self, context, id, fields=None): - LOG.debug(_("Get network in progress: %s"), id) - return super(SdnvePluginV2, self).get_network(context, id, fields) - - @_ha - def get_networks(self, context, filters=None, fields=None, sorts=None, - limit=None, marker=None, page_reverse=False): - LOG.debug(_("Get networks in progress")) - return super(SdnvePluginV2, self).get_networks( - context, filters, fields, sorts, limit, marker, page_reverse) - - # - # Port - # - - @_ha - def create_port(self, context, port): - LOG.debug(_("Create port in progress: %r"), port) - session = context.session - - # Set port status as 'ACTIVE' to avoid needing the agent - port['port']['status'] = n_const.PORT_STATUS_ACTIVE - port_data = port['port'] - - with session.begin(subtransactions=True): - port = super(SdnvePluginV2, self).create_port(context, port) - if 'id' not in port: - return port - # If the tenant_id is set to '' by create_port, add the id to - # the request being sent to the controller as the controller - # requires a tenant id - tenant_id = port.get('tenant_id') - if not tenant_id: - LOG.debug(_("Create port does not have tenant id info")) - original_network = super(SdnvePluginV2, self).get_network( - context, port['network_id']) - original_tenant_id = original_network['tenant_id'] - port['tenant_id'] = original_tenant_id - LOG.debug( - _("Create port does not have tenant id info; " - "obtained is: %s"), - port['tenant_id']) - - os_tenant_id = tenant_id - id_na, tenant_type = self.sdnve_client.sdnve_get_tenant_byid( - os_tenant_id) - self._update_base_binding_dict(tenant_type) - self._process_portbindings_create_and_update(context, - port_data, port) - - # NOTE(mb): Remove this block when controller is updated - # Remove the information that the controller does not accept - sdnve_port = port.copy() - sdnve_port.pop('device_id', None) - sdnve_port.pop('device_owner', None) - - (res, data) = self.sdnve_client.sdnve_create('port', sdnve_port) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).delete_port(context, port['id']) - raise sdnve_exc.SdnveException( - msg=(_('Create port failed in SDN-VE: %s') % res)) - - LOG.debug(_("Created port: %s"), port.get('id', 'id not found')) - return port - - @_ha - def update_port(self, context, id, port): - LOG.debug(_("Update port in progress: %r"), port) - session = context.session - - processed_request = {} - with session.begin(subtransactions=True): - original_port = super(SdnvePluginV2, self).get_port( - context, id) - processed_request['port'] = self._process_request( - port['port'], original_port) - updated_port = super(SdnvePluginV2, self).update_port( - context, id, port) - - os_tenant_id = updated_port['tenant_id'] - id_na, tenant_type = self.sdnve_client.sdnve_get_tenant_byid( - os_tenant_id) - self._update_base_binding_dict(tenant_type) - self._process_portbindings_create_and_update(context, - port['port'], - updated_port) - - if processed_request['port']: - (res, data) = self.sdnve_client.sdnve_update( - 'port', id, processed_request['port']) - if res not in constants.HTTP_ACCEPTABLE: - updated_port = super(SdnvePluginV2, self).update_port( - context, id, {'port': original_port}) - raise sdnve_exc.SdnveException( - msg=(_('Update port failed in SDN-VE: %s') % res)) - - return updated_port - - @_ha - def delete_port(self, context, id, l3_port_check=True): - LOG.debug(_("Delete port in progress: %s"), id) - - # if needed, check to see if this is a port owned by - # an l3-router. If so, we should prevent deletion. - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - self.disassociate_floatingips(context, id) - - super(SdnvePluginV2, self).delete_port(context, id) - - (res, data) = self.sdnve_client.sdnve_delete('port', id) - if res not in constants.HTTP_ACCEPTABLE: - LOG.error( - _("Delete port operation failed in SDN-VE " - "after deleting the port from DB: %s"), res) - - # - # Subnet - # - - @_ha - def create_subnet(self, context, subnet): - LOG.debug(_("Create subnet in progress: %r"), subnet) - new_subnet = super(SdnvePluginV2, self).create_subnet(context, subnet) - - # Note(mb): Use of null string currently required by controller - sdnve_subnet = new_subnet.copy() - if subnet.get('gateway_ip') is None: - sdnve_subnet['gateway_ip'] = 'null' - (res, data) = self.sdnve_client.sdnve_create('subnet', sdnve_subnet) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).delete_subnet(context, - new_subnet['id']) - raise sdnve_exc.SdnveException( - msg=(_('Create subnet failed in SDN-VE: %s') % res)) - - LOG.debug(_("Subnet created: %s"), new_subnet['id']) - - return new_subnet - - @_ha - def update_subnet(self, context, id, subnet): - LOG.debug(_("Update subnet in progress: %r"), subnet) - session = context.session - - processed_request = {} - with session.begin(subtransactions=True): - original_subnet = super(SdnvePluginV2, self).get_subnet( - context, id) - processed_request['subnet'] = self._process_request( - subnet['subnet'], original_subnet) - updated_subnet = super(SdnvePluginV2, self).update_subnet( - context, id, subnet) - - if processed_request['subnet']: - # Note(mb): Use of string containing null required by controller - if 'gateway_ip' in processed_request['subnet']: - if processed_request['subnet'].get('gateway_ip') is None: - processed_request['subnet']['gateway_ip'] = 'null' - (res, data) = self.sdnve_client.sdnve_update( - 'subnet', id, processed_request['subnet']) - if res not in constants.HTTP_ACCEPTABLE: - for key in subnet['subnet'].keys(): - subnet['subnet'][key] = original_subnet[key] - super(SdnvePluginV2, self).update_subnet( - context, id, subnet) - raise sdnve_exc.SdnveException( - msg=(_('Update subnet failed in SDN-VE: %s') % res)) - - return updated_subnet - - @_ha - def delete_subnet(self, context, id): - LOG.debug(_("Delete subnet in progress: %s"), id) - super(SdnvePluginV2, self).delete_subnet(context, id) - - (res, data) = self.sdnve_client.sdnve_delete('subnet', id) - if res not in constants.HTTP_ACCEPTABLE: - LOG.error(_("Delete subnet operation failed in SDN-VE after " - "deleting the subnet from DB: %s"), res) - - # - # Router - # - - @_ha - def create_router(self, context, router): - LOG.debug(_("Create router in progress: %r"), router) - - if router['router']['admin_state_up'] is False: - LOG.warning(_('Ignoring admin_state_up=False for router=%r. ' - 'Overriding with True'), router) - router['router']['admin_state_up'] = True - - tenant_id = self._get_tenant_id_for_create(context, router['router']) - # Create a new SDN-VE tenant if need be - sdnve_tenant = self.sdnve_client.sdnve_check_and_create_tenant( - tenant_id) - if sdnve_tenant is None: - raise sdnve_exc.SdnveException( - msg=_('Create router failed: no SDN-VE tenant.')) - - new_router = super(SdnvePluginV2, self).create_router(context, router) - # Create SDN-VE router - (res, data) = self.sdnve_client.sdnve_create('router', new_router) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).delete_router(context, new_router['id']) - raise sdnve_exc.SdnveException( - msg=(_('Create router failed in SDN-VE: %s') % res)) - - LOG.debug(_("Router created: %r"), new_router) - return new_router - - @_ha - def update_router(self, context, id, router): - LOG.debug(_("Update router in progress: id=%(id)s " - "router=%(router)r"), - {'id': id, 'router': router}) - session = context.session - - processed_request = {} - if not router['router'].get('admin_state_up', True): - raise n_exc.NotImplementedError(_('admin_state_up=False ' - 'routers are not ' - 'supported.')) - - with session.begin(subtransactions=True): - original_router = super(SdnvePluginV2, self).get_router( - context, id) - processed_request['router'] = self._process_request( - router['router'], original_router) - updated_router = super(SdnvePluginV2, self).update_router( - context, id, router) - - if processed_request['router']: - egw = processed_request['router'].get('external_gateway_info') - # Check for existing empty set (different from None) in request - if egw == {}: - processed_request['router'][ - 'external_gateway_info'] = {'network_id': 'null'} - (res, data) = self.sdnve_client.sdnve_update( - 'router', id, processed_request['router']) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).update_router( - context, id, {'router': original_router}) - raise sdnve_exc.SdnveException( - msg=(_('Update router failed in SDN-VE: %s') % res)) - - return updated_router - - @_ha - def delete_router(self, context, id): - LOG.debug(_("Delete router in progress: %s"), id) - - super(SdnvePluginV2, self).delete_router(context, id) - - (res, data) = self.sdnve_client.sdnve_delete('router', id) - if res not in constants.HTTP_ACCEPTABLE: - LOG.error( - _("Delete router operation failed in SDN-VE after " - "deleting the router in DB: %s"), res) - - @_ha - def add_router_interface(self, context, router_id, interface_info): - LOG.debug(_("Add router interface in progress: " - "router_id=%(router_id)s " - "interface_info=%(interface_info)r"), - {'router_id': router_id, 'interface_info': interface_info}) - - new_interface = super(SdnvePluginV2, self).add_router_interface( - context, router_id, interface_info) - LOG.debug( - _("SdnvePluginV2.add_router_interface called. Port info: %s"), - new_interface) - request_info = interface_info.copy() - request_info['port_id'] = new_interface['port_id'] - # Add the subnet_id to the request sent to the controller - if 'subnet_id' not in interface_info: - request_info['subnet_id'] = new_interface['subnet_id'] - - (res, data) = self.sdnve_client.sdnve_update( - 'router', router_id + '/add_router_interface', request_info) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).remove_router_interface( - context, router_id, interface_info) - raise sdnve_exc.SdnveException( - msg=(_('Update router-add-interface failed in SDN-VE: %s') % - res)) - - LOG.debug(_("Added router interface: %r"), new_interface) - return new_interface - - def _add_router_interface_only(self, context, router_id, interface_info): - LOG.debug(_("Add router interface only called: " - "router_id=%(router_id)s " - "interface_info=%(interface_info)r"), - {'router_id': router_id, 'interface_info': interface_info}) - - port_id = interface_info.get('port_id') - if port_id: - (res, data) = self.sdnve_client.sdnve_update( - 'router', router_id + '/add_router_interface', interface_info) - if res not in constants.HTTP_ACCEPTABLE: - LOG.error(_("SdnvePluginV2._add_router_interface_only: " - "failed to add the interface in the roll back." - " of a remove_router_interface operation")) - - @_ha - def remove_router_interface(self, context, router_id, interface_info): - LOG.debug(_("Remove router interface in progress: " - "router_id=%(router_id)s " - "interface_info=%(interface_info)r"), - {'router_id': router_id, 'interface_info': interface_info}) - - subnet_id = interface_info.get('subnet_id') - port_id = interface_info.get('port_id') - if not subnet_id: - if not port_id: - raise sdnve_exc.BadInputException(msg=_('No port ID')) - myport = super(SdnvePluginV2, self).get_port(context, port_id) - LOG.debug(_("SdnvePluginV2.remove_router_interface port: %s"), - myport) - myfixed_ips = myport.get('fixed_ips') - if not myfixed_ips: - raise sdnve_exc.BadInputException(msg=_('No fixed IP')) - subnet_id = myfixed_ips[0].get('subnet_id') - if subnet_id: - interface_info['subnet_id'] = subnet_id - LOG.debug( - _("SdnvePluginV2.remove_router_interface subnet_id: %s"), - subnet_id) - else: - if not port_id: - # The backend requires port id info in the request - subnet = super(SdnvePluginV2, self).get_subnet(context, - subnet_id) - df = {'device_id': [router_id], - 'device_owner': [n_const.DEVICE_OWNER_ROUTER_INTF], - 'network_id': [subnet['network_id']]} - ports = self.get_ports(context, filters=df) - if ports: - pid = ports[0]['id'] - interface_info['port_id'] = pid - msg = ("SdnvePluginV2.remove_router_interface " - "subnet_id: %(sid)s port_id: %(pid)s") - LOG.debug(msg, {'sid': subnet_id, 'pid': pid}) - - (res, data) = self.sdnve_client.sdnve_update( - 'router', router_id + '/remove_router_interface', interface_info) - - if res not in constants.HTTP_ACCEPTABLE: - raise sdnve_exc.SdnveException( - msg=(_('Update router-remove-interface failed SDN-VE: %s') % - res)) - - session = context.session - with session.begin(subtransactions=True): - try: - info = super(SdnvePluginV2, self).remove_router_interface( - context, router_id, interface_info) - except Exception: - with excutils.save_and_reraise_exception(): - self._add_router_interface_only(context, - router_id, interface_info) - - return info - - # - # Floating Ip - # - - @_ha - def create_floatingip(self, context, floatingip): - LOG.debug(_("Create floatingip in progress: %r"), - floatingip) - new_floatingip = super(SdnvePluginV2, self).create_floatingip( - context, floatingip) - - (res, data) = self.sdnve_client.sdnve_create( - 'floatingip', {'floatingip': new_floatingip}) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).delete_floatingip( - context, new_floatingip['id']) - raise sdnve_exc.SdnveException( - msg=(_('Creating floating ip operation failed ' - 'in SDN-VE controller: %s') % res)) - - LOG.debug(_("Created floatingip : %r"), new_floatingip) - return new_floatingip - - @_ha - def update_floatingip(self, context, id, floatingip): - LOG.debug(_("Update floatingip in progress: %r"), floatingip) - session = context.session - - processed_request = {} - with session.begin(subtransactions=True): - original_floatingip = super( - SdnvePluginV2, self).get_floatingip(context, id) - processed_request['floatingip'] = self._process_request( - floatingip['floatingip'], original_floatingip) - updated_floatingip = super( - SdnvePluginV2, self).update_floatingip(context, id, floatingip) - - if processed_request['floatingip']: - (res, data) = self.sdnve_client.sdnve_update( - 'floatingip', id, - {'floatingip': processed_request['floatingip']}) - if res not in constants.HTTP_ACCEPTABLE: - super(SdnvePluginV2, self).update_floatingip( - context, id, {'floatingip': original_floatingip}) - raise sdnve_exc.SdnveException( - msg=(_('Update floating ip failed in SDN-VE: %s') % res)) - - return updated_floatingip - - @_ha - def delete_floatingip(self, context, id): - LOG.debug(_("Delete floatingip in progress: %s"), id) - super(SdnvePluginV2, self).delete_floatingip(context, id) - - (res, data) = self.sdnve_client.sdnve_delete('floatingip', id) - if res not in constants.HTTP_ACCEPTABLE: - LOG.error(_("Delete floatingip failed in SDN-VE: %s"), res) diff --git a/neutron/plugins/linuxbridge/README b/neutron/plugins/linuxbridge/README deleted file mode 100644 index b7601205f..000000000 --- a/neutron/plugins/linuxbridge/README +++ /dev/null @@ -1,169 +0,0 @@ -# -- Background - -The Neutron Linux Bridge plugin is a plugin that allows you to manage -connectivity between VMs on hosts that are capable of running a Linux Bridge. - -The Neutron Linux Bridge plugin consists of three components: - -1) The plugin itself: The plugin uses a database backend (mysql for - now) to store configuration and mappings that are used by the - agent. The mysql server runs on a central server (often the same - host as nova itself). - -2) The neutron service host which will be running neutron. This can - be run on the server running nova. - -3) An agent which runs on the host and communicates with the host operating - system. The agent gathers the configuration and mappings from - the mysql database running on the neutron host. - -The sections below describe how to configure and run the neutron -service with the Linux Bridge plugin. - -# -- Python library dependencies - - Make sure you have the following package(s) installedi on neutron server - host as well as any hosts which run the agent: - python-configobj - bridge-utils - python-mysqldb - sqlite3 - -# -- Nova configuration (controller node) - -1) Ensure that the neutron network manager is configured in the - nova.conf on the node that will be running nova-network. - -network_manager=nova.network.neutron.manager.NeutronManager - -# -- Nova configuration (compute node(s)) - -1) Configure the vif driver, and libvirt/vif type - -connection_type=libvirt -libvirt_type=qemu -libvirt_vif_type=ethernet -libvirt_vif_driver=nova.virt.libvirt.vif.NeutronLinuxBridgeVIFDriver -linuxnet_interface_driver=nova.network.linux_net.NeutronLinuxBridgeInterfaceDriver - -2) If you want a DHCP server to be run for the VMs to acquire IPs, - add the following flag to your nova.conf file: - -neutron_use_dhcp=true - -(Note: For more details on how to work with Neutron using Nova, i.e. how to create networks and such, - please refer to the top level Neutron README which points to the relevant documentation.) - -# -- Neutron configuration - -Make the Linux Bridge plugin the current neutron plugin - -- edit neutron.conf and change the core_plugin - -core_plugin = neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2 - -# -- Database config. - -(Note: The plugin ships with a default SQLite in-memory database configuration, - and can be used to run tests without performing the suggested DB config below.) - -The Linux Bridge neutron plugin requires access to a mysql database in order -to store configuration and mappings that will be used by the agent. Here is -how to set up the database on the host that you will be running the neutron -service on. - -MySQL should be installed on the host, and all plugins and clients -must be configured with access to the database. - -To prep mysql, run: - -$ mysql -u root -p -e "create database neutron_linux_bridge" - -# log in to mysql service -$ mysql -u root -p -# The Linux Bridge Neutron agent running on each compute node must be able to -# make a mysql connection back to the main database server. -mysql> GRANT USAGE ON *.* to root@'yourremotehost' IDENTIFIED BY 'newpassword'; -# force update of authorization changes -mysql> FLUSH PRIVILEGES; - -(Note: If the remote connection fails to MySQL, you might need to add the IP address, - and/or fully-qualified hostname, and/or unqualified hostname in the above GRANT sql - command. Also, you might need to specify "ALL" instead of "USAGE".) - -# -- Plugin configuration - -- Edit the configuration file: - etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini - Make sure it matches your mysql configuration. This file must be updated - with the addresses and credentials to access the database. - - Note: debug and logging information should be updated in etc/neutron.conf - - Note: When running the tests, set the connection type to sqlite, and when - actually running the server set it to mysql. At any given time, only one - of these should be active in the conf file (you can comment out the other). - -- On the neutron server, network_vlan_ranges must be configured in - linuxbridge_conf.ini to specify the names of the physical networks - managed by the linuxbridge plugin, along with the ranges of VLAN IDs - available on each physical network for allocation to virtual - networks. An entry of the form - "::" specifies a VLAN range on - the named physical network. An entry of the form - "" specifies a named network without making a - range of VLANs available for allocation. Networks specified using - either form are available for adminstrators to create provider flat - networks and provider VLANs. Multiple VLAN ranges can be specified - for the same physical network. - - The following example linuxbridge_conf.ini entry shows three - physical networks that can be used to create provider networks, with - ranges of VLANs available for allocation on two of them: - - [VLANS] - network_vlan_ranges = physnet1:1000:2999,physnet1:3000:3999,physnet2,physnet3:1:4094 - - -# -- Agent configuration - -- Edit the configuration file: - etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini - -- Copy neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py - and etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini - to the compute node. - -- Copy the neutron.conf file to the compute node - - Note: debug and logging information should be updated in etc/neutron.conf - -- On each compute node, the network_interface_mappings must be - configured in linuxbridge_conf.ini to map each physical network name - to the physical interface connecting the node to that physical - network. Entries are of the form - ":". For example, one compute - node may use the following physical_inteface_mappings entries: - - [LINUX_BRIDGE] - physical_interface_mappings = physnet1:eth1,physnet2:eth2,physnet3:eth3 - - while another might use: - - [LINUX_BRIDGE] - physical_interface_mappings = physnet1:em3,physnet2:em2,physnet3:em1 - - -$ Run the following: - python linuxbridge_neutron_agent.py --config-file neutron.conf - --config-file linuxbridge_conf.ini - - Note that the the user running the agent must have sudo priviliges - to run various networking commands. Also, the agent can be - configured to use neutron-rootwrap, limiting what commands it can - run via sudo. See http://wiki.openstack.org/Packager/Rootwrap for - details on rootwrap. - - As an alternative to coping the agent python file, if neutron is - installed on the compute node, the agent can be run as - bin/neutron-linuxbridge-agent. diff --git a/neutron/plugins/linuxbridge/__init__.py b/neutron/plugins/linuxbridge/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/linuxbridge/agent/__init__.py b/neutron/plugins/linuxbridge/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py b/neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py deleted file mode 100755 index 5db728655..000000000 --- a/neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py +++ /dev/null @@ -1,1026 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# -# Performs per host Linux Bridge configuration for Neutron. -# Based on the structure of the OpenVSwitch agent in the -# Neutron OpenVSwitch Plugin. -# @author: Sumit Naiksatam, Cisco Systems, Inc. - -import os -import sys -import time - -import eventlet -eventlet.monkey_patch() - -from oslo.config import cfg - -from neutron.agent import l2population_rpc as l2pop_rpc -from neutron.agent.linux import ip_lib -from neutron.agent.linux import utils -from neutron.agent import rpc as agent_rpc -from neutron.agent import securitygroups_rpc as sg_rpc -from neutron.common import config as common_config -from neutron.common import constants -from neutron.common import exceptions -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.common import utils as q_utils -from neutron import context -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall -from neutron.plugins.common import constants as p_const -from neutron.plugins.linuxbridge.common import config # noqa -from neutron.plugins.linuxbridge.common import constants as lconst - - -LOG = logging.getLogger(__name__) - -BRIDGE_NAME_PREFIX = "brq" -TAP_INTERFACE_PREFIX = "tap" -BRIDGE_FS = "/sys/devices/virtual/net/" -BRIDGE_NAME_PLACEHOLDER = "bridge_name" -BRIDGE_INTERFACES_FS = BRIDGE_FS + BRIDGE_NAME_PLACEHOLDER + "/brif/" -DEVICE_NAME_PLACEHOLDER = "device_name" -BRIDGE_PORT_FS_FOR_DEVICE = BRIDGE_FS + DEVICE_NAME_PLACEHOLDER + "/brport" -VXLAN_INTERFACE_PREFIX = "vxlan-" - - -class NetworkSegment: - def __init__(self, network_type, physical_network, segmentation_id): - self.network_type = network_type - self.physical_network = physical_network - self.segmentation_id = segmentation_id - - -class LinuxBridgeManager: - def __init__(self, interface_mappings, root_helper): - self.interface_mappings = interface_mappings - self.root_helper = root_helper - self.ip = ip_lib.IPWrapper(self.root_helper) - # VXLAN related parameters: - self.local_ip = cfg.CONF.VXLAN.local_ip - self.vxlan_mode = lconst.VXLAN_NONE - if cfg.CONF.VXLAN.enable_vxlan: - self.local_int = self.get_interface_by_ip(self.local_ip) - if self.local_int: - self.check_vxlan_support() - else: - LOG.warning(_('VXLAN is enabled, a valid local_ip ' - 'must be provided')) - # Store network mapping to segments - self.network_map = {} - - def interface_exists_on_bridge(self, bridge, interface): - directory = '/sys/class/net/%s/brif' % bridge - for filename in os.listdir(directory): - if filename == interface: - return True - return False - - def get_bridge_name(self, network_id): - if not network_id: - LOG.warning(_("Invalid Network ID, will lead to incorrect bridge" - "name")) - bridge_name = BRIDGE_NAME_PREFIX + network_id[0:11] - return bridge_name - - def get_subinterface_name(self, physical_interface, vlan_id): - if not vlan_id: - LOG.warning(_("Invalid VLAN ID, will lead to incorrect " - "subinterface name")) - subinterface_name = '%s.%s' % (physical_interface, vlan_id) - return subinterface_name - - def get_tap_device_name(self, interface_id): - if not interface_id: - LOG.warning(_("Invalid Interface ID, will lead to incorrect " - "tap device name")) - tap_device_name = TAP_INTERFACE_PREFIX + interface_id[0:11] - return tap_device_name - - def get_vxlan_device_name(self, segmentation_id): - if 0 <= int(segmentation_id) <= constants.MAX_VXLAN_VNI: - return VXLAN_INTERFACE_PREFIX + str(segmentation_id) - else: - LOG.warning(_("Invalid Segmentation ID: %s, will lead to " - "incorrect vxlan device name"), segmentation_id) - - def get_all_neutron_bridges(self): - neutron_bridge_list = [] - bridge_list = os.listdir(BRIDGE_FS) - for bridge in bridge_list: - if bridge.startswith(BRIDGE_NAME_PREFIX): - neutron_bridge_list.append(bridge) - return neutron_bridge_list - - def get_interfaces_on_bridge(self, bridge_name): - if ip_lib.device_exists(bridge_name, root_helper=self.root_helper): - bridge_interface_path = BRIDGE_INTERFACES_FS.replace( - BRIDGE_NAME_PLACEHOLDER, bridge_name) - return os.listdir(bridge_interface_path) - else: - return [] - - def get_tap_devices_count(self, bridge_name): - bridge_interface_path = BRIDGE_INTERFACES_FS.replace( - BRIDGE_NAME_PLACEHOLDER, bridge_name) - try: - if_list = os.listdir(bridge_interface_path) - return len([interface for interface in if_list if - interface.startswith(TAP_INTERFACE_PREFIX)]) - except OSError: - return 0 - - def get_interface_by_ip(self, ip): - for device in self.ip.get_devices(): - if device.addr.list(to=ip): - return device.name - - def get_bridge_for_tap_device(self, tap_device_name): - bridges = self.get_all_neutron_bridges() - for bridge in bridges: - interfaces = self.get_interfaces_on_bridge(bridge) - if tap_device_name in interfaces: - return bridge - - return None - - def is_device_on_bridge(self, device_name): - if not device_name: - return False - else: - bridge_port_path = BRIDGE_PORT_FS_FOR_DEVICE.replace( - DEVICE_NAME_PLACEHOLDER, device_name) - return os.path.exists(bridge_port_path) - - def ensure_vlan_bridge(self, network_id, physical_interface, vlan_id): - """Create a vlan and bridge unless they already exist.""" - interface = self.ensure_vlan(physical_interface, vlan_id) - bridge_name = self.get_bridge_name(network_id) - ips, gateway = self.get_interface_details(interface) - if self.ensure_bridge(bridge_name, interface, ips, gateway): - return interface - - def ensure_vxlan_bridge(self, network_id, segmentation_id): - """Create a vxlan and bridge unless they already exist.""" - interface = self.ensure_vxlan(segmentation_id) - if not interface: - LOG.error(_("Failed creating vxlan interface for " - "%(segmentation_id)s"), - {segmentation_id: segmentation_id}) - return - bridge_name = self.get_bridge_name(network_id) - self.ensure_bridge(bridge_name, interface) - return interface - - def get_interface_details(self, interface): - device = self.ip.device(interface) - ips = device.addr.list(scope='global') - - # Update default gateway if necessary - gateway = device.route.get_gateway(scope='global') - return ips, gateway - - def ensure_flat_bridge(self, network_id, physical_interface): - """Create a non-vlan bridge unless it already exists.""" - bridge_name = self.get_bridge_name(network_id) - ips, gateway = self.get_interface_details(physical_interface) - if self.ensure_bridge(bridge_name, physical_interface, ips, gateway): - return physical_interface - - def ensure_local_bridge(self, network_id): - """Create a local bridge unless it already exists.""" - bridge_name = self.get_bridge_name(network_id) - return self.ensure_bridge(bridge_name) - - def ensure_vlan(self, physical_interface, vlan_id): - """Create a vlan unless it already exists.""" - interface = self.get_subinterface_name(physical_interface, vlan_id) - if not ip_lib.device_exists(interface, root_helper=self.root_helper): - LOG.debug(_("Creating subinterface %(interface)s for " - "VLAN %(vlan_id)s on interface " - "%(physical_interface)s"), - {'interface': interface, 'vlan_id': vlan_id, - 'physical_interface': physical_interface}) - if utils.execute(['ip', 'link', 'add', 'link', - physical_interface, - 'name', interface, 'type', 'vlan', 'id', - vlan_id], root_helper=self.root_helper): - return - if utils.execute(['ip', 'link', 'set', - interface, 'up'], root_helper=self.root_helper): - return - LOG.debug(_("Done creating subinterface %s"), interface) - return interface - - def ensure_vxlan(self, segmentation_id): - """Create a vxlan unless it already exists.""" - interface = self.get_vxlan_device_name(segmentation_id) - if not ip_lib.device_exists(interface, root_helper=self.root_helper): - LOG.debug(_("Creating vxlan interface %(interface)s for " - "VNI %(segmentation_id)s"), - {'interface': interface, - 'segmentation_id': segmentation_id}) - args = {'dev': self.local_int} - if self.vxlan_mode == lconst.VXLAN_MCAST: - args['group'] = cfg.CONF.VXLAN.vxlan_group - if cfg.CONF.VXLAN.ttl: - args['ttl'] = cfg.CONF.VXLAN.ttl - if cfg.CONF.VXLAN.tos: - args['tos'] = cfg.CONF.VXLAN.tos - if cfg.CONF.VXLAN.l2_population: - args['proxy'] = True - int_vxlan = self.ip.add_vxlan(interface, segmentation_id, **args) - int_vxlan.link.set_up() - LOG.debug(_("Done creating vxlan interface %s"), interface) - return interface - - def update_interface_ip_details(self, destination, source, ips, - gateway): - if ips or gateway: - dst_device = self.ip.device(destination) - src_device = self.ip.device(source) - - # Append IP's to bridge if necessary - if ips: - for ip in ips: - dst_device.addr.add(ip_version=ip['ip_version'], - cidr=ip['cidr'], - broadcast=ip['broadcast']) - - if gateway: - # Ensure that the gateway can be updated by changing the metric - metric = 100 - if 'metric' in gateway: - metric = gateway['metric'] - 1 - dst_device.route.add_gateway(gateway=gateway['gateway'], - metric=metric) - src_device.route.delete_gateway(gateway=gateway['gateway']) - - # Remove IP's from interface - if ips: - for ip in ips: - src_device.addr.delete(ip_version=ip['ip_version'], - cidr=ip['cidr']) - - def _bridge_exists_and_ensure_up(self, bridge_name): - """Check if the bridge exists and make sure it is up.""" - br = ip_lib.IPDevice(bridge_name, self.root_helper) - try: - # If the device doesn't exist this will throw a RuntimeError - br.link.set_up() - except RuntimeError: - return False - return True - - def ensure_bridge(self, bridge_name, interface=None, ips=None, - gateway=None): - """Create a bridge unless it already exists.""" - # _bridge_exists_and_ensure_up instead of device_exists is used here - # because there are cases where the bridge exists but it's not UP, - # for example: - # 1) A greenthread was executing this function and had not yet executed - # "ip link set bridge_name up" before eventlet switched to this - # thread running the same function - # 2) The Nova VIF driver was running concurrently and had just created - # the bridge, but had not yet put it UP - if not self._bridge_exists_and_ensure_up(bridge_name): - LOG.debug(_("Starting bridge %(bridge_name)s for subinterface " - "%(interface)s"), - {'bridge_name': bridge_name, 'interface': interface}) - if utils.execute(['brctl', 'addbr', bridge_name], - root_helper=self.root_helper): - return - if utils.execute(['brctl', 'setfd', bridge_name, - str(0)], root_helper=self.root_helper): - return - if utils.execute(['brctl', 'stp', bridge_name, - 'off'], root_helper=self.root_helper): - return - if utils.execute(['ip', 'link', 'set', bridge_name, - 'up'], root_helper=self.root_helper): - return - LOG.debug(_("Done starting bridge %(bridge_name)s for " - "subinterface %(interface)s"), - {'bridge_name': bridge_name, 'interface': interface}) - - if not interface: - return bridge_name - - # Update IP info if necessary - self.update_interface_ip_details(bridge_name, interface, ips, gateway) - - # Check if the interface is part of the bridge - if not self.interface_exists_on_bridge(bridge_name, interface): - try: - # Check if the interface is not enslaved in another bridge - if self.is_device_on_bridge(interface): - bridge = self.get_bridge_for_tap_device(interface) - utils.execute(['brctl', 'delif', bridge, interface], - root_helper=self.root_helper) - - utils.execute(['brctl', 'addif', bridge_name, interface], - root_helper=self.root_helper) - except Exception as e: - LOG.error(_("Unable to add %(interface)s to %(bridge_name)s! " - "Exception: %(e)s"), - {'interface': interface, 'bridge_name': bridge_name, - 'e': e}) - return - return bridge_name - - def ensure_physical_in_bridge(self, network_id, - network_type, - physical_network, - segmentation_id): - if network_type == p_const.TYPE_VXLAN: - if self.vxlan_mode == lconst.VXLAN_NONE: - LOG.error(_("Unable to add vxlan interface for network %s"), - network_id) - return - return self.ensure_vxlan_bridge(network_id, segmentation_id) - - physical_interface = self.interface_mappings.get(physical_network) - if not physical_interface: - LOG.error(_("No mapping for physical network %s"), - physical_network) - return - if network_type == p_const.TYPE_FLAT: - return self.ensure_flat_bridge(network_id, physical_interface) - elif network_type == p_const.TYPE_VLAN: - return self.ensure_vlan_bridge(network_id, physical_interface, - segmentation_id) - else: - LOG.error(_("Unknown network_type %(network_type)s for network " - "%(network_id)s."), {network_type: network_type, - network_id: network_id}) - - def add_tap_interface(self, network_id, network_type, physical_network, - segmentation_id, tap_device_name): - """Add tap interface. - - If a VIF has been plugged into a network, this function will - add the corresponding tap device to the relevant bridge. - """ - if not ip_lib.device_exists(tap_device_name, - root_helper=self.root_helper): - LOG.debug(_("Tap device: %s does not exist on " - "this host, skipped"), tap_device_name) - return False - - bridge_name = self.get_bridge_name(network_id) - if network_type == p_const.TYPE_LOCAL: - self.ensure_local_bridge(network_id) - elif not self.ensure_physical_in_bridge(network_id, - network_type, - physical_network, - segmentation_id): - return False - - # Check if device needs to be added to bridge - tap_device_in_bridge = self.get_bridge_for_tap_device(tap_device_name) - if not tap_device_in_bridge: - data = {'tap_device_name': tap_device_name, - 'bridge_name': bridge_name} - msg = _("Adding device %(tap_device_name)s to bridge " - "%(bridge_name)s") % data - LOG.debug(msg) - if utils.execute(['brctl', 'addif', bridge_name, tap_device_name], - root_helper=self.root_helper): - return False - else: - data = {'tap_device_name': tap_device_name, - 'bridge_name': bridge_name} - msg = _("%(tap_device_name)s already exists on bridge " - "%(bridge_name)s") % data - LOG.debug(msg) - return True - - def add_interface(self, network_id, network_type, physical_network, - segmentation_id, port_id): - self.network_map[network_id] = NetworkSegment(network_type, - physical_network, - segmentation_id) - tap_device_name = self.get_tap_device_name(port_id) - return self.add_tap_interface(network_id, network_type, - physical_network, segmentation_id, - tap_device_name) - - def delete_vlan_bridge(self, bridge_name): - if ip_lib.device_exists(bridge_name, root_helper=self.root_helper): - interfaces_on_bridge = self.get_interfaces_on_bridge(bridge_name) - for interface in interfaces_on_bridge: - self.remove_interface(bridge_name, interface) - - if interface.startswith(VXLAN_INTERFACE_PREFIX): - self.delete_vxlan(interface) - continue - - for physical_interface in self.interface_mappings.itervalues(): - if (interface.startswith(physical_interface)): - ips, gateway = self.get_interface_details(bridge_name) - if ips: - # This is a flat network or a VLAN interface that - # was setup outside of neutron => return IP's from - # bridge to interface - self.update_interface_ip_details(interface, - bridge_name, - ips, gateway) - elif physical_interface != interface: - self.delete_vlan(interface) - - LOG.debug(_("Deleting bridge %s"), bridge_name) - if utils.execute(['ip', 'link', 'set', bridge_name, 'down'], - root_helper=self.root_helper): - return - if utils.execute(['brctl', 'delbr', bridge_name], - root_helper=self.root_helper): - return - LOG.debug(_("Done deleting bridge %s"), bridge_name) - - else: - LOG.error(_("Cannot delete bridge %s, does not exist"), - bridge_name) - - def remove_empty_bridges(self): - for network_id in self.network_map.keys(): - bridge_name = self.get_bridge_name(network_id) - if not self.get_tap_devices_count(bridge_name): - self.delete_vlan_bridge(bridge_name) - del self.network_map[network_id] - - def remove_interface(self, bridge_name, interface_name): - if ip_lib.device_exists(bridge_name, root_helper=self.root_helper): - if not self.is_device_on_bridge(interface_name): - return True - LOG.debug(_("Removing device %(interface_name)s from bridge " - "%(bridge_name)s"), - {'interface_name': interface_name, - 'bridge_name': bridge_name}) - if utils.execute(['brctl', 'delif', bridge_name, interface_name], - root_helper=self.root_helper): - return False - LOG.debug(_("Done removing device %(interface_name)s from bridge " - "%(bridge_name)s"), - {'interface_name': interface_name, - 'bridge_name': bridge_name}) - return True - else: - LOG.debug(_("Cannot remove device %(interface_name)s bridge " - "%(bridge_name)s does not exist"), - {'interface_name': interface_name, - 'bridge_name': bridge_name}) - return False - - def delete_vlan(self, interface): - if ip_lib.device_exists(interface, root_helper=self.root_helper): - LOG.debug(_("Deleting subinterface %s for vlan"), interface) - if utils.execute(['ip', 'link', 'set', interface, 'down'], - root_helper=self.root_helper): - return - if utils.execute(['ip', 'link', 'delete', interface], - root_helper=self.root_helper): - return - LOG.debug(_("Done deleting subinterface %s"), interface) - - def delete_vxlan(self, interface): - if ip_lib.device_exists(interface, root_helper=self.root_helper): - LOG.debug(_("Deleting vxlan interface %s for vlan"), - interface) - int_vxlan = self.ip.device(interface) - int_vxlan.link.set_down() - int_vxlan.link.delete() - LOG.debug(_("Done deleting vxlan interface %s"), interface) - - def get_tap_devices(self): - devices = set() - for device in os.listdir(BRIDGE_FS): - if device.startswith(TAP_INTERFACE_PREFIX): - devices.add(device) - return devices - - def vxlan_ucast_supported(self): - if not cfg.CONF.VXLAN.l2_population: - return False - if not ip_lib.iproute_arg_supported( - ['bridge', 'fdb'], 'append', self.root_helper): - LOG.warning(_('Option "%(option)s" must be supported by command ' - '"%(command)s" to enable %(mode)s mode') % - {'option': 'append', - 'command': 'bridge fdb', - 'mode': 'VXLAN UCAST'}) - return False - for segmentation_id in range(1, constants.MAX_VXLAN_VNI + 1): - if not ip_lib.device_exists( - self.get_vxlan_device_name(segmentation_id), - root_helper=self.root_helper): - break - else: - LOG.error(_('No valid Segmentation ID to perform UCAST test.')) - return False - - test_iface = self.ensure_vxlan(segmentation_id) - try: - utils.execute( - cmd=['bridge', 'fdb', 'append', constants.FLOODING_ENTRY[0], - 'dev', test_iface, 'dst', '1.1.1.1'], - root_helper=self.root_helper) - return True - except RuntimeError: - return False - finally: - self.delete_vxlan(test_iface) - - def vxlan_mcast_supported(self): - if not cfg.CONF.VXLAN.vxlan_group: - LOG.warning(_('VXLAN muticast group must be provided in ' - 'vxlan_group option to enable VXLAN MCAST mode')) - return False - if not ip_lib.iproute_arg_supported( - ['ip', 'link', 'add', 'type', 'vxlan'], - 'proxy', self.root_helper): - LOG.warning(_('Option "%(option)s" must be supported by command ' - '"%(command)s" to enable %(mode)s mode') % - {'option': 'proxy', - 'command': 'ip link add type vxlan', - 'mode': 'VXLAN MCAST'}) - - return False - return True - - def vxlan_module_supported(self): - try: - utils.execute(cmd=['modinfo', 'vxlan']) - return True - except RuntimeError: - return False - - def check_vxlan_support(self): - self.vxlan_mode = lconst.VXLAN_NONE - if not self.vxlan_module_supported(): - LOG.error(_('Linux kernel vxlan module and iproute2 3.8 or above ' - 'are required to enable VXLAN.')) - raise exceptions.VxlanNetworkUnsupported() - - if self.vxlan_ucast_supported(): - self.vxlan_mode = lconst.VXLAN_UCAST - elif self.vxlan_mcast_supported(): - self.vxlan_mode = lconst.VXLAN_MCAST - else: - raise exceptions.VxlanNetworkUnsupported() - LOG.debug(_('Using %s VXLAN mode'), self.vxlan_mode) - - def fdb_ip_entry_exists(self, mac, ip, interface): - entries = utils.execute(['ip', 'neigh', 'show', 'to', ip, - 'dev', interface], - root_helper=self.root_helper) - return mac in entries - - def fdb_bridge_entry_exists(self, mac, interface, agent_ip=None): - entries = utils.execute(['bridge', 'fdb', 'show', 'dev', interface], - root_helper=self.root_helper) - if not agent_ip: - return mac in entries - - return (agent_ip in entries and mac in entries) - - def add_fdb_ip_entry(self, mac, ip, interface): - utils.execute(['ip', 'neigh', 'replace', ip, 'lladdr', mac, - 'dev', interface, 'nud', 'permanent'], - root_helper=self.root_helper, - check_exit_code=False) - - def remove_fdb_ip_entry(self, mac, ip, interface): - utils.execute(['ip', 'neigh', 'del', ip, 'lladdr', mac, - 'dev', interface], - root_helper=self.root_helper, - check_exit_code=False) - - def add_fdb_bridge_entry(self, mac, agent_ip, interface, operation="add"): - utils.execute(['bridge', 'fdb', operation, mac, 'dev', interface, - 'dst', agent_ip], - root_helper=self.root_helper, - check_exit_code=False) - - def remove_fdb_bridge_entry(self, mac, agent_ip, interface): - utils.execute(['bridge', 'fdb', 'del', mac, 'dev', interface, - 'dst', agent_ip], - root_helper=self.root_helper, - check_exit_code=False) - - def add_fdb_entries(self, agent_ip, ports, interface): - for mac, ip in ports: - if mac != constants.FLOODING_ENTRY[0]: - self.add_fdb_ip_entry(mac, ip, interface) - self.add_fdb_bridge_entry(mac, agent_ip, interface) - elif self.vxlan_mode == lconst.VXLAN_UCAST: - if self.fdb_bridge_entry_exists(mac, interface): - self.add_fdb_bridge_entry(mac, agent_ip, interface, - "append") - else: - self.add_fdb_bridge_entry(mac, agent_ip, interface) - - def remove_fdb_entries(self, agent_ip, ports, interface): - for mac, ip in ports: - if mac != constants.FLOODING_ENTRY[0]: - self.remove_fdb_ip_entry(mac, ip, interface) - self.remove_fdb_bridge_entry(mac, agent_ip, interface) - elif self.vxlan_mode == lconst.VXLAN_UCAST: - self.remove_fdb_bridge_entry(mac, agent_ip, interface) - - -class LinuxBridgeRpcCallbacks(rpc_compat.RpcCallback, - sg_rpc.SecurityGroupAgentRpcCallbackMixin, - l2pop_rpc.L2populationRpcCallBackMixin): - - # Set RPC API version to 1.0 by default. - # history - # 1.1 Support Security Group RPC - RPC_API_VERSION = '1.1' - - def __init__(self, context, agent): - super(LinuxBridgeRpcCallbacks, self).__init__() - self.context = context - self.agent = agent - self.sg_agent = agent - - def network_delete(self, context, **kwargs): - LOG.debug(_("network_delete received")) - network_id = kwargs.get('network_id') - bridge_name = self.agent.br_mgr.get_bridge_name(network_id) - LOG.debug(_("Delete %s"), bridge_name) - self.agent.br_mgr.delete_vlan_bridge(bridge_name) - - def port_update(self, context, **kwargs): - port_id = kwargs['port']['id'] - tap_name = self.agent.br_mgr.get_tap_device_name(port_id) - # Put the tap name in the updated_devices set. - # Do not store port details, as if they're used for processing - # notifications there is no guarantee the notifications are - # processed in the same order as the relevant API requests. - self.agent.updated_devices.add(tap_name) - LOG.debug(_("port_update RPC received for port: %s"), port_id) - - def fdb_add(self, context, fdb_entries): - LOG.debug(_("fdb_add received")) - for network_id, values in fdb_entries.items(): - segment = self.agent.br_mgr.network_map.get(network_id) - if not segment: - return - - if segment.network_type != p_const.TYPE_VXLAN: - return - - interface = self.agent.br_mgr.get_vxlan_device_name( - segment.segmentation_id) - - agent_ports = values.get('ports') - for agent_ip, ports in agent_ports.items(): - if agent_ip == self.agent.br_mgr.local_ip: - continue - - self.agent.br_mgr.add_fdb_entries(agent_ip, - ports, - interface) - - def fdb_remove(self, context, fdb_entries): - LOG.debug(_("fdb_remove received")) - for network_id, values in fdb_entries.items(): - segment = self.agent.br_mgr.network_map.get(network_id) - if not segment: - return - - if segment.network_type != p_const.TYPE_VXLAN: - return - - interface = self.agent.br_mgr.get_vxlan_device_name( - segment.segmentation_id) - - agent_ports = values.get('ports') - for agent_ip, ports in agent_ports.items(): - if agent_ip == self.agent.br_mgr.local_ip: - continue - - self.agent.br_mgr.remove_fdb_entries(agent_ip, - ports, - interface) - - def _fdb_chg_ip(self, context, fdb_entries): - LOG.debug(_("update chg_ip received")) - for network_id, agent_ports in fdb_entries.items(): - segment = self.agent.br_mgr.network_map.get(network_id) - if not segment: - return - - if segment.network_type != p_const.TYPE_VXLAN: - return - - interface = self.agent.br_mgr.get_vxlan_device_name( - segment.segmentation_id) - - for agent_ip, state in agent_ports.items(): - if agent_ip == self.agent.br_mgr.local_ip: - continue - - after = state.get('after') - for mac, ip in after: - self.agent.br_mgr.add_fdb_ip_entry(mac, ip, interface) - - before = state.get('before') - for mac, ip in before: - self.agent.br_mgr.remove_fdb_ip_entry(mac, ip, interface) - - def fdb_update(self, context, fdb_entries): - LOG.debug(_("fdb_update received")) - for action, values in fdb_entries.items(): - method = '_fdb_' + action - if not hasattr(self, method): - raise NotImplementedError() - - getattr(self, method)(context, values) - - -class LinuxBridgePluginApi(agent_rpc.PluginApi, - sg_rpc.SecurityGroupServerRpcApiMixin): - pass - - -class LinuxBridgeNeutronAgentRPC(sg_rpc.SecurityGroupAgentRpcMixin): - - def __init__(self, interface_mappings, polling_interval, - root_helper): - self.polling_interval = polling_interval - self.root_helper = root_helper - self.setup_linux_bridge(interface_mappings) - configurations = {'interface_mappings': interface_mappings} - if self.br_mgr.vxlan_mode != lconst.VXLAN_NONE: - configurations['tunneling_ip'] = self.br_mgr.local_ip - configurations['tunnel_types'] = [p_const.TYPE_VXLAN] - configurations['l2_population'] = cfg.CONF.VXLAN.l2_population - self.agent_state = { - 'binary': 'neutron-linuxbridge-agent', - 'host': cfg.CONF.host, - 'topic': constants.L2_AGENT_TOPIC, - 'configurations': configurations, - 'agent_type': constants.AGENT_TYPE_LINUXBRIDGE, - 'start_flag': True} - - # stores received port_updates for processing by the main loop - self.updated_devices = set() - self.setup_rpc(interface_mappings.values()) - self.init_firewall() - - def _report_state(self): - try: - devices = len(self.br_mgr.get_tap_devices()) - self.agent_state.get('configurations')['devices'] = devices - self.state_rpc.report_state(self.context, - self.agent_state) - self.agent_state.pop('start_flag', None) - except Exception: - LOG.exception(_("Failed reporting state!")) - - def setup_rpc(self, physical_interfaces): - if physical_interfaces: - mac = utils.get_interface_mac(physical_interfaces[0]) - else: - devices = ip_lib.IPWrapper(self.root_helper).get_devices(True) - if devices: - mac = utils.get_interface_mac(devices[0].name) - else: - LOG.error(_("Unable to obtain MAC address for unique ID. " - "Agent terminated!")) - exit(1) - self.agent_id = '%s%s' % ('lb', (mac.replace(":", ""))) - LOG.info(_("RPC agent_id: %s"), self.agent_id) - - self.topic = topics.AGENT - self.plugin_rpc = LinuxBridgePluginApi(topics.PLUGIN) - self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) - # RPC network init - self.context = context.get_admin_context_without_session() - # Handle updates from service - self.endpoints = [LinuxBridgeRpcCallbacks(self.context, self)] - # Define the listening consumers for the agent - consumers = [[topics.PORT, topics.UPDATE], - [topics.NETWORK, topics.DELETE], - [topics.SECURITY_GROUP, topics.UPDATE]] - if cfg.CONF.VXLAN.l2_population: - consumers.append([topics.L2POPULATION, - topics.UPDATE, cfg.CONF.host]) - self.connection = agent_rpc.create_consumers(self.endpoints, - self.topic, - consumers) - report_interval = cfg.CONF.AGENT.report_interval - if report_interval: - heartbeat = loopingcall.FixedIntervalLoopingCall( - self._report_state) - heartbeat.start(interval=report_interval) - - def setup_linux_bridge(self, interface_mappings): - self.br_mgr = LinuxBridgeManager(interface_mappings, self.root_helper) - - def remove_port_binding(self, network_id, interface_id): - bridge_name = self.br_mgr.get_bridge_name(network_id) - tap_device_name = self.br_mgr.get_tap_device_name(interface_id) - return self.br_mgr.remove_interface(bridge_name, tap_device_name) - - def process_network_devices(self, device_info): - resync_a = False - resync_b = False - - self.prepare_devices_filter(device_info.get('added')) - - if device_info.get('updated'): - self.refresh_firewall() - - # Updated devices are processed the same as new ones, as their - # admin_state_up may have changed. The set union prevents duplicating - # work when a device is new and updated in the same polling iteration. - devices_added_updated = (set(device_info.get('added')) - | set(device_info.get('updated'))) - if devices_added_updated: - resync_a = self.treat_devices_added_updated(devices_added_updated) - - if device_info.get('removed'): - resync_b = self.treat_devices_removed(device_info['removed']) - # If one of the above operations fails => resync with plugin - return (resync_a | resync_b) - - def treat_devices_added_updated(self, devices): - resync = False - - for device in devices: - LOG.debug(_("Treating added or updated device: %s"), device) - try: - details = self.plugin_rpc.get_device_details(self.context, - device, - self.agent_id) - except Exception as e: - LOG.debug(_("Unable to get port details for " - "%(device)s: %(e)s"), - {'device': device, 'e': e}) - resync = True - continue - if 'port_id' in details: - LOG.info(_("Port %(device)s updated. Details: %(details)s"), - {'device': device, 'details': details}) - if details['admin_state_up']: - # create the networking for the port - network_type = details.get('network_type') - if network_type: - segmentation_id = details.get('segmentation_id') - else: - # compatibility with pre-Havana RPC vlan_id encoding - vlan_id = details.get('vlan_id') - (network_type, - segmentation_id) = lconst.interpret_vlan_id(vlan_id) - if self.br_mgr.add_interface(details['network_id'], - network_type, - details['physical_network'], - segmentation_id, - details['port_id']): - - # update plugin about port status - self.plugin_rpc.update_device_up(self.context, - device, - self.agent_id, - cfg.CONF.host) - else: - self.plugin_rpc.update_device_down(self.context, - device, - self.agent_id, - cfg.CONF.host) - else: - self.remove_port_binding(details['network_id'], - details['port_id']) - else: - LOG.info(_("Device %s not defined on plugin"), device) - return resync - - def treat_devices_removed(self, devices): - resync = False - self.remove_devices_filter(devices) - for device in devices: - LOG.info(_("Attachment %s removed"), device) - details = None - try: - details = self.plugin_rpc.update_device_down(self.context, - device, - self.agent_id, - cfg.CONF.host) - except Exception as e: - LOG.debug(_("port_removed failed for %(device)s: %(e)s"), - {'device': device, 'e': e}) - resync = True - if details and details['exists']: - LOG.info(_("Port %s updated."), device) - else: - LOG.debug(_("Device %s not defined on plugin"), device) - self.br_mgr.remove_empty_bridges() - return resync - - def scan_devices(self, registered_devices, updated_devices): - curr_devices = self.br_mgr.get_tap_devices() - device_info = {} - device_info['current'] = curr_devices - device_info['added'] = curr_devices - registered_devices - # we don't want to process updates for devices that don't exist - device_info['updated'] = updated_devices & curr_devices - # we need to clean up after devices are removed - device_info['removed'] = registered_devices - curr_devices - return device_info - - def _device_info_has_changes(self, device_info): - return (device_info.get('added') - or device_info.get('updated') - or device_info.get('removed')) - - def daemon_loop(self): - sync = True - devices = set() - - LOG.info(_("LinuxBridge Agent RPC Daemon Started!")) - - while True: - start = time.time() - if sync: - LOG.info(_("Agent out of sync with plugin!")) - devices.clear() - sync = False - device_info = {} - # Save updated devices dict to perform rollback in case - # resync would be needed, and then clear self.updated_devices. - # As the greenthread should not yield between these - # two statements, this will should be thread-safe. - updated_devices_copy = self.updated_devices - self.updated_devices = set() - try: - device_info = self.scan_devices(devices, updated_devices_copy) - if self._device_info_has_changes(device_info): - LOG.debug(_("Agent loop found changes! %s"), device_info) - # If treat devices fails - indicates must resync with - # plugin - sync = self.process_network_devices(device_info) - devices = device_info['current'] - except Exception: - LOG.exception(_("Error in agent loop. Devices info: %s"), - device_info) - sync = True - # Restore devices that were removed from this set earlier - # without overwriting ones that may have arrived since. - self.updated_devices |= updated_devices_copy - - # sleep till end of polling interval - elapsed = (time.time() - start) - if (elapsed < self.polling_interval): - time.sleep(self.polling_interval - elapsed) - else: - LOG.debug(_("Loop iteration exceeded interval " - "(%(polling_interval)s vs. %(elapsed)s)!"), - {'polling_interval': self.polling_interval, - 'elapsed': elapsed}) - - -def main(): - common_config.init(sys.argv[1:]) - - common_config.setup_logging(cfg.CONF) - try: - interface_mappings = q_utils.parse_mappings( - cfg.CONF.LINUX_BRIDGE.physical_interface_mappings) - except ValueError as e: - LOG.error(_("Parsing physical_interface_mappings failed: %s." - " Agent terminated!"), e) - sys.exit(1) - LOG.info(_("Interface mappings: %s"), interface_mappings) - - polling_interval = cfg.CONF.AGENT.polling_interval - root_helper = cfg.CONF.AGENT.root_helper - agent = LinuxBridgeNeutronAgentRPC(interface_mappings, - polling_interval, - root_helper) - LOG.info(_("Agent initialized successfully, now running... ")) - agent.daemon_loop() - sys.exit(0) - - -if __name__ == "__main__": - main() diff --git a/neutron/plugins/linuxbridge/common/__init__.py b/neutron/plugins/linuxbridge/common/__init__.py deleted file mode 100644 index 5bb15232d..000000000 --- a/neutron/plugins/linuxbridge/common/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. diff --git a/neutron/plugins/linuxbridge/common/config.py b/neutron/plugins/linuxbridge/common/config.py deleted file mode 100644 index 8736d63a6..000000000 --- a/neutron/plugins/linuxbridge/common/config.py +++ /dev/null @@ -1,78 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Rohit Agarwalla, Cisco Systems, Inc. - -from oslo.config import cfg - -from neutron.agent.common import config - -DEFAULT_VLAN_RANGES = [] -DEFAULT_INTERFACE_MAPPINGS = [] -DEFAULT_VXLAN_GROUP = '224.0.0.1' - - -vlan_opts = [ - cfg.StrOpt('tenant_network_type', default='local', - help=_("Network type for tenant networks " - "(local, vlan, or none)")), - cfg.ListOpt('network_vlan_ranges', - default=DEFAULT_VLAN_RANGES, - help=_("List of :: " - "or ")), -] - -vxlan_opts = [ - cfg.BoolOpt('enable_vxlan', default=False, - help=_("Enable VXLAN on the agent. Can be enabled when " - "agent is managed by ml2 plugin using linuxbridge " - "mechanism driver")), - cfg.IntOpt('ttl', - help=_("TTL for vxlan interface protocol packets.")), - cfg.IntOpt('tos', - help=_("TOS for vxlan interface protocol packets.")), - cfg.StrOpt('vxlan_group', default=DEFAULT_VXLAN_GROUP, - help=_("Multicast group for vxlan interface.")), - cfg.StrOpt('local_ip', default='', - help=_("Local IP address of the VXLAN endpoints.")), - cfg.BoolOpt('l2_population', default=False, - help=_("Extension to use alongside ml2 plugin's l2population " - "mechanism driver. It enables the plugin to populate " - "VXLAN forwarding table.")), -] - -bridge_opts = [ - cfg.ListOpt('physical_interface_mappings', - default=DEFAULT_INTERFACE_MAPPINGS, - help=_("List of :")), -] - -agent_opts = [ - cfg.IntOpt('polling_interval', default=2, - help=_("The number of seconds the agent will wait between " - "polling for local device changes.")), - cfg.BoolOpt('rpc_support_old_agents', default=False, - help=_("Enable server RPC compatibility with old agents")), -] - - -cfg.CONF.register_opts(vlan_opts, "VLANS") -cfg.CONF.register_opts(vxlan_opts, "VXLAN") -cfg.CONF.register_opts(bridge_opts, "LINUX_BRIDGE") -cfg.CONF.register_opts(agent_opts, "AGENT") -config.register_agent_state_opts_helper(cfg.CONF) -config.register_root_helper(cfg.CONF) diff --git a/neutron/plugins/linuxbridge/common/constants.py b/neutron/plugins/linuxbridge/common/constants.py deleted file mode 100644 index 6dee88f40..000000000 --- a/neutron/plugins/linuxbridge/common/constants.py +++ /dev/null @@ -1,42 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. - - -from neutron.plugins.common import constants as p_const - - -FLAT_VLAN_ID = -1 -LOCAL_VLAN_ID = -2 - -# Supported VXLAN features -VXLAN_NONE = 'not_supported' -VXLAN_MCAST = 'multicast_flooding' -VXLAN_UCAST = 'unicast_flooding' - - -# TODO(rkukura): Eventually remove this function, which provides -# temporary backward compatibility with pre-Havana RPC and DB vlan_id -# encoding. -def interpret_vlan_id(vlan_id): - """Return (network_type, segmentation_id) tuple for encoded vlan_id.""" - if vlan_id == LOCAL_VLAN_ID: - return (p_const.TYPE_LOCAL, None) - elif vlan_id == FLAT_VLAN_ID: - return (p_const.TYPE_FLAT, None) - else: - return (p_const.TYPE_VLAN, vlan_id) diff --git a/neutron/plugins/linuxbridge/db/__init__.py b/neutron/plugins/linuxbridge/db/__init__.py deleted file mode 100644 index 33daf1f33..000000000 --- a/neutron/plugins/linuxbridge/db/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# diff --git a/neutron/plugins/linuxbridge/db/l2network_db_v2.py b/neutron/plugins/linuxbridge/db/l2network_db_v2.py deleted file mode 100644 index 416bd2f59..000000000 --- a/neutron/plugins/linuxbridge/db/l2network_db_v2.py +++ /dev/null @@ -1,238 +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 six import moves -from sqlalchemy.orm import exc - -from neutron.common import exceptions as n_exc -import neutron.db.api as db -from neutron.db import models_v2 -from neutron.db import securitygroups_db as sg_db -from neutron import manager -from neutron.openstack.common import log as logging -from neutron.plugins.linuxbridge.common import config # noqa -from neutron.plugins.linuxbridge.common import constants -from neutron.plugins.linuxbridge.db import l2network_models_v2 - -LOG = logging.getLogger(__name__) - - -def sync_network_states(network_vlan_ranges): - """Synchronize network_states table with current configured VLAN ranges.""" - - session = db.get_session() - with session.begin(): - # get existing allocations for all physical networks - allocations = dict() - states = (session.query(l2network_models_v2.NetworkState). - all()) - for state in states: - if state.physical_network not in allocations: - allocations[state.physical_network] = set() - allocations[state.physical_network].add(state) - - # process vlan ranges for each configured physical network - for physical_network, vlan_ranges in network_vlan_ranges.iteritems(): - # determine current configured allocatable vlans for this - # physical network - vlan_ids = set() - for vlan_range in vlan_ranges: - vlan_ids |= set(moves.xrange(vlan_range[0], vlan_range[1] + 1)) - - # remove from table unallocated vlans not currently allocatable - if physical_network in allocations: - for state in allocations[physical_network]: - try: - # see if vlan is allocatable - vlan_ids.remove(state.vlan_id) - except KeyError: - # it's not allocatable, so check if its allocated - if not state.allocated: - # it's not, so remove it from table - LOG.debug(_("Removing vlan %(vlan_id)s on " - "physical network %(physical_network)s" - " from pool"), - {'vlan_id': state.vlan_id, - 'physical_network': physical_network}) - session.delete(state) - del allocations[physical_network] - - # add missing allocatable vlans to table - for vlan_id in sorted(vlan_ids): - state = l2network_models_v2.NetworkState(physical_network, - vlan_id) - session.add(state) - - # remove from table unallocated vlans for any unconfigured physical - # networks - for states in allocations.itervalues(): - for state in states: - if not state.allocated: - LOG.debug(_("Removing vlan %(vlan_id)s on physical " - "network %(physical_network)s" - " from pool"), - {'vlan_id': state.vlan_id, - 'physical_network': state.physical_network}) - session.delete(state) - - -def get_network_state(physical_network, vlan_id): - """Get state of specified network.""" - - session = db.get_session() - try: - state = (session.query(l2network_models_v2.NetworkState). - filter_by(physical_network=physical_network, - vlan_id=vlan_id). - one()) - return state - except exc.NoResultFound: - return None - - -def reserve_network(session): - with session.begin(subtransactions=True): - state = (session.query(l2network_models_v2.NetworkState). - filter_by(allocated=False). - with_lockmode('update'). - first()) - if not state: - raise n_exc.NoNetworkAvailable() - LOG.debug(_("Reserving vlan %(vlan_id)s on physical network " - "%(physical_network)s from pool"), - {'vlan_id': state.vlan_id, - 'physical_network': state.physical_network}) - state.allocated = True - return (state.physical_network, state.vlan_id) - - -def reserve_specific_network(session, physical_network, vlan_id): - with session.begin(subtransactions=True): - try: - state = (session.query(l2network_models_v2.NetworkState). - filter_by(physical_network=physical_network, - vlan_id=vlan_id). - with_lockmode('update'). - one()) - if state.allocated: - if vlan_id == constants.FLAT_VLAN_ID: - raise n_exc.FlatNetworkInUse( - physical_network=physical_network) - else: - raise n_exc.VlanIdInUse(vlan_id=vlan_id, - physical_network=physical_network) - LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical " - "network %(physical_network)s from pool"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - state.allocated = True - except exc.NoResultFound: - LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical " - "network %(physical_network)s outside pool"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - state = l2network_models_v2.NetworkState(physical_network, vlan_id) - state.allocated = True - session.add(state) - - -def release_network(session, physical_network, vlan_id, network_vlan_ranges): - with session.begin(subtransactions=True): - try: - state = (session.query(l2network_models_v2.NetworkState). - filter_by(physical_network=physical_network, - vlan_id=vlan_id). - with_lockmode('update'). - one()) - state.allocated = False - inside = False - for vlan_range in network_vlan_ranges.get(physical_network, []): - if vlan_id >= vlan_range[0] and vlan_id <= vlan_range[1]: - inside = True - break - if inside: - LOG.debug(_("Releasing vlan %(vlan_id)s on physical network " - "%(physical_network)s to pool"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - else: - LOG.debug(_("Releasing vlan %(vlan_id)s on physical network " - "%(physical_network)s outside pool"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - session.delete(state) - except exc.NoResultFound: - LOG.warning(_("vlan_id %(vlan_id)s on physical network " - "%(physical_network)s not found"), - {'vlan_id': vlan_id, - 'physical_network': physical_network}) - - -def add_network_binding(session, network_id, physical_network, vlan_id): - with session.begin(subtransactions=True): - binding = l2network_models_v2.NetworkBinding(network_id, - physical_network, vlan_id) - session.add(binding) - - -def get_network_binding(session, network_id): - try: - binding = (session.query(l2network_models_v2.NetworkBinding). - filter_by(network_id=network_id). - one()) - return binding - except exc.NoResultFound: - return - - -def get_port_from_device(device): - """Get port from database.""" - LOG.debug(_("get_port_from_device() called")) - session = db.get_session() - sg_binding_port = sg_db.SecurityGroupPortBinding.port_id - - query = session.query(models_v2.Port, - sg_db.SecurityGroupPortBinding.security_group_id) - query = query.outerjoin(sg_db.SecurityGroupPortBinding, - models_v2.Port.id == sg_binding_port) - query = query.filter(models_v2.Port.id.startswith(device)) - port_and_sgs = query.all() - if not port_and_sgs: - return - port = port_and_sgs[0][0] - plugin = manager.NeutronManager.get_plugin() - port_dict = plugin._make_port_dict(port) - port_dict['security_groups'] = [] - for port_in_db, sg_id in port_and_sgs: - if sg_id: - port_dict['security_groups'].append(sg_id) - port_dict['security_group_rules'] = [] - port_dict['security_group_source_groups'] = [] - port_dict['fixed_ips'] = [ip['ip_address'] - for ip in port['fixed_ips']] - return port_dict - - -def set_port_status(port_id, status): - """Set the port status.""" - LOG.debug(_("set_port_status as %s called"), status) - session = db.get_session() - try: - port = session.query(models_v2.Port).filter_by(id=port_id).one() - port['status'] = status - session.merge(port) - session.flush() - except exc.NoResultFound: - raise n_exc.PortNotFound(port_id=port_id) diff --git a/neutron/plugins/linuxbridge/db/l2network_models_v2.py b/neutron/plugins/linuxbridge/db/l2network_models_v2.py deleted file mode 100644 index 0c08e29c5..000000000 --- a/neutron/plugins/linuxbridge/db/l2network_models_v2.py +++ /dev/null @@ -1,59 +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. - -import sqlalchemy as sa - -from neutron.db import model_base - - -class NetworkState(model_base.BASEV2): - """Represents state of vlan_id on physical network.""" - __tablename__ = 'network_states' - - physical_network = sa.Column(sa.String(64), nullable=False, - primary_key=True) - vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True, - autoincrement=False) - allocated = sa.Column(sa.Boolean, nullable=False) - - def __init__(self, physical_network, vlan_id): - self.physical_network = physical_network - self.vlan_id = vlan_id - self.allocated = False - - def __repr__(self): - return "" % (self.physical_network, - self.vlan_id, self.allocated) - - -class NetworkBinding(model_base.BASEV2): - """Represents binding of virtual network to physical network and vlan.""" - __tablename__ = 'network_bindings' - - network_id = sa.Column(sa.String(36), - sa.ForeignKey('networks.id', ondelete="CASCADE"), - primary_key=True) - physical_network = sa.Column(sa.String(64)) - vlan_id = sa.Column(sa.Integer, nullable=False) - - def __init__(self, network_id, physical_network, vlan_id): - self.network_id = network_id - self.physical_network = physical_network - self.vlan_id = vlan_id - - def __repr__(self): - return "" % (self.network_id, - self.physical_network, - self.vlan_id) diff --git a/neutron/plugins/linuxbridge/lb_neutron_plugin.py b/neutron/plugins/linuxbridge/lb_neutron_plugin.py deleted file mode 100644 index 412275d24..000000000 --- a/neutron/plugins/linuxbridge/lb_neutron_plugin.py +++ /dev/null @@ -1,530 +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. - -import sys - -from oslo.config import cfg - -from neutron.agent import securitygroups_rpc as sg_rpc -from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api -from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api -from neutron.api.v2 import attributes -from neutron.common import constants as q_const -from neutron.common import exceptions as n_exc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.common import utils -from neutron.db import agents_db -from neutron.db import agentschedulers_db -from neutron.db import api as db_api -from neutron.db import db_base_plugin_v2 -from neutron.db import dhcp_rpc_base -from neutron.db import external_net_db -from neutron.db import extraroute_db -from neutron.db import l3_agentschedulers_db -from neutron.db import l3_gwmode_db -from neutron.db import l3_rpc_base -from neutron.db import portbindings_db -from neutron.db import quota_db # noqa -from neutron.db import securitygroups_rpc_base as sg_db_rpc -from neutron.extensions import portbindings -from neutron.extensions import providernet as provider -from neutron import manager -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants as svc_constants -from neutron.plugins.common import utils as plugin_utils -from neutron.plugins.linuxbridge.common import constants -from neutron.plugins.linuxbridge.db import l2network_db_v2 as db - - -LOG = logging.getLogger(__name__) - - -class LinuxBridgeRpcCallbacks(rpc_compat.RpcCallback, - dhcp_rpc_base.DhcpRpcCallbackMixin, - l3_rpc_base.L3RpcCallbackMixin, - sg_db_rpc.SecurityGroupServerRpcCallbackMixin - ): - - # history - # 1.1 Support Security Group RPC - RPC_API_VERSION = '1.1' - # Device names start with "tap" - TAP_PREFIX_LEN = 3 - - @classmethod - def get_port_from_device(cls, device): - port = db.get_port_from_device(device[cls.TAP_PREFIX_LEN:]) - if port: - port['device'] = device - return port - - def get_device_details(self, rpc_context, **kwargs): - """Agent requests device details.""" - agent_id = kwargs.get('agent_id') - device = kwargs.get('device') - LOG.debug(_("Device %(device)s details requested from %(agent_id)s"), - {'device': device, 'agent_id': agent_id}) - port = self.get_port_from_device(device) - if port: - binding = db.get_network_binding(db_api.get_session(), - port['network_id']) - (network_type, - segmentation_id) = constants.interpret_vlan_id(binding.vlan_id) - entry = {'device': device, - 'network_type': network_type, - 'physical_network': binding.physical_network, - 'segmentation_id': segmentation_id, - 'network_id': port['network_id'], - 'port_id': port['id'], - 'admin_state_up': port['admin_state_up']} - if cfg.CONF.AGENT.rpc_support_old_agents: - entry['vlan_id'] = binding.vlan_id - new_status = (q_const.PORT_STATUS_ACTIVE if port['admin_state_up'] - else q_const.PORT_STATUS_DOWN) - if port['status'] != new_status: - db.set_port_status(port['id'], new_status) - else: - entry = {'device': device} - LOG.debug(_("%s can not be found in database"), device) - return entry - - def update_device_down(self, rpc_context, **kwargs): - """Device no longer exists on agent.""" - # TODO(garyk) - live migration and port status - agent_id = kwargs.get('agent_id') - device = kwargs.get('device') - host = kwargs.get('host') - port = self.get_port_from_device(device) - LOG.debug(_("Device %(device)s no longer exists on %(agent_id)s"), - {'device': device, 'agent_id': agent_id}) - plugin = manager.NeutronManager.get_plugin() - if port: - entry = {'device': device, - 'exists': True} - if (host and not - plugin.get_port_host(rpc_context, port['id']) == host): - LOG.debug(_("Device %(device)s not bound to the" - " agent host %(host)s"), - {'device': device, 'host': host}) - elif port['status'] != q_const.PORT_STATUS_DOWN: - # Set port status to DOWN - db.set_port_status(port['id'], q_const.PORT_STATUS_DOWN) - else: - entry = {'device': device, - 'exists': False} - LOG.debug(_("%s can not be found in database"), device) - return entry - - def update_device_up(self, rpc_context, **kwargs): - """Device is up on agent.""" - agent_id = kwargs.get('agent_id') - device = kwargs.get('device') - host = kwargs.get('host') - port = self.get_port_from_device(device) - LOG.debug(_("Device %(device)s up on %(agent_id)s"), - {'device': device, 'agent_id': agent_id}) - plugin = manager.NeutronManager.get_plugin() - if port: - if (host and - not plugin.get_port_host(rpc_context, port['id']) == host): - LOG.debug(_("Device %(device)s not bound to the" - " agent host %(host)s"), - {'device': device, 'host': host}) - return - elif port['status'] != q_const.PORT_STATUS_ACTIVE: - db.set_port_status(port['id'], - q_const.PORT_STATUS_ACTIVE) - else: - LOG.debug(_("%s can not be found in database"), device) - - -class AgentNotifierApi(rpc_compat.RpcProxy, - sg_rpc.SecurityGroupAgentRpcApiMixin): - '''Agent side of the linux bridge rpc API. - - API version history: - 1.0 - Initial version. - 1.1 - Added get_active_networks_info, create_dhcp_port, - and update_dhcp_port methods. - - - ''' - - BASE_RPC_API_VERSION = '1.1' - - def __init__(self, topic): - super(AgentNotifierApi, self).__init__( - topic=topic, default_version=self.BASE_RPC_API_VERSION) - self.topic = topic - self.topic_network_delete = topics.get_topic_name(topic, - topics.NETWORK, - topics.DELETE) - self.topic_port_update = topics.get_topic_name(topic, - topics.PORT, - topics.UPDATE) - - def network_delete(self, context, network_id): - self.fanout_cast(context, - self.make_msg('network_delete', - network_id=network_id), - topic=self.topic_network_delete) - - def port_update(self, context, port, physical_network, vlan_id): - network_type, segmentation_id = constants.interpret_vlan_id(vlan_id) - kwargs = {'port': port, - 'network_type': network_type, - 'physical_network': physical_network, - 'segmentation_id': segmentation_id} - if cfg.CONF.AGENT.rpc_support_old_agents: - kwargs['vlan_id'] = vlan_id - msg = self.make_msg('port_update', **kwargs) - self.fanout_cast(context, msg, - topic=self.topic_port_update) - - -class LinuxBridgePluginV2(db_base_plugin_v2.NeutronDbPluginV2, - external_net_db.External_net_db_mixin, - extraroute_db.ExtraRoute_db_mixin, - l3_gwmode_db.L3_NAT_db_mixin, - sg_db_rpc.SecurityGroupServerRpcMixin, - l3_agentschedulers_db.L3AgentSchedulerDbMixin, - agentschedulers_db.DhcpAgentSchedulerDbMixin, - portbindings_db.PortBindingMixin): - """Implement the Neutron abstractions using Linux bridging. - - A new VLAN is created for each network. An agent is relied upon - to perform the actual Linux bridge configuration on each host. - - The provider extension is also supported. As discussed in - https://bugs.launchpad.net/neutron/+bug/1023156, this class could - be simplified, and filtering on extended attributes could be - handled, by adding support for extended attributes to the - NeutronDbPluginV2 base class. When that occurs, this class should - be updated to take advantage of it. - - The port binding extension enables an external application relay - information to and from the plugin. - """ - - # This attribute specifies whether the plugin supports or not - # bulk/pagination/sorting operations. Name mangling is used in - # order to ensure it is qualified by class - __native_bulk_support = True - __native_pagination_support = True - __native_sorting_support = True - - _supported_extension_aliases = ["provider", "external-net", "router", - "ext-gw-mode", "binding", "quotas", - "security-group", "agent", "extraroute", - "l3_agent_scheduler", - "dhcp_agent_scheduler"] - - @property - def supported_extension_aliases(self): - if not hasattr(self, '_aliases'): - aliases = self._supported_extension_aliases[:] - sg_rpc.disable_security_group_extension_by_config(aliases) - self._aliases = aliases - return self._aliases - - def __init__(self): - super(LinuxBridgePluginV2, self).__init__() - self.base_binding_dict = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_BRIDGE, - portbindings.VIF_DETAILS: { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases}} - self._parse_network_vlan_ranges() - db.sync_network_states(self.network_vlan_ranges) - self.tenant_network_type = cfg.CONF.VLANS.tenant_network_type - if self.tenant_network_type not in [svc_constants.TYPE_LOCAL, - svc_constants.TYPE_VLAN, - svc_constants.TYPE_NONE]: - LOG.error(_("Invalid tenant_network_type: %s. " - "Service terminated!"), - self.tenant_network_type) - sys.exit(1) - self._setup_rpc() - self.network_scheduler = importutils.import_object( - cfg.CONF.network_scheduler_driver - ) - self.router_scheduler = importutils.import_object( - cfg.CONF.router_scheduler_driver - ) - LOG.debug(_("Linux Bridge Plugin initialization complete")) - - def _setup_rpc(self): - # RPC support - self.service_topics = {svc_constants.CORE: topics.PLUGIN, - svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN} - self.conn = rpc_compat.create_connection(new=True) - self.endpoints = [LinuxBridgeRpcCallbacks(), - agents_db.AgentExtRpcCallback()] - for svc_topic in self.service_topics.values(): - self.conn.create_consumer(svc_topic, self.endpoints, fanout=False) - # Consume from all consumers in threads - self.conn.consume_in_threads() - self.notifier = AgentNotifierApi(topics.AGENT) - self.agent_notifiers[q_const.AGENT_TYPE_DHCP] = ( - dhcp_rpc_agent_api.DhcpAgentNotifyAPI() - ) - self.agent_notifiers[q_const.AGENT_TYPE_L3] = ( - l3_rpc_agent_api.L3AgentNotifyAPI() - ) - - def _parse_network_vlan_ranges(self): - try: - self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges( - cfg.CONF.VLANS.network_vlan_ranges) - except Exception as ex: - LOG.error(_("%s. Agent terminated!"), ex) - sys.exit(1) - LOG.info(_("Network VLAN ranges: %s"), self.network_vlan_ranges) - - def _add_network_vlan_range(self, physical_network, vlan_min, vlan_max): - self._add_network(physical_network) - self.network_vlan_ranges[physical_network].append((vlan_min, vlan_max)) - - def _add_network(self, physical_network): - if physical_network not in self.network_vlan_ranges: - self.network_vlan_ranges[physical_network] = [] - - def _extend_network_dict_provider(self, context, network): - binding = db.get_network_binding(context.session, network['id']) - if binding.vlan_id == constants.FLAT_VLAN_ID: - network[provider.NETWORK_TYPE] = svc_constants.TYPE_FLAT - network[provider.PHYSICAL_NETWORK] = binding.physical_network - network[provider.SEGMENTATION_ID] = None - elif binding.vlan_id == constants.LOCAL_VLAN_ID: - network[provider.NETWORK_TYPE] = svc_constants.TYPE_LOCAL - network[provider.PHYSICAL_NETWORK] = None - network[provider.SEGMENTATION_ID] = None - else: - network[provider.NETWORK_TYPE] = svc_constants.TYPE_VLAN - network[provider.PHYSICAL_NETWORK] = binding.physical_network - network[provider.SEGMENTATION_ID] = binding.vlan_id - - def _process_provider_create(self, context, attrs): - network_type = attrs.get(provider.NETWORK_TYPE) - physical_network = attrs.get(provider.PHYSICAL_NETWORK) - segmentation_id = attrs.get(provider.SEGMENTATION_ID) - - network_type_set = attributes.is_attr_set(network_type) - physical_network_set = attributes.is_attr_set(physical_network) - segmentation_id_set = attributes.is_attr_set(segmentation_id) - - if not (network_type_set or physical_network_set or - segmentation_id_set): - return (None, None, None) - - if not network_type_set: - msg = _("provider:network_type required") - raise n_exc.InvalidInput(error_message=msg) - elif network_type == svc_constants.TYPE_FLAT: - if segmentation_id_set: - msg = _("provider:segmentation_id specified for flat network") - raise n_exc.InvalidInput(error_message=msg) - else: - segmentation_id = constants.FLAT_VLAN_ID - elif network_type == svc_constants.TYPE_VLAN: - if not segmentation_id_set: - msg = _("provider:segmentation_id required") - raise n_exc.InvalidInput(error_message=msg) - if not utils.is_valid_vlan_tag(segmentation_id): - msg = (_("provider:segmentation_id out of range " - "(%(min_id)s through %(max_id)s)") % - {'min_id': q_const.MIN_VLAN_TAG, - 'max_id': q_const.MAX_VLAN_TAG}) - raise n_exc.InvalidInput(error_message=msg) - elif network_type == svc_constants.TYPE_LOCAL: - if physical_network_set: - msg = _("provider:physical_network specified for local " - "network") - raise n_exc.InvalidInput(error_message=msg) - else: - physical_network = None - if segmentation_id_set: - msg = _("provider:segmentation_id specified for local " - "network") - raise n_exc.InvalidInput(error_message=msg) - else: - segmentation_id = constants.LOCAL_VLAN_ID - else: - msg = _("provider:network_type %s not supported") % network_type - raise n_exc.InvalidInput(error_message=msg) - - if network_type in [svc_constants.TYPE_VLAN, svc_constants.TYPE_FLAT]: - if physical_network_set: - if physical_network not in self.network_vlan_ranges: - msg = (_("Unknown provider:physical_network %s") % - physical_network) - raise n_exc.InvalidInput(error_message=msg) - elif 'default' in self.network_vlan_ranges: - physical_network = 'default' - else: - msg = _("provider:physical_network required") - raise n_exc.InvalidInput(error_message=msg) - - return (network_type, physical_network, segmentation_id) - - def create_network(self, context, network): - (network_type, physical_network, - vlan_id) = self._process_provider_create(context, - network['network']) - - session = context.session - with session.begin(subtransactions=True): - #set up default security groups - tenant_id = self._get_tenant_id_for_create( - context, network['network']) - self._ensure_default_security_group(context, tenant_id) - - if not network_type: - # tenant network - network_type = self.tenant_network_type - if network_type == svc_constants.TYPE_NONE: - raise n_exc.TenantNetworksDisabled() - elif network_type == svc_constants.TYPE_VLAN: - physical_network, vlan_id = db.reserve_network(session) - else: # TYPE_LOCAL - vlan_id = constants.LOCAL_VLAN_ID - else: - # provider network - if network_type in [svc_constants.TYPE_VLAN, - svc_constants.TYPE_FLAT]: - db.reserve_specific_network(session, physical_network, - vlan_id) - # no reservation needed for TYPE_LOCAL - net = super(LinuxBridgePluginV2, self).create_network(context, - network) - db.add_network_binding(session, net['id'], - physical_network, vlan_id) - self._process_l3_create(context, net, network['network']) - self._extend_network_dict_provider(context, net) - # note - exception will rollback entire transaction - return net - - def update_network(self, context, id, network): - provider._raise_if_updates_provider_attributes(network['network']) - - session = context.session - with session.begin(subtransactions=True): - net = super(LinuxBridgePluginV2, self).update_network(context, id, - network) - self._process_l3_update(context, net, network['network']) - self._extend_network_dict_provider(context, net) - return net - - def delete_network(self, context, id): - session = context.session - with session.begin(subtransactions=True): - binding = db.get_network_binding(session, id) - self._process_l3_delete(context, id) - super(LinuxBridgePluginV2, self).delete_network(context, id) - if binding.vlan_id != constants.LOCAL_VLAN_ID: - db.release_network(session, binding.physical_network, - binding.vlan_id, self.network_vlan_ranges) - # the network_binding record is deleted via cascade from - # the network record, so explicit removal is not necessary - self.notifier.network_delete(context, id) - - def get_network(self, context, id, fields=None): - session = context.session - with session.begin(subtransactions=True): - net = super(LinuxBridgePluginV2, self).get_network(context, - id, None) - self._extend_network_dict_provider(context, net) - return self._fields(net, fields) - - def get_networks(self, context, filters=None, fields=None, - sorts=None, limit=None, marker=None, page_reverse=False): - session = context.session - with session.begin(subtransactions=True): - nets = super(LinuxBridgePluginV2, - self).get_networks(context, filters, None, sorts, - limit, marker, page_reverse) - for net in nets: - self._extend_network_dict_provider(context, net) - - return [self._fields(net, fields) for net in nets] - - def create_port(self, context, port): - session = context.session - port_data = port['port'] - with session.begin(subtransactions=True): - self._ensure_default_security_group_on_port(context, port) - sgids = self._get_security_groups_on_port(context, port) - # Set port status as 'DOWN'. This will be updated by agent - port['port']['status'] = q_const.PORT_STATUS_DOWN - - port = super(LinuxBridgePluginV2, - self).create_port(context, port) - self._process_portbindings_create_and_update(context, - port_data, - port) - self._process_port_create_security_group( - context, port, sgids) - self.notify_security_groups_member_updated(context, port) - return port - - def update_port(self, context, id, port): - original_port = self.get_port(context, id) - session = context.session - need_port_update_notify = False - - with session.begin(subtransactions=True): - updated_port = super(LinuxBridgePluginV2, self).update_port( - context, id, port) - self._process_portbindings_create_and_update(context, - port['port'], - updated_port) - need_port_update_notify = self.update_security_group_on_port( - context, id, port, original_port, updated_port) - - need_port_update_notify |= self.is_security_group_member_updated( - context, original_port, updated_port) - - if original_port['admin_state_up'] != updated_port['admin_state_up']: - need_port_update_notify = True - - if need_port_update_notify: - self._notify_port_updated(context, updated_port) - return updated_port - - def delete_port(self, context, id, l3_port_check=True): - - # if needed, check to see if this is a port owned by - # and l3-router. If so, we should prevent deletion. - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - - session = context.session - with session.begin(subtransactions=True): - self.disassociate_floatingips(context, id) - port = self.get_port(context, id) - self._delete_port_security_group_bindings(context, id) - super(LinuxBridgePluginV2, self).delete_port(context, id) - - self.notify_security_groups_member_updated(context, port) - - def _notify_port_updated(self, context, port): - binding = db.get_network_binding(context.session, - port['network_id']) - self.notifier.port_update(context, port, - binding.physical_network, - binding.vlan_id) diff --git a/neutron/plugins/metaplugin/README b/neutron/plugins/metaplugin/README deleted file mode 100644 index 8dbc47756..000000000 --- a/neutron/plugins/metaplugin/README +++ /dev/null @@ -1,92 +0,0 @@ -# -- 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. - - - diff --git a/neutron/plugins/metaplugin/__init__.py b/neutron/plugins/metaplugin/__init__.py deleted file mode 100644 index d8bce7745..000000000 --- a/neutron/plugins/metaplugin/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# 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. diff --git a/neutron/plugins/metaplugin/common/__init__.py b/neutron/plugins/metaplugin/common/__init__.py deleted file mode 100644 index d8bce7745..000000000 --- a/neutron/plugins/metaplugin/common/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# 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. diff --git a/neutron/plugins/metaplugin/common/config.py b/neutron/plugins/metaplugin/common/config.py deleted file mode 100644 index 26978d71b..000000000 --- a/neutron/plugins/metaplugin/common/config.py +++ /dev/null @@ -1,80 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# 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 oslo.config import cfg - - -meta_plugin_opts = [ - cfg.StrOpt( - 'plugin_list', - default='', - help=_("Comma separated list of flavor:neutron_plugin for " - "plugins to load. Extension method is searched in the " - "list order and the first one is used.")), - cfg.StrOpt( - 'l3_plugin_list', - default='', - help=_("Comma separated list of flavor:neutron_plugin for L3 " - "service plugins to load. This is intended for specifying " - "L2 plugins which support L3 functions. If you use a router " - "service plugin, set this blank.")), - cfg.StrOpt( - 'default_flavor', - default='', - help=_("Default flavor to use, when flavor:network is not " - "specified at network creation.")), - cfg.StrOpt( - 'default_l3_flavor', - default='', - help=_("Default L3 flavor to use, when flavor:router is not " - "specified at router creation. Ignored if 'l3_plugin_list' " - "is blank.")), - cfg.StrOpt( - 'supported_extension_aliases', - default='', - help=_("Comma separated list of supported extension aliases.")), - cfg.StrOpt( - 'extension_map', - default='', - help=_("Comma separated list of method:flavor to select specific " - "plugin for a method. This has priority over method search " - "order based on 'plugin_list'.")), - cfg.StrOpt( - 'rpc_flavor', - default='', - help=_("Specifies flavor for plugin to handle 'q-plugin' RPC " - "requests.")), -] - -proxy_plugin_opts = [ - cfg.StrOpt('admin_user', - help=_("Admin user")), - cfg.StrOpt('admin_password', - help=_("Admin password"), - secret=True), - cfg.StrOpt('admin_tenant_name', - help=_("Admin tenant name")), - cfg.StrOpt('auth_url', - help=_("Authentication URL")), - cfg.StrOpt('auth_strategy', default='keystone', - help=_("The type of authentication to use")), - cfg.StrOpt('auth_region', - help=_("Authentication region")), -] - -cfg.CONF.register_opts(meta_plugin_opts, "META") -cfg.CONF.register_opts(proxy_plugin_opts, "PROXY") diff --git a/neutron/plugins/metaplugin/meta_db_v2.py b/neutron/plugins/metaplugin/meta_db_v2.py deleted file mode 100644 index 68c9055ff..000000000 --- a/neutron/plugins/metaplugin/meta_db_v2.py +++ /dev/null @@ -1,52 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# 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_models_v2.py b/neutron/plugins/metaplugin/meta_models_v2.py deleted file mode 100644 index 566d1d8d8..000000000 --- a/neutron/plugins/metaplugin/meta_models_v2.py +++ /dev/null @@ -1,43 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# 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 sqlalchemy as sa -from sqlalchemy import Column, String - -from neutron.db import models_v2 - - -class NetworkFlavor(models_v2.model_base.BASEV2): - """Represents a binding of network_id to flavor.""" - flavor = Column(String(255)) - network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', - ondelete="CASCADE"), - primary_key=True) - - def __repr__(self): - return "" % (self.flavor, self.network_id) - - -class RouterFlavor(models_v2.model_base.BASEV2): - """Represents a binding of router_id to flavor.""" - flavor = Column(String(255)) - router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id', - ondelete="CASCADE"), - primary_key=True) - - def __repr__(self): - return "" % (self.flavor, self.router_id) diff --git a/neutron/plugins/metaplugin/meta_neutron_plugin.py b/neutron/plugins/metaplugin/meta_neutron_plugin.py deleted file mode 100644 index 92a962846..000000000 --- a/neutron/plugins/metaplugin/meta_neutron_plugin.py +++ /dev/null @@ -1,419 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 oslo.config import cfg - -from neutron.common import exceptions as exc -from neutron.common import topics -from neutron import context as neutron_context -from neutron.db import api as db -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.openstack.common import importutils -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 - - -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 - - # Keep existing tables if multiple plugin use same table name. - db.model_base.NeutronBase.__table_args__ = {'keep_existing': True} - - 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(_('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 - not flavor 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(_('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] diff --git a/neutron/plugins/metaplugin/proxy_neutron_plugin.py b/neutron/plugins/metaplugin/proxy_neutron_plugin.py deleted file mode 100644 index 61cc34026..000000000 --- a/neutron/plugins/metaplugin/proxy_neutron_plugin.py +++ /dev/null @@ -1,136 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 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.openstack.common import log as logging -from neutronclient.common import exceptions -from neutronclient.v2_0 import client - - -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(_("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(_("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(_("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(_("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(_("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(_("Port in remote have already deleted")) - return super(ProxyPluginV2, self).delete_port(context, id) diff --git a/neutron/plugins/midonet/__init__.py b/neutron/plugins/midonet/__init__.py deleted file mode 100644 index 439ff6594..000000000 --- a/neutron/plugins/midonet/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012 Midokura Japan K.K. -# Copyright (C) 2013 Midokura PTE LTD -# 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. diff --git a/neutron/plugins/midonet/agent/__init__.py b/neutron/plugins/midonet/agent/__init__.py deleted file mode 100644 index 9fddc1976..000000000 --- a/neutron/plugins/midonet/agent/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2013 Midokura PTE LTD -# 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. diff --git a/neutron/plugins/midonet/agent/midonet_driver.py b/neutron/plugins/midonet/agent/midonet_driver.py deleted file mode 100644 index ada98a3d1..000000000 --- a/neutron/plugins/midonet/agent/midonet_driver.py +++ /dev/null @@ -1,52 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2013 Midokura PTE LTD -# 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. -# -# @author: Rossella Sblendido, Midokura Japan KK -# @author: Tomoe Sugihara, Midokura Japan KK -# @author: Ryu Ishimoto, Midokura Japan KK - -from neutron.agent.linux import dhcp -from neutron.openstack.common import log as logging -from neutron.plugins.midonet.common import config # noqa - -LOG = logging.getLogger(__name__) - - -class DhcpNoOpDriver(dhcp.DhcpLocalProcess): - - @classmethod - def existing_dhcp_networks(cls, conf, root_helper): - """Return a list of existing networks ids that we have configs for.""" - return [] - - @classmethod - def check_version(cls): - """Execute version checks on DHCP server.""" - return float(1.0) - - def disable(self, retain_port=False): - """Disable DHCP for this network.""" - if not retain_port: - self.device_manager.destroy(self.network, self.interface_name) - self._remove_config_files() - - def reload_allocations(self): - """Force the DHCP server to reload the assignment database.""" - pass - - def spawn_process(self): - pass diff --git a/neutron/plugins/midonet/common/__init__.py b/neutron/plugins/midonet/common/__init__.py deleted file mode 100644 index 9fddc1976..000000000 --- a/neutron/plugins/midonet/common/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2013 Midokura PTE LTD -# 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. diff --git a/neutron/plugins/midonet/common/config.py b/neutron/plugins/midonet/common/config.py deleted file mode 100644 index 924474f5b..000000000 --- a/neutron/plugins/midonet/common/config.py +++ /dev/null @@ -1,46 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012 Midokura Japan K.K. -# Copyright (C) 2013 Midokura PTE LTD -# 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. -# -# @author: Tomoe Sugihara, Midokura Japan KK - -from oslo.config import cfg - -midonet_opts = [ - cfg.StrOpt('midonet_uri', default='http://localhost:8080/midonet-api', - help=_('MidoNet API server URI.')), - cfg.StrOpt('username', default='admin', - help=_('MidoNet admin username.')), - cfg.StrOpt('password', default='passw0rd', - secret=True, - help=_('MidoNet admin password.')), - cfg.StrOpt('project_id', - default='77777777-7777-7777-7777-777777777777', - help=_('ID of the project that MidoNet admin user' - 'belongs to.')), - cfg.StrOpt('provider_router_id', - help=_('Virtual provider router ID.')), - cfg.StrOpt('mode', - default='dev', - help=_('Operational mode. Internal dev use only.')), - cfg.StrOpt('midonet_host_uuid_path', - default='/etc/midolman/host_uuid.properties', - help=_('Path to midonet host uuid file')) -] - - -cfg.CONF.register_opts(midonet_opts, "MIDONET") diff --git a/neutron/plugins/midonet/common/net_util.py b/neutron/plugins/midonet/common/net_util.py deleted file mode 100644 index 884048675..000000000 --- a/neutron/plugins/midonet/common/net_util.py +++ /dev/null @@ -1,68 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2013 Midokura PTE LTD -# 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. -# -# @author: Ryu Ishimoto, Midokura Japan KK - - -from neutron.common import constants - - -def subnet_str(cidr): - """Convert the cidr string to x.x.x.x_y format - - :param cidr: CIDR in x.x.x.x/y format - """ - if cidr is None: - return None - return cidr.replace("/", "_") - - -def net_addr(addr): - """Get network address prefix and length from a given address.""" - if addr is None: - return (None, None) - nw_addr, nw_len = addr.split('/') - nw_len = int(nw_len) - return nw_addr, nw_len - - -def get_ethertype_value(ethertype): - """Convert string representation of ethertype to the numerical.""" - if ethertype is None: - return None - mapping = { - 'ipv4': 0x0800, - 'ipv6': 0x86DD, - 'arp': 0x806 - } - return mapping.get(ethertype.lower()) - - -def get_protocol_value(protocol): - """Convert string representation of protocol to the numerical.""" - if protocol is None: - return None - - if isinstance(protocol, int): - return protocol - - mapping = { - constants.PROTO_NAME_TCP: constants.PROTO_NUM_TCP, - constants.PROTO_NAME_UDP: constants.PROTO_NUM_UDP, - constants.PROTO_NAME_ICMP: constants.PROTO_NUM_ICMP - } - return mapping.get(protocol.lower()) diff --git a/neutron/plugins/midonet/midonet_lib.py b/neutron/plugins/midonet/midonet_lib.py deleted file mode 100644 index 74d2bae6a..000000000 --- a/neutron/plugins/midonet/midonet_lib.py +++ /dev/null @@ -1,696 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012 Midokura Japan K.K. -# Copyright (C) 2013 Midokura PTE LTD -# 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. -# -# @author: Tomoe Sugihara, Midokura Japan KK -# @author: Ryu Ishimoto, Midokura Japan KK -# @author: Rossella Sblendido, Midokura Japan KK -# @author: Duarte Nunes, Midokura Japan KK - -from midonetclient import exc -from webob import exc as w_exc - -from neutron.common import exceptions as n_exc -from neutron.openstack.common import log as logging -from neutron.plugins.midonet.common import net_util - -LOG = logging.getLogger(__name__) - - -def handle_api_error(fn): - """Wrapper for methods that throws custom exceptions.""" - def wrapped(*args, **kwargs): - try: - return fn(*args, **kwargs) - except (w_exc.HTTPException, - exc.MidoApiConnectionError) as ex: - raise MidonetApiException(msg=ex) - return wrapped - - -class MidonetResourceNotFound(n_exc.NotFound): - message = _('MidoNet %(resource_type)s %(id)s could not be found') - - -class MidonetApiException(n_exc.NeutronException): - message = _("MidoNet API error: %(msg)s") - - -class MidoClient: - - def __init__(self, mido_api): - self.mido_api = mido_api - - @classmethod - def _fill_dto(cls, dto, fields): - for field_name, field_value in fields.iteritems(): - # We assume the setters are named the - # same way as the attributes themselves. - try: - getattr(dto, field_name)(field_value) - except AttributeError: - pass - return dto - - @classmethod - def _create_dto(cls, dto, fields): - return cls._fill_dto(dto, fields).create() - - @classmethod - def _update_dto(cls, dto, fields): - return cls._fill_dto(dto, fields).update() - - @handle_api_error - def create_bridge(self, **kwargs): - """Create a new bridge - - :param \**kwargs: configuration of the new bridge - :returns: newly created bridge - """ - LOG.debug(_("MidoClient.create_bridge called: " - "kwargs=%(kwargs)s"), {'kwargs': kwargs}) - return self._create_dto(self.mido_api.add_bridge(), kwargs) - - @handle_api_error - def delete_bridge(self, id): - """Delete a bridge - - :param id: id of the bridge - """ - LOG.debug(_("MidoClient.delete_bridge called: id=%(id)s"), {'id': id}) - return self.mido_api.delete_bridge(id) - - @handle_api_error - def get_bridge(self, id): - """Get a bridge - - :param id: id of the bridge - :returns: requested bridge. None if bridge does not exist. - """ - LOG.debug(_("MidoClient.get_bridge called: id=%s"), id) - try: - return self.mido_api.get_bridge(id) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Bridge', id=id) - - @handle_api_error - def update_bridge(self, id, **kwargs): - """Update a bridge of the given id with the new fields - - :param id: id of the bridge - :param \**kwargs: the fields to update and their values - :returns: bridge object - """ - LOG.debug(_("MidoClient.update_bridge called: " - "id=%(id)s, kwargs=%(kwargs)s"), - {'id': id, 'kwargs': kwargs}) - try: - return self._update_dto(self.mido_api.get_bridge(id), kwargs) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Bridge', id=id) - - @handle_api_error - def create_dhcp(self, bridge, gateway_ip, cidr, host_rts=None, - dns_servers=None): - """Create a new DHCP entry - - :param bridge: bridge object to add dhcp to - :param gateway_ip: IP address of gateway - :param cidr: subnet represented as x.x.x.x/y - :param host_rts: list of routes set in the host - :param dns_servers: list of dns servers - :returns: newly created dhcp - """ - LOG.debug(_("MidoClient.create_dhcp called: bridge=%(bridge)s, " - "cidr=%(cidr)s, gateway_ip=%(gateway_ip)s, " - "host_rts=%(host_rts)s, dns_servers=%(dns_servers)s"), - {'bridge': bridge, 'cidr': cidr, 'gateway_ip': gateway_ip, - 'host_rts': host_rts, 'dns_servers': dns_servers}) - self.mido_api.add_bridge_dhcp(bridge, gateway_ip, cidr, - host_rts=host_rts, - dns_nservers=dns_servers) - - @handle_api_error - def add_dhcp_host(self, bridge, cidr, ip, mac): - """Add DHCP host entry - - :param bridge: bridge the DHCP is configured for - :param cidr: subnet represented as x.x.x.x/y - :param ip: IP address - :param mac: MAC address - """ - LOG.debug(_("MidoClient.add_dhcp_host called: bridge=%(bridge)s, " - "cidr=%(cidr)s, ip=%(ip)s, mac=%(mac)s"), - {'bridge': bridge, 'cidr': cidr, 'ip': ip, 'mac': mac}) - subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr)) - if subnet is None: - raise MidonetApiException(msg=_("Tried to add to" - "non-existent DHCP")) - - subnet.add_dhcp_host().ip_addr(ip).mac_addr(mac).create() - - @handle_api_error - def remove_dhcp_host(self, bridge, cidr, ip, mac): - """Remove DHCP host entry - - :param bridge: bridge the DHCP is configured for - :param cidr: subnet represented as x.x.x.x/y - :param ip: IP address - :param mac: MAC address - """ - LOG.debug(_("MidoClient.remove_dhcp_host called: bridge=%(bridge)s, " - "cidr=%(cidr)s, ip=%(ip)s, mac=%(mac)s"), - {'bridge': bridge, 'cidr': cidr, 'ip': ip, 'mac': mac}) - subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr)) - if subnet is None: - LOG.warn(_("Tried to delete mapping from non-existent subnet")) - return - - for dh in subnet.get_dhcp_hosts(): - if dh.get_mac_addr() == mac and dh.get_ip_addr() == ip: - LOG.debug(_("MidoClient.remove_dhcp_host: Deleting %(dh)r"), - {"dh": dh}) - dh.delete() - - @handle_api_error - def delete_dhcp_host(self, bridge_id, cidr, ip, mac): - """Delete DHCP host entry - - :param bridge_id: id of the bridge of the DHCP - :param cidr: subnet represented as x.x.x.x/y - :param ip: IP address - :param mac: MAC address - """ - LOG.debug(_("MidoClient.delete_dhcp_host called: " - "bridge_id=%(bridge_id)s, cidr=%(cidr)s, ip=%(ip)s, " - "mac=%(mac)s"), {'bridge_id': bridge_id, - 'cidr': cidr, - 'ip': ip, 'mac': mac}) - bridge = self.get_bridge(bridge_id) - self.remove_dhcp_host(bridge, net_util.subnet_str(cidr), ip, mac) - - @handle_api_error - def delete_dhcp(self, bridge, cidr): - """Delete a DHCP entry - - :param bridge: bridge to remove DHCP from - :param cidr: subnet represented as x.x.x.x/y - """ - LOG.debug(_("MidoClient.delete_dhcp called: bridge=%(bridge)s, " - "cidr=%(cidr)s"), - {'bridge': bridge, 'cidr': cidr}) - dhcp_subnets = bridge.get_dhcp_subnets() - net_addr, net_len = net_util.net_addr(cidr) - if not dhcp_subnets: - raise MidonetApiException( - msg=_("Tried to delete non-existent DHCP")) - for dhcp in dhcp_subnets: - if dhcp.get_subnet_prefix() == net_addr: - dhcp.delete() - break - - @handle_api_error - def delete_port(self, id, delete_chains=False): - """Delete a port - - :param id: id of the port - """ - LOG.debug(_("MidoClient.delete_port called: id=%(id)s, " - "delete_chains=%(delete_chains)s"), - {'id': id, 'delete_chains': delete_chains}) - if delete_chains: - self.delete_port_chains(id) - - self.mido_api.delete_port(id) - - @handle_api_error - def get_port(self, id): - """Get a port - - :param id: id of the port - :returns: requested port. None if it does not exist - """ - LOG.debug(_("MidoClient.get_port called: id=%(id)s"), {'id': id}) - try: - return self.mido_api.get_port(id) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Port', id=id) - - @handle_api_error - def add_bridge_port(self, bridge, **kwargs): - """Add a port on a bridge - - :param bridge: bridge to add a new port to - :param \**kwargs: configuration of the new port - :returns: newly created port - """ - LOG.debug(_("MidoClient.add_bridge_port called: " - "bridge=%(bridge)s, kwargs=%(kwargs)s"), - {'bridge': bridge, 'kwargs': kwargs}) - return self._create_dto(self.mido_api.add_bridge_port(bridge), kwargs) - - @handle_api_error - def update_port(self, id, **kwargs): - """Update a port of the given id with the new fields - - :param id: id of the port - :param \**kwargs: the fields to update and their values - """ - LOG.debug(_("MidoClient.update_port called: " - "id=%(id)s, kwargs=%(kwargs)s"), - {'id': id, 'kwargs': kwargs}) - try: - return self._update_dto(self.mido_api.get_port(id), kwargs) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Port', id=id) - - @handle_api_error - def add_router_port(self, router, **kwargs): - """Add a new port to an existing router. - - :param router: router to add a new port to - :param \**kwargs: configuration of the new port - :returns: newly created port - """ - return self._create_dto(self.mido_api.add_router_port(router), kwargs) - - @handle_api_error - def create_router(self, **kwargs): - """Create a new router - - :param \**kwargs: configuration of the new router - :returns: newly created router - """ - LOG.debug(_("MidoClient.create_router called: " - "kwargs=%(kwargs)s"), {'kwargs': kwargs}) - return self._create_dto(self.mido_api.add_router(), kwargs) - - @handle_api_error - def delete_router(self, id): - """Delete a router - - :param id: id of the router - """ - LOG.debug(_("MidoClient.delete_router called: id=%(id)s"), {'id': id}) - return self.mido_api.delete_router(id) - - @handle_api_error - def get_router(self, id): - """Get a router with the given id - - :param id: id of the router - :returns: requested router object. None if it does not exist. - """ - LOG.debug(_("MidoClient.get_router called: id=%(id)s"), {'id': id}) - try: - return self.mido_api.get_router(id) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Router', id=id) - - @handle_api_error - def update_router(self, id, **kwargs): - """Update a router of the given id with the new name - - :param id: id of the router - :param \**kwargs: the fields to update and their values - :returns: router object - """ - LOG.debug(_("MidoClient.update_router called: " - "id=%(id)s, kwargs=%(kwargs)s"), - {'id': id, 'kwargs': kwargs}) - try: - return self._update_dto(self.mido_api.get_router(id), kwargs) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Router', id=id) - - @handle_api_error - def delete_route(self, id): - return self.mido_api.delete_route(id) - - @handle_api_error - def add_dhcp_route_option(self, bridge, cidr, gw_ip, dst_ip): - """Add Option121 route to subnet - - :param bridge: Bridge to add the option route to - :param cidr: subnet represented as x.x.x.x/y - :param gw_ip: IP address of the next hop - :param dst_ip: IP address of the destination, in x.x.x.x/y format - """ - LOG.debug(_("MidoClient.add_dhcp_route_option called: " - "bridge=%(bridge)s, cidr=%(cidr)s, gw_ip=%(gw_ip)s" - "dst_ip=%(dst_ip)s"), - {"bridge": bridge, "cidr": cidr, "gw_ip": gw_ip, - "dst_ip": dst_ip}) - subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr)) - if subnet is None: - raise MidonetApiException( - msg=_("Tried to access non-existent DHCP")) - prefix, length = dst_ip.split("/") - routes = [{'destinationPrefix': prefix, 'destinationLength': length, - 'gatewayAddr': gw_ip}] - cur_routes = subnet.get_opt121_routes() - if cur_routes: - routes = routes + cur_routes - subnet.opt121_routes(routes).update() - - @handle_api_error - def link(self, port, peer_id): - """Link a port to a given peerId.""" - self.mido_api.link(port, peer_id) - - @handle_api_error - def delete_port_routes(self, routes, port_id): - """Remove routes whose next hop port is the given port ID.""" - for route in routes: - if route.get_next_hop_port() == port_id: - self.mido_api.delete_route(route.get_id()) - - @handle_api_error - def get_router_routes(self, router_id): - """Get all routes for the given router.""" - return self.mido_api.get_router_routes(router_id) - - @handle_api_error - def unlink(self, port): - """Unlink a port - - :param port: port object - """ - LOG.debug(_("MidoClient.unlink called: port=%(port)s"), - {'port': port}) - if port.get_peer_id(): - self.mido_api.unlink(port) - else: - LOG.warn(_("Attempted to unlink a port that was not linked. %s"), - port.get_id()) - - @handle_api_error - def remove_rules_by_property(self, tenant_id, chain_name, key, value): - """Remove all the rules that match the provided key and value.""" - LOG.debug(_("MidoClient.remove_rules_by_property called: " - "tenant_id=%(tenant_id)s, chain_name=%(chain_name)s" - "key=%(key)s, value=%(value)s"), - {'tenant_id': tenant_id, 'chain_name': chain_name, - 'key': key, 'value': value}) - chain = self.get_chain_by_name(tenant_id, chain_name) - if chain is None: - raise MidonetResourceNotFound(resource_type='Chain', - id=chain_name) - - for r in chain.get_rules(): - if key in r.get_properties(): - if r.get_properties()[key] == value: - self.mido_api.delete_rule(r.get_id()) - - @handle_api_error - def add_router_chains(self, router, inbound_chain_name, - outbound_chain_name): - """Create chains for a new router. - - Creates inbound and outbound chains for the router with the given - names, and the new chains are set on the router. - - :param router: router to set chains for - :param inbound_chain_name: Name of the inbound chain - :param outbound_chain_name: Name of the outbound chain - """ - LOG.debug(_("MidoClient.create_router_chains called: " - "router=%(router)s, inbound_chain_name=%(in_chain)s, " - "outbound_chain_name=%(out_chain)s"), - {"router": router, "in_chain": inbound_chain_name, - "out_chain": outbound_chain_name}) - tenant_id = router.get_tenant_id() - - inbound_chain = self.mido_api.add_chain().tenant_id(tenant_id).name( - inbound_chain_name,).create() - outbound_chain = self.mido_api.add_chain().tenant_id(tenant_id).name( - outbound_chain_name).create() - - # set chains to in/out filters - router.inbound_filter_id(inbound_chain.get_id()).outbound_filter_id( - outbound_chain.get_id()).update() - return inbound_chain, outbound_chain - - @handle_api_error - def delete_router_chains(self, id): - """Deletes chains of a router. - - :param id: router ID to delete chains of - """ - LOG.debug(_("MidoClient.delete_router_chains called: " - "id=%(id)s"), {'id': id}) - router = self.get_router(id) - if (router.get_inbound_filter_id()): - self.mido_api.delete_chain(router.get_inbound_filter_id()) - - if (router.get_outbound_filter_id()): - self.mido_api.delete_chain(router.get_outbound_filter_id()) - - @handle_api_error - def delete_port_chains(self, id): - """Deletes chains of a port. - - :param id: port ID to delete chains of - """ - LOG.debug(_("MidoClient.delete_port_chains called: " - "id=%(id)s"), {'id': id}) - port = self.get_port(id) - if (port.get_inbound_filter_id()): - self.mido_api.delete_chain(port.get_inbound_filter_id()) - - if (port.get_outbound_filter_id()): - self.mido_api.delete_chain(port.get_outbound_filter_id()) - - @handle_api_error - def get_link_port(self, router, peer_router_id): - """Setup a route on the router to the next hop router.""" - LOG.debug(_("MidoClient.get_link_port called: " - "router=%(router)s, peer_router_id=%(peer_router_id)s"), - {'router': router, 'peer_router_id': peer_router_id}) - # Find the port linked between the two routers - link_port = None - for p in router.get_peer_ports(): - if p.get_device_id() == peer_router_id: - link_port = p - break - return link_port - - @handle_api_error - def add_router_route(self, router, type='Normal', - src_network_addr=None, src_network_length=None, - dst_network_addr=None, dst_network_length=None, - next_hop_port=None, next_hop_gateway=None, - weight=100): - """Setup a route on the router.""" - return self.mido_api.add_router_route( - router, type=type, src_network_addr=src_network_addr, - src_network_length=src_network_length, - dst_network_addr=dst_network_addr, - dst_network_length=dst_network_length, - next_hop_port=next_hop_port, next_hop_gateway=next_hop_gateway, - weight=weight) - - @handle_api_error - def add_static_nat(self, tenant_id, chain_name, from_ip, to_ip, port_id, - nat_type='dnat', **kwargs): - """Add a static NAT entry - - :param tenant_id: owner fo the chain to add a NAT to - :param chain_name: name of the chain to add a NAT to - :param from_ip: IP to translate from - :param from_ip: IP to translate from - :param to_ip: IP to translate to - :param port_id: port to match on - :param nat_type: 'dnat' or 'snat' - """ - LOG.debug(_("MidoClient.add_static_nat called: " - "tenant_id=%(tenant_id)s, chain_name=%(chain_name)s, " - "from_ip=%(from_ip)s, to_ip=%(to_ip)s, " - "port_id=%(port_id)s, nat_type=%(nat_type)s"), - {'tenant_id': tenant_id, 'chain_name': chain_name, - 'from_ip': from_ip, 'to_ip': to_ip, - 'portid': port_id, 'nat_type': nat_type}) - if nat_type not in ['dnat', 'snat']: - raise ValueError(_("Invalid NAT type passed in %s") % nat_type) - - chain = self.get_chain_by_name(tenant_id, chain_name) - nat_targets = [] - nat_targets.append( - {'addressFrom': to_ip, 'addressTo': to_ip, - 'portFrom': 0, 'portTo': 0}) - - rule = chain.add_rule().type(nat_type).flow_action('accept').position( - 1).nat_targets(nat_targets).properties(kwargs) - - if nat_type == 'dnat': - rule = rule.nw_dst_address(from_ip).nw_dst_length(32).in_ports( - [port_id]) - else: - rule = rule.nw_src_address(from_ip).nw_src_length(32).out_ports( - [port_id]) - - return rule.create() - - @handle_api_error - def add_dynamic_snat(self, tenant_id, pre_chain_name, post_chain_name, - snat_ip, port_id, **kwargs): - """Add SNAT masquerading rule - - MidoNet requires two rules on the router, one to do NAT to a range of - ports, and another to retrieve back the original IP in the return - flow. - """ - pre_chain = self.get_chain_by_name(tenant_id, pre_chain_name) - post_chain = self.get_chain_by_name(tenant_id, post_chain_name) - - pre_chain.add_rule().nw_dst_address(snat_ip).nw_dst_length( - 32).type('rev_snat').flow_action('accept').in_ports( - [port_id]).properties(kwargs).position(1).create() - - nat_targets = [] - nat_targets.append( - {'addressFrom': snat_ip, 'addressTo': snat_ip, - 'portFrom': 1, 'portTo': 65535}) - - post_chain.add_rule().type('snat').flow_action( - 'accept').nat_targets(nat_targets).out_ports( - [port_id]).properties(kwargs).position(1).create() - - @handle_api_error - def remove_static_route(self, router, ip): - """Remove static route for the IP - - :param router: next hop router to remove the routes to - :param ip: IP address of the route to remove - """ - LOG.debug(_("MidoClient.remote_static_route called: " - "router=%(router)s, ip=%(ip)s"), - {'router': router, 'ip': ip}) - for r in router.get_routes(): - if (r.get_dst_network_addr() == ip and - r.get_dst_network_length() == 32): - self.mido_api.delete_route(r.get_id()) - - @handle_api_error - def update_port_chains(self, port, inbound_chain_id, outbound_chain_id): - """Bind inbound and outbound chains to the port.""" - LOG.debug(_("MidoClient.update_port_chains called: port=%(port)s" - "inbound_chain_id=%(inbound_chain_id)s, " - "outbound_chain_id=%(outbound_chain_id)s"), - {"port": port, "inbound_chain_id": inbound_chain_id, - "outbound_chain_id": outbound_chain_id}) - port.inbound_filter_id(inbound_chain_id).outbound_filter_id( - outbound_chain_id).update() - - @handle_api_error - def create_chain(self, tenant_id, name): - """Create a new chain.""" - LOG.debug(_("MidoClient.create_chain called: tenant_id=%(tenant_id)s " - " name=%(name)s"), {"tenant_id": tenant_id, "name": name}) - return self.mido_api.add_chain().tenant_id(tenant_id).name( - name).create() - - @handle_api_error - def delete_chain(self, id): - """Delete chain matching the ID.""" - LOG.debug(_("MidoClient.delete_chain called: id=%(id)s"), {"id": id}) - self.mido_api.delete_chain(id) - - @handle_api_error - def delete_chains_by_names(self, tenant_id, names): - """Delete chains matching the names given for a tenant.""" - LOG.debug(_("MidoClient.delete_chains_by_names called: " - "tenant_id=%(tenant_id)s names=%(names)s "), - {"tenant_id": tenant_id, "names": names}) - chains = self.mido_api.get_chains({'tenant_id': tenant_id}) - for c in chains: - if c.get_name() in names: - self.mido_api.delete_chain(c.get_id()) - - @handle_api_error - def get_chain_by_name(self, tenant_id, name): - """Get the chain by its name.""" - LOG.debug(_("MidoClient.get_chain_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s "), - {"tenant_id": tenant_id, "name": name}) - for c in self.mido_api.get_chains({'tenant_id': tenant_id}): - if c.get_name() == name: - return c - return None - - @handle_api_error - def get_port_group_by_name(self, tenant_id, name): - """Get the port group by name.""" - LOG.debug(_("MidoClient.get_port_group_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s "), - {"tenant_id": tenant_id, "name": name}) - for p in self.mido_api.get_port_groups({'tenant_id': tenant_id}): - if p.get_name() == name: - return p - return None - - @handle_api_error - def create_port_group(self, tenant_id, name): - """Create a port group - - Create a new port group for a given name and ID. - """ - LOG.debug(_("MidoClient.create_port_group called: " - "tenant_id=%(tenant_id)s name=%(name)s"), - {"tenant_id": tenant_id, "name": name}) - return self.mido_api.add_port_group().tenant_id(tenant_id).name( - name).create() - - @handle_api_error - def delete_port_group_by_name(self, tenant_id, name): - """Delete port group matching the name given for a tenant.""" - LOG.debug(_("MidoClient.delete_port_group_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s "), - {"tenant_id": tenant_id, "name": name}) - pgs = self.mido_api.get_port_groups({'tenant_id': tenant_id}) - for pg in pgs: - if pg.get_name() == name: - LOG.debug(_("Deleting pg %(id)s"), {"id": pg.get_id()}) - self.mido_api.delete_port_group(pg.get_id()) - - @handle_api_error - def add_port_to_port_group_by_name(self, tenant_id, name, port_id): - """Add a port to a port group with the given name.""" - LOG.debug(_("MidoClient.add_port_to_port_group_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s " - "port_id=%(port_id)s"), - {"tenant_id": tenant_id, "name": name, "port_id": port_id}) - pg = self.get_port_group_by_name(tenant_id, name) - if pg is None: - raise MidonetResourceNotFound(resource_type='PortGroup', id=name) - - pg = pg.add_port_group_port().port_id(port_id).create() - return pg - - @handle_api_error - def remove_port_from_port_groups(self, port_id): - """Remove a port binding from all the port groups.""" - LOG.debug(_("MidoClient.remove_port_from_port_groups called: " - "port_id=%(port_id)s"), {"port_id": port_id}) - port = self.get_port(port_id) - for pg in port.get_port_groups(): - pg.delete() - - @handle_api_error - def add_chain_rule(self, chain, action='accept', **kwargs): - """Create a new accept chain rule.""" - self.mido_api.add_chain_rule(chain, action, **kwargs) diff --git a/neutron/plugins/midonet/plugin.py b/neutron/plugins/midonet/plugin.py deleted file mode 100644 index 9a706d4a5..000000000 --- a/neutron/plugins/midonet/plugin.py +++ /dev/null @@ -1,1258 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012 Midokura Japan K.K. -# Copyright (C) 2013 Midokura PTE LTD -# 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. -# -# @author: Takaaki Suzuki, Midokura Japan KK -# @author: Tomoe Sugihara, Midokura Japan KK -# @author: Ryu Ishimoto, Midokura Japan KK -# @author: Rossella Sblendido, Midokura Japan KK -# @author: Duarte Nunes, Midokura Japan KK - -from midonetclient import api -from oslo.config import cfg -from sqlalchemy.orm import exc as sa_exc - -from neutron.api.v2 import attributes -from neutron.common import constants -from neutron.common import exceptions as n_exc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.db import agents_db -from neutron.db import agentschedulers_db -from neutron.db import db_base_plugin_v2 -from neutron.db import dhcp_rpc_base -from neutron.db import external_net_db -from neutron.db import l3_db -from neutron.db import models_v2 -from neutron.db import portbindings_db -from neutron.db import securitygroups_db -from neutron.extensions import external_net as ext_net -from neutron.extensions import l3 -from neutron.extensions import portbindings -from neutron.extensions import securitygroup as ext_sg -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.midonet.common import config # noqa -from neutron.plugins.midonet.common import net_util -from neutron.plugins.midonet import midonet_lib - -LOG = logging.getLogger(__name__) - -EXTERNAL_GW_INFO = l3.EXTERNAL_GW_INFO - -METADATA_DEFAULT_IP = "169.254.169.254/32" -OS_FLOATING_IP_RULE_KEY = 'OS_FLOATING_IP' -OS_SG_RULE_KEY = 'OS_SG_RULE_ID' -OS_TENANT_ROUTER_RULE_KEY = 'OS_TENANT_ROUTER_RULE' -PRE_ROUTING_CHAIN_NAME = "OS_PRE_ROUTING_%s" -PORT_INBOUND_CHAIN_NAME = "OS_PORT_%s_INBOUND" -PORT_OUTBOUND_CHAIN_NAME = "OS_PORT_%s_OUTBOUND" -POST_ROUTING_CHAIN_NAME = "OS_POST_ROUTING_%s" -SG_INGRESS_CHAIN_NAME = "OS_SG_%s_INGRESS" -SG_EGRESS_CHAIN_NAME = "OS_SG_%s_EGRESS" -SG_PORT_GROUP_NAME = "OS_PG_%s" -SNAT_RULE = 'SNAT' - - -def _get_nat_ips(type, fip): - """Get NAT IP address information. - - From the route type given, determine the source and target IP addresses - from the provided floating IP DB object. - """ - if type == 'pre-routing': - return fip["floating_ip_address"], fip["fixed_ip_address"] - elif type == 'post-routing': - return fip["fixed_ip_address"], fip["floating_ip_address"] - else: - raise ValueError(_("Invalid nat_type %s") % type) - - -def _nat_chain_names(router_id): - """Get the chain names for NAT. - - These names are used to associate MidoNet chains to the NAT rules - applied to the router. For each of these, there are two NAT types, - 'dnat' and 'snat' that are returned as keys, and the corresponding - chain names as their values. - """ - pre_routing_name = PRE_ROUTING_CHAIN_NAME % router_id - post_routing_name = POST_ROUTING_CHAIN_NAME % router_id - return {'pre-routing': pre_routing_name, 'post-routing': post_routing_name} - - -def _sg_chain_names(sg_id): - """Get the chain names for security group. - - These names are used to associate a security group to MidoNet chains. - There are two names for ingress and egress security group directions. - """ - ingress = SG_INGRESS_CHAIN_NAME % sg_id - egress = SG_EGRESS_CHAIN_NAME % sg_id - return {'ingress': ingress, 'egress': egress} - - -def _port_chain_names(port_id): - """Get the chain names for a port. - - These are chains to hold security group chains. - """ - inbound = PORT_INBOUND_CHAIN_NAME % port_id - outbound = PORT_OUTBOUND_CHAIN_NAME % port_id - return {'inbound': inbound, 'outbound': outbound} - - -def _sg_port_group_name(sg_id): - """Get the port group name for security group.. - - This name is used to associate a security group to MidoNet port groups. - """ - return SG_PORT_GROUP_NAME % sg_id - - -def _rule_direction(sg_direction): - """Convert the SG direction to MidoNet direction - - MidoNet terms them 'inbound' and 'outbound' instead of 'ingress' and - 'egress'. Also, the direction is reversed since MidoNet sees it - from the network port's point of view, not the VM's. - """ - if sg_direction == 'ingress': - return 'outbound' - elif sg_direction == 'egress': - return 'inbound' - else: - raise ValueError(_("Unrecognized direction %s") % sg_direction) - - -def _is_router_interface_port(port): - """Check whether the given port is a router interface port.""" - device_owner = port['device_owner'] - return (device_owner in l3_db.DEVICE_OWNER_ROUTER_INTF) - - -def _is_router_gw_port(port): - """Check whether the given port is a router gateway port.""" - device_owner = port['device_owner'] - return (device_owner in l3_db.DEVICE_OWNER_ROUTER_GW) - - -def _is_vif_port(port): - """Check whether the given port is a standard VIF port.""" - device_owner = port['device_owner'] - return (not _is_dhcp_port(port) and - device_owner not in (l3_db.DEVICE_OWNER_ROUTER_GW, - l3_db.DEVICE_OWNER_ROUTER_INTF)) - - -def _is_dhcp_port(port): - """Check whether the given port is a DHCP port.""" - device_owner = port['device_owner'] - return device_owner.startswith(constants.DEVICE_OWNER_DHCP) - - -def _check_resource_exists(func, id, name, raise_exc=False): - """Check whether the given resource exists in MidoNet data store.""" - try: - func(id) - except midonet_lib.MidonetResourceNotFound as exc: - LOG.error(_("There is no %(name)s with ID %(id)s in MidoNet."), - {"name": name, "id": id}) - if raise_exc: - raise MidonetPluginException(msg=exc) - - -class MidoRpcCallbacks(rpc_compat.RpcCallback, - dhcp_rpc_base.DhcpRpcCallbackMixin): - RPC_API_VERSION = '1.1' - - -class MidonetPluginException(n_exc.NeutronException): - message = _("%(msg)s") - - -class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2, - portbindings_db.PortBindingMixin, - external_net_db.External_net_db_mixin, - l3_db.L3_NAT_db_mixin, - agentschedulers_db.DhcpAgentSchedulerDbMixin, - securitygroups_db.SecurityGroupDbMixin): - - supported_extension_aliases = ['external-net', 'router', 'security-group', - 'agent', 'dhcp_agent_scheduler', 'binding'] - __native_bulk_support = False - - def __init__(self): - super(MidonetPluginV2, self).__init__() - # Read config values - midonet_conf = cfg.CONF.MIDONET - midonet_uri = midonet_conf.midonet_uri - admin_user = midonet_conf.username - admin_pass = midonet_conf.password - admin_project_id = midonet_conf.project_id - self.provider_router_id = midonet_conf.provider_router_id - self.provider_router = None - - self.mido_api = api.MidonetApi(midonet_uri, admin_user, - admin_pass, - project_id=admin_project_id) - self.client = midonet_lib.MidoClient(self.mido_api) - - # self.provider_router_id should have been set. - if self.provider_router_id is None: - msg = _('provider_router_id should be configured in the plugin ' - 'config file') - LOG.exception(msg) - raise MidonetPluginException(msg=msg) - - self.setup_rpc() - - self.base_binding_dict = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_MIDONET, - portbindings.VIF_DETAILS: { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases}} - - def _get_provider_router(self): - if self.provider_router is None: - self.provider_router = self.client.get_router( - self.provider_router_id) - return self.provider_router - - def _dhcp_mappings(self, context, fixed_ips, mac): - for fixed_ip in fixed_ips: - subnet = self._get_subnet(context, fixed_ip["subnet_id"]) - if subnet["ip_version"] == 6: - # TODO(ryu) handle IPv6 - continue - if not subnet["enable_dhcp"]: - # Skip if DHCP is disabled - continue - yield subnet['cidr'], fixed_ip["ip_address"], mac - - def _metadata_subnets(self, context, fixed_ips): - for fixed_ip in fixed_ips: - subnet = self._get_subnet(context, fixed_ip["subnet_id"]) - if subnet["ip_version"] == 6: - continue - yield subnet['cidr'], fixed_ip["ip_address"] - - def _initialize_port_chains(self, port, in_chain, out_chain, sg_ids): - - tenant_id = port["tenant_id"] - - position = 1 - # mac spoofing protection - self._add_chain_rule(in_chain, action='drop', - dl_src=port["mac_address"], inv_dl_src=True, - position=position) - - # ip spoofing protection - for fixed_ip in port["fixed_ips"]: - position += 1 - self._add_chain_rule(in_chain, action="drop", - src_addr=fixed_ip["ip_address"] + "/32", - inv_nw_src=True, dl_type=0x0800, # IPv4 - position=position) - - # conntrack - position += 1 - self._add_chain_rule(in_chain, action='accept', - match_forward_flow=True, - position=position) - - # Reset the position to process egress - position = 1 - - # Add rule for SGs - if sg_ids: - for sg_id in sg_ids: - chain_name = _sg_chain_names(sg_id)["ingress"] - chain = self.client.get_chain_by_name(tenant_id, chain_name) - self._add_chain_rule(out_chain, action='jump', - jump_chain_id=chain.get_id(), - jump_chain_name=chain_name, - position=position) - position += 1 - - # add reverse flow matching at the end - self._add_chain_rule(out_chain, action='accept', - match_return_flow=True, - position=position) - position += 1 - - # fall back DROP rule at the end except for ARP - self._add_chain_rule(out_chain, action='drop', - dl_type=0x0806, # ARP - inv_dl_type=True, position=position) - - def _bind_port_to_sgs(self, context, port, sg_ids): - self._process_port_create_security_group(context, port, sg_ids) - if sg_ids is not None: - for sg_id in sg_ids: - pg_name = _sg_port_group_name(sg_id) - self.client.add_port_to_port_group_by_name( - port["tenant_id"], pg_name, port["id"]) - - def _unbind_port_from_sgs(self, context, port_id): - self._delete_port_security_group_bindings(context, port_id) - self.client.remove_port_from_port_groups(port_id) - - def _create_accept_chain_rule(self, context, sg_rule, chain=None): - direction = sg_rule["direction"] - tenant_id = sg_rule["tenant_id"] - sg_id = sg_rule["security_group_id"] - chain_name = _sg_chain_names(sg_id)[direction] - - if chain is None: - chain = self.client.get_chain_by_name(tenant_id, chain_name) - - pg_id = None - if sg_rule["remote_group_id"] is not None: - pg_name = _sg_port_group_name(sg_id) - pg = self.client.get_port_group_by_name(tenant_id, pg_name) - pg_id = pg.get_id() - - props = {OS_SG_RULE_KEY: str(sg_rule["id"])} - - # Determine source or destination address by looking at direction - src_pg_id = dst_pg_id = None - src_addr = dst_addr = None - src_port_to = dst_port_to = None - src_port_from = dst_port_from = None - if direction == "egress": - dst_pg_id = pg_id - dst_addr = sg_rule["remote_ip_prefix"] - dst_port_from = sg_rule["port_range_min"] - dst_port_to = sg_rule["port_range_max"] - else: - src_pg_id = pg_id - src_addr = sg_rule["remote_ip_prefix"] - src_port_from = sg_rule["port_range_min"] - src_port_to = sg_rule["port_range_max"] - - return self._add_chain_rule( - chain, action='accept', port_group_src=src_pg_id, - port_group_dst=dst_pg_id, - src_addr=src_addr, src_port_from=src_port_from, - src_port_to=src_port_to, - dst_addr=dst_addr, dst_port_from=dst_port_from, - dst_port_to=dst_port_to, - nw_proto=net_util.get_protocol_value(sg_rule["protocol"]), - dl_type=net_util.get_ethertype_value(sg_rule["ethertype"]), - properties=props) - - def _remove_nat_rules(self, context, fip): - router = self.client.get_router(fip["router_id"]) - self.client.remove_static_route(self._get_provider_router(), - fip["floating_ip_address"]) - - chain_names = _nat_chain_names(router.get_id()) - for _type, name in chain_names.iteritems(): - self.client.remove_rules_by_property( - router.get_tenant_id(), name, - OS_FLOATING_IP_RULE_KEY, fip["id"]) - - def setup_rpc(self): - # RPC support - self.topic = topics.PLUGIN - self.conn = rpc_compat.create_connection(new=True) - self.endpoints = [MidoRpcCallbacks(), - agents_db.AgentExtRpcCallback()] - self.conn.create_consumer(self.topic, self.endpoints, - fanout=False) - # Consume from all consumers in threads - self.conn.consume_in_threads() - - def create_subnet(self, context, subnet): - """Create Neutron subnet. - - Creates a Neutron subnet and a DHCP entry in MidoNet bridge. - """ - LOG.debug(_("MidonetPluginV2.create_subnet called: subnet=%r"), subnet) - - s = subnet["subnet"] - net = super(MidonetPluginV2, self).get_network( - context, subnet['subnet']['network_id'], fields=None) - - session = context.session - with session.begin(subtransactions=True): - sn_entry = super(MidonetPluginV2, self).create_subnet(context, - subnet) - bridge = self.client.get_bridge(sn_entry['network_id']) - - gateway_ip = s['gateway_ip'] - cidr = s['cidr'] - if s['enable_dhcp']: - dns_nameservers = None - host_routes = None - if s['dns_nameservers'] is not attributes.ATTR_NOT_SPECIFIED: - dns_nameservers = s['dns_nameservers'] - - if s['host_routes'] is not attributes.ATTR_NOT_SPECIFIED: - host_routes = s['host_routes'] - - self.client.create_dhcp(bridge, gateway_ip, cidr, - host_rts=host_routes, - dns_servers=dns_nameservers) - - # For external network, link the bridge to the provider router. - if net['router:external']: - self._link_bridge_to_gw_router( - bridge, self._get_provider_router(), gateway_ip, cidr) - - LOG.debug(_("MidonetPluginV2.create_subnet exiting: sn_entry=%r"), - sn_entry) - return sn_entry - - def delete_subnet(self, context, id): - """Delete Neutron subnet. - - Delete neutron network and its corresponding MidoNet bridge. - """ - LOG.debug(_("MidonetPluginV2.delete_subnet called: id=%s"), id) - subnet = super(MidonetPluginV2, self).get_subnet(context, id, - fields=None) - net = super(MidonetPluginV2, self).get_network(context, - subnet['network_id'], - fields=None) - session = context.session - with session.begin(subtransactions=True): - - super(MidonetPluginV2, self).delete_subnet(context, id) - bridge = self.client.get_bridge(subnet['network_id']) - if subnet['enable_dhcp']: - self.client.delete_dhcp(bridge, subnet['cidr']) - - # If the network is external, clean up routes, links, ports - if net[ext_net.EXTERNAL]: - self._unlink_bridge_from_gw_router( - bridge, self._get_provider_router()) - - LOG.debug(_("MidonetPluginV2.delete_subnet exiting")) - - def create_network(self, context, network): - """Create Neutron network. - - Create a new Neutron network and its corresponding MidoNet bridge. - """ - LOG.debug(_('MidonetPluginV2.create_network called: network=%r'), - network) - net_data = network['network'] - tenant_id = self._get_tenant_id_for_create(context, net_data) - net_data['tenant_id'] = tenant_id - self._ensure_default_security_group(context, tenant_id) - - bridge = self.client.create_bridge(**net_data) - net_data['id'] = bridge.get_id() - - session = context.session - with session.begin(subtransactions=True): - net = super(MidonetPluginV2, self).create_network(context, network) - self._process_l3_create(context, net, net_data) - - LOG.debug(_("MidonetPluginV2.create_network exiting: net=%r"), net) - return net - - def update_network(self, context, id, network): - """Update Neutron network. - - Update an existing Neutron network and its corresponding MidoNet - bridge. - """ - LOG.debug(_("MidonetPluginV2.update_network called: id=%(id)r, " - "network=%(network)r"), {'id': id, 'network': network}) - session = context.session - with session.begin(subtransactions=True): - net = super(MidonetPluginV2, self).update_network( - context, id, network) - self._process_l3_update(context, net, network['network']) - self.client.update_bridge(id, **network['network']) - - LOG.debug(_("MidonetPluginV2.update_network exiting: net=%r"), net) - return net - - def get_network(self, context, id, fields=None): - """Get Neutron network. - - Retrieves a Neutron network and its corresponding MidoNet bridge. - """ - LOG.debug(_("MidonetPluginV2.get_network called: id=%(id)r, " - "fields=%(fields)r"), {'id': id, 'fields': fields}) - qnet = super(MidonetPluginV2, self).get_network(context, id, fields) - self.client.get_bridge(id) - - LOG.debug(_("MidonetPluginV2.get_network exiting: qnet=%r"), qnet) - return qnet - - def delete_network(self, context, id): - """Delete a network and its corresponding MidoNet bridge.""" - LOG.debug(_("MidonetPluginV2.delete_network called: id=%r"), id) - self.client.delete_bridge(id) - try: - with context.session.begin(subtransactions=True): - self._process_l3_delete(context, id) - super(MidonetPluginV2, self).delete_network(context, id) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.error(_('Failed to delete neutron db, while Midonet ' - 'bridge=%r had been deleted'), id) - - def create_port(self, context, port): - """Create a L2 port in Neutron/MidoNet.""" - LOG.debug(_("MidonetPluginV2.create_port called: port=%r"), port) - port_data = port['port'] - - # Create a bridge port in MidoNet and set the bridge port ID as the - # port ID in Neutron. - bridge = self.client.get_bridge(port_data["network_id"]) - tenant_id = bridge.get_tenant_id() - asu = port_data.get("admin_state_up", True) - bridge_port = self.client.add_bridge_port(bridge, - admin_state_up=asu) - port_data["id"] = bridge_port.get_id() - - try: - session = context.session - with session.begin(subtransactions=True): - # Create a Neutron port - new_port = super(MidonetPluginV2, self).create_port(context, - port) - port_data.update(new_port) - self._ensure_default_security_group_on_port(context, - port) - if _is_vif_port(port_data): - # Bind security groups to the port - sg_ids = self._get_security_groups_on_port(context, port) - self._bind_port_to_sgs(context, new_port, sg_ids) - - # Create port chains - port_chains = {} - for d, name in _port_chain_names( - new_port["id"]).iteritems(): - port_chains[d] = self.client.create_chain(tenant_id, - name) - - self._initialize_port_chains(port_data, - port_chains['inbound'], - port_chains['outbound'], - sg_ids) - - # Update the port with the chain - self.client.update_port_chains( - bridge_port, port_chains["inbound"].get_id(), - port_chains["outbound"].get_id()) - - # DHCP mapping is only for VIF ports - for cidr, ip, mac in self._dhcp_mappings( - context, port_data["fixed_ips"], - port_data["mac_address"]): - self.client.add_dhcp_host(bridge, cidr, ip, mac) - - elif _is_dhcp_port(port_data): - # For DHCP port, add a metadata route - for cidr, ip in self._metadata_subnets( - context, port_data["fixed_ips"]): - self.client.add_dhcp_route_option(bridge, cidr, ip, - METADATA_DEFAULT_IP) - - self._process_portbindings_create_and_update(context, - port_data, new_port) - except Exception as ex: - # Try removing the MidoNet port before raising an exception. - with excutils.save_and_reraise_exception(): - LOG.error(_("Failed to create a port on network %(net_id)s: " - "%(err)s"), - {"net_id": port_data["network_id"], "err": ex}) - self.client.delete_port(bridge_port.get_id()) - - LOG.debug(_("MidonetPluginV2.create_port exiting: port=%r"), new_port) - return new_port - - def get_port(self, context, id, fields=None): - """Retrieve port.""" - LOG.debug(_("MidonetPluginV2.get_port called: id=%(id)s " - "fields=%(fields)r"), {'id': id, 'fields': fields}) - port = super(MidonetPluginV2, self).get_port(context, id, fields) - "Check if the port exists in MidoNet DB""" - try: - self.client.get_port(id) - except midonet_lib.MidonetResourceNotFound as exc: - LOG.error(_("There is no port with ID %(id)s in MidoNet."), - {"id": id}) - port['status'] = constants.PORT_STATUS_ERROR - raise exc - LOG.debug(_("MidonetPluginV2.get_port exiting: port=%r"), port) - return port - - def get_ports(self, context, filters=None, fields=None): - """List neutron ports and verify that they exist in MidoNet.""" - LOG.debug(_("MidonetPluginV2.get_ports called: filters=%(filters)s " - "fields=%(fields)r"), - {'filters': filters, 'fields': fields}) - ports = super(MidonetPluginV2, self).get_ports(context, filters, - fields) - return ports - - def delete_port(self, context, id, l3_port_check=True): - """Delete a neutron port and corresponding MidoNet bridge port.""" - LOG.debug(_("MidonetPluginV2.delete_port called: id=%(id)s " - "l3_port_check=%(l3_port_check)r"), - {'id': id, 'l3_port_check': l3_port_check}) - # if needed, check to see if this is a port owned by - # and l3-router. If so, we should prevent deletion. - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - - self.disassociate_floatingips(context, id) - port = self.get_port(context, id) - device_id = port['device_id'] - # If this port is for router interface/gw, unlink and delete. - if _is_router_interface_port(port): - self._unlink_bridge_from_router(device_id, id) - elif _is_router_gw_port(port): - # Gateway removed - # Remove all the SNAT rules that are tagged. - router = self._get_router(context, device_id) - tenant_id = router["tenant_id"] - chain_names = _nat_chain_names(device_id) - for _type, name in chain_names.iteritems(): - self.client.remove_rules_by_property( - tenant_id, name, OS_TENANT_ROUTER_RULE_KEY, - SNAT_RULE) - # Remove the default routes and unlink - self._remove_router_gateway(port['device_id']) - - self.client.delete_port(id, delete_chains=True) - try: - for cidr, ip, mac in self._dhcp_mappings( - context, port["fixed_ips"], port["mac_address"]): - self.client.delete_dhcp_host(port["network_id"], cidr, ip, - mac) - except Exception: - LOG.error(_("Failed to delete DHCP mapping for port %(id)s"), - {"id": id}) - - super(MidonetPluginV2, self).delete_port(context, id) - - def update_port(self, context, id, port): - """Handle port update, including security groups and fixed IPs.""" - with context.session.begin(subtransactions=True): - - # Get the port and save the fixed IPs - old_port = self._get_port(context, id) - net_id = old_port["network_id"] - mac = old_port["mac_address"] - old_ips = old_port["fixed_ips"] - # update the port DB - p = super(MidonetPluginV2, self).update_port(context, id, port) - - if "admin_state_up" in port["port"]: - asu = port["port"]["admin_state_up"] - mido_port = self.client.update_port(id, admin_state_up=asu) - - # If we're changing the admin_state_up flag and the port is - # associated with a router, then we also need to update the - # peer port. - if _is_router_interface_port(p): - self.client.update_port(mido_port.get_peer_id(), - admin_state_up=asu) - - new_ips = p["fixed_ips"] - if new_ips: - bridge = self.client.get_bridge(net_id) - # If it's a DHCP port, add a route to reach the MD server - if _is_dhcp_port(p): - for cidr, ip in self._metadata_subnets( - context, new_ips): - self.client.add_dhcp_route_option( - bridge, cidr, ip, METADATA_DEFAULT_IP) - else: - # IPs have changed. Re-map the DHCP entries - for cidr, ip, mac in self._dhcp_mappings( - context, old_ips, mac): - self.client.remove_dhcp_host( - bridge, cidr, ip, mac) - - for cidr, ip, mac in self._dhcp_mappings( - context, new_ips, mac): - self.client.add_dhcp_host( - bridge, cidr, ip, mac) - - if (self._check_update_deletes_security_groups(port) or - self._check_update_has_security_groups(port)): - self._unbind_port_from_sgs(context, p["id"]) - sg_ids = self._get_security_groups_on_port(context, port) - self._bind_port_to_sgs(context, p, sg_ids) - - self._process_portbindings_create_and_update(context, - port['port'], - p) - return p - - def create_router(self, context, router): - """Handle router creation. - - When a new Neutron router is created, its corresponding MidoNet router - is also created. In MidoNet, this router is initialized with chains - for inbound and outbound traffic, which will be used to hold other - chains that include various rules, such as NAT. - - :param router: Router information provided to create a new router. - """ - - # NOTE(dcahill): Similar to the NSX plugin, we completely override - # this method in order to be able to use the MidoNet ID as Neutron ID - # TODO(dcahill): Propose upstream patch for allowing - # 3rd parties to specify IDs as we do with l2 plugin - LOG.debug(_("MidonetPluginV2.create_router called: router=%(router)s"), - {"router": router}) - r = router['router'] - tenant_id = self._get_tenant_id_for_create(context, r) - r['tenant_id'] = tenant_id - mido_router = self.client.create_router(**r) - mido_router_id = mido_router.get_id() - - try: - has_gw_info = False - if EXTERNAL_GW_INFO in r: - has_gw_info = True - gw_info = r.pop(EXTERNAL_GW_INFO) - with context.session.begin(subtransactions=True): - # pre-generate id so it will be available when - # configuring external gw port - router_db = l3_db.Router(id=mido_router_id, - tenant_id=tenant_id, - name=r['name'], - admin_state_up=r['admin_state_up'], - status="ACTIVE") - context.session.add(router_db) - if has_gw_info: - self._update_router_gw_info(context, router_db['id'], - gw_info) - - router_data = self._make_router_dict(router_db) - - except Exception: - # Try removing the midonet router - with excutils.save_and_reraise_exception(): - self.client.delete_router(mido_router_id) - - # Create router chains - chain_names = _nat_chain_names(mido_router_id) - try: - self.client.add_router_chains(mido_router, - chain_names["pre-routing"], - chain_names["post-routing"]) - except Exception: - # Set the router status to Error - with context.session.begin(subtransactions=True): - r = self._get_router(context, router_data["id"]) - router_data['status'] = constants.NET_STATUS_ERROR - r['status'] = router_data['status'] - context.session.add(r) - - LOG.debug(_("MidonetPluginV2.create_router exiting: " - "router_data=%(router_data)s."), - {"router_data": router_data}) - return router_data - - def _set_router_gateway(self, id, gw_router, gw_ip): - """Set router uplink gateway - - :param ID: ID of the router - :param gw_router: gateway router to link to - :param gw_ip: gateway IP address - """ - LOG.debug(_("MidonetPluginV2.set_router_gateway called: id=%(id)s, " - "gw_router=%(gw_router)s, gw_ip=%(gw_ip)s"), - {'id': id, 'gw_router': gw_router, 'gw_ip': gw_ip}), - - router = self.client.get_router(id) - - # Create a port in the gw router - gw_port = self.client.add_router_port(gw_router, - port_address='169.254.255.1', - network_address='169.254.255.0', - network_length=30) - - # Create a port in the router - port = self.client.add_router_port(router, - port_address='169.254.255.2', - network_address='169.254.255.0', - network_length=30) - - # Link them - self.client.link(gw_port, port.get_id()) - - # Add a route for gw_ip to bring it down to the router - self.client.add_router_route(gw_router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=gw_ip, - dst_network_length=32, - next_hop_port=gw_port.get_id(), - weight=100) - - # Add default route to uplink in the router - self.client.add_router_route(router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr='0.0.0.0', - dst_network_length=0, - next_hop_port=port.get_id(), - weight=100) - - def _remove_router_gateway(self, id): - """Clear router gateway - - :param ID: ID of the router - """ - LOG.debug(_("MidonetPluginV2.remove_router_gateway called: " - "id=%(id)s"), {'id': id}) - router = self.client.get_router(id) - - # delete the port that is connected to the gateway router - for p in router.get_ports(): - if p.get_port_address() == '169.254.255.2': - peer_port_id = p.get_peer_id() - if peer_port_id is not None: - self.client.unlink(p) - self.client.delete_port(peer_port_id) - - # delete default route - for r in router.get_routes(): - if (r.get_dst_network_addr() == '0.0.0.0' and - r.get_dst_network_length() == 0): - self.client.delete_route(r.get_id()) - - def update_router(self, context, id, router): - """Handle router updates.""" - LOG.debug(_("MidonetPluginV2.update_router called: id=%(id)s " - "router=%(router)r"), {"id": id, "router": router}) - - router_data = router["router"] - - # Check if the update included changes to the gateway. - gw_updated = l3_db.EXTERNAL_GW_INFO in router_data - with context.session.begin(subtransactions=True): - - # Update the Neutron DB - r = super(MidonetPluginV2, self).update_router(context, id, - router) - tenant_id = r["tenant_id"] - if gw_updated: - if (l3_db.EXTERNAL_GW_INFO in r and - r[l3_db.EXTERNAL_GW_INFO] is not None): - # Gateway created - gw_port_neutron = self._get_port( - context.elevated(), r["gw_port_id"]) - gw_ip = gw_port_neutron['fixed_ips'][0]['ip_address'] - - # First link routers and set up the routes - self._set_router_gateway(r["id"], - self._get_provider_router(), - gw_ip) - gw_port_midonet = self.client.get_link_port( - self._get_provider_router(), r["id"]) - - # Get the NAT chains and add dynamic SNAT rules. - chain_names = _nat_chain_names(r["id"]) - props = {OS_TENANT_ROUTER_RULE_KEY: SNAT_RULE} - self.client.add_dynamic_snat(tenant_id, - chain_names['pre-routing'], - chain_names['post-routing'], - gw_ip, - gw_port_midonet.get_id(), - **props) - - self.client.update_router(id, **router_data) - - LOG.debug(_("MidonetPluginV2.update_router exiting: router=%r"), r) - return r - - def delete_router(self, context, id): - """Handler for router deletion. - - Deleting a router on Neutron simply means deleting its corresponding - router in MidoNet. - - :param id: router ID to remove - """ - LOG.debug(_("MidonetPluginV2.delete_router called: id=%s"), id) - - self.client.delete_router_chains(id) - self.client.delete_router(id) - - super(MidonetPluginV2, self).delete_router(context, id) - - def _link_bridge_to_gw_router(self, bridge, gw_router, gw_ip, cidr): - """Link a bridge to the gateway router - - :param bridge: bridge - :param gw_router: gateway router to link to - :param gw_ip: IP address of gateway - :param cidr: network CIDR - """ - net_addr, net_len = net_util.net_addr(cidr) - - # create a port on the gateway router - gw_port = self.client.add_router_port(gw_router, port_address=gw_ip, - network_address=net_addr, - network_length=net_len) - - # create a bridge port, then link it to the router. - port = self.client.add_bridge_port(bridge) - self.client.link(gw_port, port.get_id()) - - # add a route for the subnet in the gateway router - self.client.add_router_route(gw_router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=net_addr, - dst_network_length=net_len, - next_hop_port=gw_port.get_id(), - weight=100) - - def _unlink_bridge_from_gw_router(self, bridge, gw_router): - """Unlink a bridge from the gateway router - - :param bridge: bridge to unlink - :param gw_router: gateway router to unlink from - """ - # Delete routes and unlink the router and the bridge. - routes = self.client.get_router_routes(gw_router.get_id()) - - bridge_ports_to_delete = [ - p for p in gw_router.get_peer_ports() - if p.get_device_id() == bridge.get_id()] - - for p in bridge.get_peer_ports(): - if p.get_device_id() == gw_router.get_id(): - # delete the routes going to the bridge - for r in routes: - if r.get_next_hop_port() == p.get_id(): - self.client.delete_route(r.get_id()) - self.client.unlink(p) - self.client.delete_port(p.get_id()) - - # delete bridge port - for port in bridge_ports_to_delete: - self.client.delete_port(port.get_id()) - - def _link_bridge_to_router(self, router, bridge_port, net_addr, net_len, - gw_ip, metadata_gw_ip): - router_port = self.client.add_router_port( - router, network_length=net_len, network_address=net_addr, - port_address=gw_ip, admin_state_up=bridge_port['admin_state_up']) - self.client.link(router_port, bridge_port['id']) - self.client.add_router_route(router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=net_addr, - dst_network_length=net_len, - next_hop_port=router_port.get_id(), - weight=100) - - if metadata_gw_ip: - # Add a route for the metadata server. - # Not all VM images supports DHCP option 121. Add a route for the - # Metadata server in the router to forward the packet to the bridge - # that will send them to the Metadata Proxy. - md_net_addr, md_net_len = net_util.net_addr(METADATA_DEFAULT_IP) - self.client.add_router_route( - router, type='Normal', src_network_addr=net_addr, - src_network_length=net_len, - dst_network_addr=md_net_addr, - dst_network_length=md_net_len, - next_hop_port=router_port.get_id(), - next_hop_gateway=metadata_gw_ip) - - def _unlink_bridge_from_router(self, router_id, bridge_port_id): - """Unlink a bridge from a router.""" - - # Remove the routes to the port and unlink the port - bridge_port = self.client.get_port(bridge_port_id) - routes = self.client.get_router_routes(router_id) - self.client.delete_port_routes(routes, bridge_port.get_peer_id()) - self.client.unlink(bridge_port) - - def add_router_interface(self, context, router_id, interface_info): - """Handle router linking with network.""" - LOG.debug(_("MidonetPluginV2.add_router_interface called: " - "router_id=%(router_id)s " - "interface_info=%(interface_info)r"), - {'router_id': router_id, 'interface_info': interface_info}) - - with context.session.begin(subtransactions=True): - info = super(MidonetPluginV2, self).add_router_interface( - context, router_id, interface_info) - - try: - subnet = self._get_subnet(context, info["subnet_id"]) - cidr = subnet["cidr"] - net_addr, net_len = net_util.net_addr(cidr) - router = self.client.get_router(router_id) - - # Get the metadata GW IP - metadata_gw_ip = None - rport_qry = context.session.query(models_v2.Port) - dhcp_ports = rport_qry.filter_by( - network_id=subnet["network_id"], - device_owner=constants.DEVICE_OWNER_DHCP).all() - if dhcp_ports and dhcp_ports[0].fixed_ips: - metadata_gw_ip = dhcp_ports[0].fixed_ips[0].ip_address - else: - LOG.warn(_("DHCP agent is not working correctly. No port " - "to reach the Metadata server on this network")) - # Link the router and the bridge - port = super(MidonetPluginV2, self).get_port(context, - info["port_id"]) - self._link_bridge_to_router(router, port, net_addr, - net_len, subnet["gateway_ip"], - metadata_gw_ip) - except Exception: - LOG.error(_("Failed to create MidoNet resources to add router " - "interface. info=%(info)s, router_id=%(router_id)s"), - {"info": info, "router_id": router_id}) - with excutils.save_and_reraise_exception(): - with context.session.begin(subtransactions=True): - self.remove_router_interface(context, router_id, info) - - LOG.debug(_("MidonetPluginV2.add_router_interface exiting: " - "info=%r"), info) - return info - - def _assoc_fip(self, fip): - router = self.client.get_router(fip["router_id"]) - link_port = self.client.get_link_port( - self._get_provider_router(), router.get_id()) - self.client.add_router_route( - self._get_provider_router(), - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=fip["floating_ip_address"], - dst_network_length=32, - next_hop_port=link_port.get_peer_id()) - props = {OS_FLOATING_IP_RULE_KEY: fip['id']} - tenant_id = router.get_tenant_id() - chain_names = _nat_chain_names(router.get_id()) - for chain_type, name in chain_names.items(): - src_ip, target_ip = _get_nat_ips(chain_type, fip) - if chain_type == 'pre-routing': - nat_type = 'dnat' - else: - nat_type = 'snat' - self.client.add_static_nat(tenant_id, name, src_ip, - target_ip, - link_port.get_id(), - nat_type, **props) - - def create_floatingip(self, context, floatingip): - session = context.session - with session.begin(subtransactions=True): - fip = super(MidonetPluginV2, self).create_floatingip( - context, floatingip) - if fip['port_id']: - self._assoc_fip(fip) - return fip - - def update_floatingip(self, context, id, floatingip): - """Handle floating IP association and disassociation.""" - LOG.debug(_("MidonetPluginV2.update_floatingip called: id=%(id)s " - "floatingip=%(floatingip)s "), - {'id': id, 'floatingip': floatingip}) - - session = context.session - with session.begin(subtransactions=True): - if floatingip['floatingip']['port_id']: - fip = super(MidonetPluginV2, self).update_floatingip( - context, id, floatingip) - - self._assoc_fip(fip) - - # disassociate floating IP - elif floatingip['floatingip']['port_id'] is None: - fip = super(MidonetPluginV2, self).get_floatingip(context, id) - self._remove_nat_rules(context, fip) - super(MidonetPluginV2, self).update_floatingip(context, id, - floatingip) - - LOG.debug(_("MidonetPluginV2.update_floating_ip exiting: fip=%s"), fip) - return fip - - def disassociate_floatingips(self, context, port_id): - """Disassociate floating IPs (if any) from this port.""" - try: - fip_qry = context.session.query(l3_db.FloatingIP) - fip_dbs = fip_qry.filter_by(fixed_port_id=port_id) - for fip_db in fip_dbs: - self._remove_nat_rules(context, fip_db) - except sa_exc.NoResultFound: - pass - - super(MidonetPluginV2, self).disassociate_floatingips(context, port_id) - - def create_security_group(self, context, security_group, default_sg=False): - """Create security group. - - Create a new security group, including the default security group. - In MidoNet, this means creating a pair of chains, inbound and outbound, - as well as a new port group. - """ - LOG.debug(_("MidonetPluginV2.create_security_group called: " - "security_group=%(security_group)s " - "default_sg=%(default_sg)s "), - {'security_group': security_group, 'default_sg': default_sg}) - - sg = security_group.get('security_group') - tenant_id = self._get_tenant_id_for_create(context, sg) - if not default_sg: - self._ensure_default_security_group(context, tenant_id) - - # Create the Neutron sg first - sg = super(MidonetPluginV2, self).create_security_group( - context, security_group, default_sg) - - try: - # Process the MidoNet side - self.client.create_port_group(tenant_id, - _sg_port_group_name(sg["id"])) - chain_names = _sg_chain_names(sg["id"]) - chains = {} - for direction, chain_name in chain_names.iteritems(): - c = self.client.create_chain(tenant_id, chain_name) - chains[direction] = c - - # Create all the rules for this SG. Only accept rules are created - for r in sg['security_group_rules']: - self._create_accept_chain_rule(context, r, - chain=chains[r['direction']]) - except Exception: - LOG.error(_("Failed to create MidoNet resources for sg %(sg)r"), - {"sg": sg}) - with excutils.save_and_reraise_exception(): - with context.session.begin(subtransactions=True): - sg = self._get_security_group(context, sg["id"]) - context.session.delete(sg) - - LOG.debug(_("MidonetPluginV2.create_security_group exiting: sg=%r"), - sg) - return sg - - def delete_security_group(self, context, id): - """Delete chains for Neutron security group.""" - LOG.debug(_("MidonetPluginV2.delete_security_group called: id=%s"), id) - - with context.session.begin(subtransactions=True): - sg = super(MidonetPluginV2, self).get_security_group(context, id) - if not sg: - raise ext_sg.SecurityGroupNotFound(id=id) - - if sg["name"] == 'default' and not context.is_admin: - raise ext_sg.SecurityGroupCannotRemoveDefault() - - sg_id = sg['id'] - filters = {'security_group_id': [sg_id]} - if super(MidonetPluginV2, self)._get_port_security_group_bindings( - context, filters): - raise ext_sg.SecurityGroupInUse(id=sg_id) - - # Delete MidoNet Chains and portgroup for the SG - tenant_id = sg['tenant_id'] - self.client.delete_chains_by_names( - tenant_id, _sg_chain_names(sg["id"]).values()) - - self.client.delete_port_group_by_name( - tenant_id, _sg_port_group_name(sg["id"])) - - super(MidonetPluginV2, self).delete_security_group(context, id) - - def create_security_group_rule(self, context, security_group_rule): - """Create a security group rule - - Create a security group rule in the Neutron DB and corresponding - MidoNet resources in its data store. - """ - LOG.debug(_("MidonetPluginV2.create_security_group_rule called: " - "security_group_rule=%(security_group_rule)r"), - {'security_group_rule': security_group_rule}) - - with context.session.begin(subtransactions=True): - rule = super(MidonetPluginV2, self).create_security_group_rule( - context, security_group_rule) - - self._create_accept_chain_rule(context, rule) - - LOG.debug(_("MidonetPluginV2.create_security_group_rule exiting: " - "rule=%r"), rule) - return rule - - def delete_security_group_rule(self, context, sg_rule_id): - """Delete a security group rule - - Delete a security group rule from the Neutron DB and corresponding - MidoNet resources from its data store. - """ - LOG.debug(_("MidonetPluginV2.delete_security_group_rule called: " - "sg_rule_id=%s"), sg_rule_id) - with context.session.begin(subtransactions=True): - rule = super(MidonetPluginV2, self).get_security_group_rule( - context, sg_rule_id) - - if not rule: - raise ext_sg.SecurityGroupRuleNotFound(id=sg_rule_id) - - sg = self._get_security_group(context, - rule["security_group_id"]) - chain_name = _sg_chain_names(sg["id"])[rule["direction"]] - self.client.remove_rules_by_property(rule["tenant_id"], chain_name, - OS_SG_RULE_KEY, - str(rule["id"])) - super(MidonetPluginV2, self).delete_security_group_rule( - context, sg_rule_id) - - def _add_chain_rule(self, chain, action, **kwargs): - - nw_proto = kwargs.get("nw_proto") - src_addr = kwargs.pop("src_addr", None) - dst_addr = kwargs.pop("dst_addr", None) - src_port_from = kwargs.pop("src_port_from", None) - src_port_to = kwargs.pop("src_port_to", None) - dst_port_from = kwargs.pop("dst_port_from", None) - dst_port_to = kwargs.pop("dst_port_to", None) - - # Convert to the keys and values that midonet client understands - if src_addr: - kwargs["nw_src_addr"], kwargs["nw_src_length"] = net_util.net_addr( - src_addr) - - if dst_addr: - kwargs["nw_dst_addr"], kwargs["nw_dst_length"] = net_util.net_addr( - dst_addr) - - kwargs["tp_src"] = {"start": src_port_from, "end": src_port_to} - - kwargs["tp_dst"] = {"start": dst_port_from, "end": dst_port_to} - - if nw_proto == 1: # ICMP - # Overwrite port fields regardless of the direction - kwargs["tp_src"] = {"start": src_port_from, "end": src_port_from} - kwargs["tp_dst"] = {"start": dst_port_to, "end": dst_port_to} - - return self.client.add_chain_rule(chain, action=action, **kwargs) diff --git a/neutron/plugins/ml2/README b/neutron/plugins/ml2/README deleted file mode 100644 index 4dce789cb..000000000 --- a/neutron/plugins/ml2/README +++ /dev/null @@ -1,53 +0,0 @@ -The Modular Layer 2 (ML2) plugin is a framework allowing OpenStack -Networking to simultaneously utilize the variety of layer 2 networking -technologies found in complex real-world data centers. It supports the -Open vSwitch, Linux bridge, and Hyper-V L2 agents, replacing and -deprecating the monolithic plugins previously associated with those -agents, and can also support hardware devices and SDN controllers. The -ML2 framework is intended to greatly simplify adding support for new -L2 networking technologies, requiring much less initial and ongoing -effort than would be required for an additional monolithic core -plugin. It is also intended to foster innovation through its -organization as optional driver modules. - -The ML2 plugin supports all the non-vendor-specific neutron API -extensions, and works with the standard neutron DHCP agent. It -utilizes the service plugin interface to implement the L3 router -abstraction, allowing use of either the standard neutron L3 agent or -alternative L3 solutions. Additional service plugins can also be used -with the ML2 core plugin. - -Drivers within ML2 implement separately extensible sets of network -types and of mechanisms for accessing networks of those types. Unlike -with the metaplugin, multiple mechanisms can be used simultaneously to -access different ports of the same virtual network. Mechanisms can -utilize L2 agents via RPC and/or interact with external devices or -controllers. By utilizing the multiprovidernet extension, virtual -networks can be composed of multiple segments of the same or different -types. Type and mechanism drivers are loaded as python entrypoints -using the stevedore library. - -Each available network type is managed by an ML2 type driver. Type -drivers maintain any needed type-specific network state, and perform -provider network validation and tenant network allocation. As of the -havana release, drivers for the local, flat, vlan, gre, and vxlan -network types are included. - -Each available networking mechanism is managed by an ML2 mechanism -driver. All registered mechanism drivers are called twice when -networks, subnets, and ports are created, updated, or deleted. They -are first called as part of the DB transaction, where they can -maintain any needed driver-specific state. Once the transaction has -been committed, they are called again, at which point they can -interact with external devices and controllers. Mechanism drivers are -also called as part of the port binding process, to determine whether -the associated mechanism can provide connectivity for the network, and -if so, the network segment and VIF driver to be used. The havana -release includes mechanism drivers for the Open vSwitch, Linux bridge, -and Hyper-V L2 agents, for Arista and Cisco switches, and for the -Tail-f NCS. It also includes an L2 Population mechanism driver that -can help optimize tunneled virtual network traffic. - -For additional information regarding the ML2 plugin and its collection -of type and mechanism drivers, see the OpenStack manuals and -http://wiki.openstack.org/wiki/Neutron/ML2. diff --git a/neutron/plugins/ml2/__init__.py b/neutron/plugins/ml2/__init__.py deleted file mode 100644 index 788cea1f7..000000000 --- a/neutron/plugins/ml2/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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. diff --git a/neutron/plugins/ml2/common/__init__.py b/neutron/plugins/ml2/common/__init__.py deleted file mode 100644 index 788cea1f7..000000000 --- a/neutron/plugins/ml2/common/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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. diff --git a/neutron/plugins/ml2/common/exceptions.py b/neutron/plugins/ml2/common/exceptions.py deleted file mode 100644 index ed94b1e1f..000000000 --- a/neutron/plugins/ml2/common/exceptions.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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 ML2.""" - -from neutron.common import exceptions - - -class MechanismDriverError(exceptions.NeutronException): - """Mechanism driver call failed.""" - message = _("%(method)s failed.") diff --git a/neutron/plugins/ml2/config.py b/neutron/plugins/ml2/config.py deleted file mode 100644 index afce63045..000000000 --- a/neutron/plugins/ml2/config.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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_opts = [ - cfg.ListOpt('type_drivers', - default=['local', 'flat', 'vlan', 'gre', 'vxlan'], - help=_("List of network type driver entrypoints to be loaded " - "from the neutron.ml2.type_drivers namespace.")), - cfg.ListOpt('tenant_network_types', - default=['local'], - help=_("Ordered list of network_types to allocate as tenant " - "networks.")), - cfg.ListOpt('mechanism_drivers', - default=[], - help=_("An ordered list of networking mechanism driver " - "entrypoints to be loaded from the " - "neutron.ml2.mechanism_drivers namespace.")), -] - - -cfg.CONF.register_opts(ml2_opts, "ml2") diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py deleted file mode 100644 index 4cf8eed32..000000000 --- a/neutron/plugins/ml2/db.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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 api as db_api -from neutron.db import models_v2 -from neutron.db import securitygroups_db as sg_db -from neutron.extensions import portbindings -from neutron import manager -from neutron.openstack.common import log -from neutron.openstack.common import uuidutils -from neutron.plugins.ml2 import driver_api as api -from neutron.plugins.ml2 import models - -LOG = log.getLogger(__name__) - - -def add_network_segment(session, network_id, segment): - with session.begin(subtransactions=True): - record = models.NetworkSegment( - id=uuidutils.generate_uuid(), - network_id=network_id, - network_type=segment.get(api.NETWORK_TYPE), - physical_network=segment.get(api.PHYSICAL_NETWORK), - segmentation_id=segment.get(api.SEGMENTATION_ID) - ) - session.add(record) - LOG.info(_("Added segment %(id)s of type %(network_type)s for network" - " %(network_id)s"), - {'id': record.id, - 'network_type': record.network_type, - 'network_id': record.network_id}) - - -def get_network_segments(session, network_id): - with session.begin(subtransactions=True): - records = (session.query(models.NetworkSegment). - filter_by(network_id=network_id)) - return [{api.ID: record.id, - api.NETWORK_TYPE: record.network_type, - api.PHYSICAL_NETWORK: record.physical_network, - api.SEGMENTATION_ID: record.segmentation_id} - for record in records] - - -def ensure_port_binding(session, port_id): - with session.begin(subtransactions=True): - try: - record = (session.query(models.PortBinding). - filter_by(port_id=port_id). - one()) - except exc.NoResultFound: - record = models.PortBinding( - port_id=port_id, - vif_type=portbindings.VIF_TYPE_UNBOUND) - session.add(record) - return record - - -def get_port(session, port_id): - """Get port record for update within transcation.""" - - with session.begin(subtransactions=True): - try: - record = (session.query(models_v2.Port). - filter(models_v2.Port.id.startswith(port_id)). - one()) - return record - except exc.NoResultFound: - return - except exc.MultipleResultsFound: - LOG.error(_("Multiple ports have port_id starting with %s"), - port_id) - return - - -def get_port_from_device_mac(device_mac): - LOG.debug(_("get_port_from_device_mac() called for mac %s"), device_mac) - session = db_api.get_session() - qry = session.query(models_v2.Port).filter_by(mac_address=device_mac) - return qry.first() - - -def get_port_and_sgs(port_id): - """Get port from database with security group info.""" - - LOG.debug(_("get_port_and_sgs() called for port_id %s"), port_id) - session = db_api.get_session() - sg_binding_port = sg_db.SecurityGroupPortBinding.port_id - - with session.begin(subtransactions=True): - query = session.query(models_v2.Port, - sg_db.SecurityGroupPortBinding.security_group_id) - query = query.outerjoin(sg_db.SecurityGroupPortBinding, - models_v2.Port.id == sg_binding_port) - query = query.filter(models_v2.Port.id.startswith(port_id)) - port_and_sgs = query.all() - if not port_and_sgs: - return - port = port_and_sgs[0][0] - plugin = manager.NeutronManager.get_plugin() - port_dict = plugin._make_port_dict(port) - port_dict['security_groups'] = [ - sg_id for port_, sg_id in port_and_sgs if sg_id] - port_dict['security_group_rules'] = [] - port_dict['security_group_source_groups'] = [] - port_dict['fixed_ips'] = [ip['ip_address'] - for ip in port['fixed_ips']] - return port_dict - - -def get_port_binding_host(port_id): - session = db_api.get_session() - with session.begin(subtransactions=True): - try: - query = (session.query(models.PortBinding). - filter(models.PortBinding.port_id.startswith(port_id)). - one()) - except exc.NoResultFound: - LOG.debug(_("No binding found for port %(port_id)s"), - {'port_id': port_id}) - return - return query.host diff --git a/neutron/plugins/ml2/driver_api.py b/neutron/plugins/ml2/driver_api.py deleted file mode 100644 index 2384b0cf9..000000000 --- a/neutron/plugins/ml2/driver_api.py +++ /dev/null @@ -1,597 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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 abc -import six - -# The following keys are used in the segment dictionaries passed via -# the driver API. These are defined separately from similar keys in -# neutron.extensions.providernet so that drivers don't need to change -# if/when providernet moves to the core API. -# -ID = 'id' -NETWORK_TYPE = 'network_type' -PHYSICAL_NETWORK = 'physical_network' -SEGMENTATION_ID = 'segmentation_id' - - -@six.add_metaclass(abc.ABCMeta) -class TypeDriver(object): - """Define stable abstract interface for ML2 type drivers. - - ML2 type drivers each support a specific network_type for provider - and/or tenant network segments. Type drivers must implement this - abstract interface, which defines the API by which the plugin uses - the driver to manage the persistent type-specific resource - allocation state associated with network segments of that type. - - Network segments are represented by segment dictionaries using the - NETWORK_TYPE, PHYSICAL_NETWORK, and SEGMENTATION_ID keys defined - above, corresponding to the provider attributes. Future revisions - of the TypeDriver API may add additional segment dictionary - keys. Attributes not applicable for a particular network_type may - either be excluded or stored as None. - """ - - @abc.abstractmethod - def get_type(self): - """Get driver's network type. - - :returns network_type value handled by this driver - """ - pass - - @abc.abstractmethod - def initialize(self): - """Perform driver initialization. - - Called after all drivers have been loaded and the database has - been initialized. No abstract methods defined below will be - called prior to this method being called. - """ - pass - - @abc.abstractmethod - def validate_provider_segment(self, segment): - """Validate attributes of a provider network segment. - - :param segment: segment dictionary using keys defined above - :raises: neutron.common.exceptions.InvalidInput if invalid - - Called outside transaction context to validate the provider - attributes for a provider network segment. Raise InvalidInput - if: - - - any required attribute is missing - - any prohibited or unrecognized attribute is present - - any attribute value is not valid - - The network_type attribute is present in segment, but - need not be validated. - """ - pass - - @abc.abstractmethod - def reserve_provider_segment(self, session, segment): - """Reserve resource associated with a provider network segment. - - :param session: database session - :param segment: segment dictionary using keys defined above - - Called inside transaction context on session to reserve the - type-specific resource for a provider network segment. The - segment dictionary passed in was returned by a previous - validate_provider_segment() call. - """ - pass - - @abc.abstractmethod - def allocate_tenant_segment(self, session): - """Allocate resource for a new tenant network segment. - - :param session: database session - :returns: segment dictionary using keys defined above - - Called inside transaction context on session to allocate a new - tenant network, typically from a type-specific resource - pool. If successful, return a segment dictionary describing - the segment. If tenant network segment cannot be allocated - (i.e. tenant networks not supported or resource pool is - exhausted), return None. - """ - pass - - @abc.abstractmethod - def release_segment(self, session, segment): - """Release network segment. - - :param session: database session - :param segment: segment dictionary using keys defined above - - Called inside transaction context on session to release a - tenant or provider network's type-specific resource. Runtime - errors are not expected, but raising an exception will result - in rollback of the transaction. - """ - pass - - -@six.add_metaclass(abc.ABCMeta) -class NetworkContext(object): - """Context passed to MechanismDrivers for changes to network resources. - - A NetworkContext instance wraps a network resource. It provides - helper methods for accessing other relevant information. Results - from expensive operations are cached so that other - MechanismDrivers can freely access the same information. - """ - - @abc.abstractproperty - def current(self): - """Return the current state of the network. - - Return the current state of the network, as defined by - NeutronPluginBaseV2.create_network and all extensions in the - ml2 plugin. - """ - pass - - @abc.abstractproperty - def original(self): - """Return the original state of the network. - - Return the original state of the network, prior to a call to - update_network. Method is only valid within calls to - update_network_precommit and update_network_postcommit. - """ - pass - - @abc.abstractproperty - def network_segments(self): - """Return the segments associated with this network resource.""" - pass - - -@six.add_metaclass(abc.ABCMeta) -class SubnetContext(object): - """Context passed to MechanismDrivers for changes to subnet resources. - - A SubnetContext instance wraps a subnet resource. It provides - helper methods for accessing other relevant information. Results - from expensive operations are cached so that other - MechanismDrivers can freely access the same information. - """ - - @abc.abstractproperty - def current(self): - """Return the current state of the subnet. - - Return the current state of the subnet, as defined by - NeutronPluginBaseV2.create_subnet and all extensions in the - ml2 plugin. - """ - pass - - @abc.abstractproperty - def original(self): - """Return the original state of the subnet. - - Return the original state of the subnet, prior to a call to - update_subnet. Method is only valid within calls to - update_subnet_precommit and update_subnet_postcommit. - """ - pass - - -@six.add_metaclass(abc.ABCMeta) -class PortContext(object): - """Context passed to MechanismDrivers for changes to port resources. - - A PortContext instance wraps a port resource. It provides helper - methods for accessing other relevant information. Results from - expensive operations are cached so that other MechanismDrivers can - freely access the same information. - """ - - @abc.abstractproperty - def current(self): - """Return the current state of the port. - - Return the current state of the port, as defined by - NeutronPluginBaseV2.create_port and all extensions in the ml2 - plugin. - """ - pass - - @abc.abstractproperty - def original(self): - """Return the original state of the port. - - Return the original state of the port, prior to a call to - update_port. Method is only valid within calls to - update_port_precommit and update_port_postcommit. - """ - pass - - @abc.abstractproperty - def network(self): - """Return the NetworkContext associated with this port.""" - pass - - @abc.abstractproperty - def bound_segment(self): - """Return the currently bound segment dictionary.""" - pass - - @abc.abstractproperty - def original_bound_segment(self): - """Return the original bound segment dictionary. - - Return the original bound segment dictionary, prior to a call - to update_port. Method is only valid within calls to - update_port_precommit and update_port_postcommit. - """ - pass - - @abc.abstractproperty - def bound_driver(self): - """Return the currently bound mechanism driver name.""" - pass - - @abc.abstractproperty - def original_bound_driver(self): - """Return the original bound mechanism driver name. - - Return the original bound mechanism driver name, prior to a - call to update_port. Method is only valid within calls to - update_port_precommit and update_port_postcommit. - """ - pass - - @abc.abstractmethod - def host_agents(self, agent_type): - """Get agents of the specified type on port's host. - - :param agent_type: Agent type identifier - :returns: List of agents_db.Agent records - """ - pass - - @abc.abstractmethod - def set_binding(self, segment_id, vif_type, vif_details, - status=None): - """Set the binding for the port. - - :param segment_id: Network segment bound for the port. - :param vif_type: The VIF type for the bound port. - :param vif_details: Dictionary with details for VIF driver. - :param status: Port status to set if not None. - - Called by MechanismDriver.bind_port to indicate success and - specify binding details to use for port. The segment_id must - identify an item in network.network_segments. - """ - pass - - -@six.add_metaclass(abc.ABCMeta) -class MechanismDriver(object): - """Define stable abstract interface for ML2 mechanism drivers. - - A mechanism driver is called on the creation, update, and deletion - of networks and ports. For every event, there are two methods that - get called - one within the database transaction (method suffix of - _precommit), one right afterwards (method suffix of _postcommit). - - Exceptions raised by methods called inside the transaction can - rollback, but should not make any blocking calls (for example, - REST requests to an outside controller). Methods called after - transaction commits can make blocking external calls, though these - will block the entire process. Exceptions raised in calls after - the transaction commits may cause the associated resource to be - deleted. - - Because rollback outside of the transaction is not done in the - update network/port case, all data validation must be done within - methods that are part of the database transaction. - """ - - @abc.abstractmethod - def initialize(self): - """Perform driver initialization. - - Called after all drivers have been loaded and the database has - been initialized. No abstract methods defined below will be - called prior to this method being called. - """ - pass - - def create_network_precommit(self, context): - """Allocate resources for a new network. - - :param context: NetworkContext instance describing the new - network. - - Create a new network, allocating resources as necessary in the - database. Called inside transaction context on session. Call - cannot block. Raising an exception will result in a rollback - of the current transaction. - """ - pass - - def create_network_postcommit(self, context): - """Create a network. - - :param context: NetworkContext instance describing the new - network. - - Called after the transaction commits. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Raising an exception will - cause the deletion of the resource. - """ - pass - - def update_network_precommit(self, context): - """Update resources of a network. - - :param context: NetworkContext instance describing the new - state of the network, as well as the original state prior - to the update_network call. - - Update values of a network, updating the associated resources - in the database. Called inside transaction context on session. - Raising an exception will result in rollback of the - transaction. - - update_network_precommit is called for all changes to the - network state. It is up to the mechanism driver to ignore - state or state changes that it does not know or care about. - """ - pass - - def update_network_postcommit(self, context): - """Update a network. - - :param context: NetworkContext instance describing the new - state of the network, as well as the original state prior - to the update_network call. - - Called after the transaction commits. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Raising an exception will - cause the deletion of the resource. - - update_network_postcommit is called for all changes to the - network state. It is up to the mechanism driver to ignore - state or state changes that it does not know or care about. - """ - pass - - def delete_network_precommit(self, context): - """Delete resources for a network. - - :param context: NetworkContext instance describing the current - state of the network, prior to the call to delete it. - - Delete network resources previously allocated by this - mechanism driver for a network. Called inside transaction - context on session. Runtime errors are not expected, but - raising an exception will result in rollback of the - transaction. - """ - pass - - def delete_network_postcommit(self, context): - """Delete a network. - - :param context: NetworkContext instance describing the current - state of the network, prior to the call to delete it. - - Called after the transaction commits. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Runtime errors are not - expected, and will not prevent the resource from being - deleted. - """ - pass - - def create_subnet_precommit(self, context): - """Allocate resources for a new subnet. - - :param context: SubnetContext instance describing the new - subnet. - - Create a new subnet, allocating resources as necessary in the - database. Called inside transaction context on session. Call - cannot block. Raising an exception will result in a rollback - of the current transaction. - """ - pass - - def create_subnet_postcommit(self, context): - """Create a subnet. - - :param context: SubnetContext instance describing the new - subnet. - - Called after the transaction commits. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Raising an exception will - cause the deletion of the resource. - """ - pass - - def update_subnet_precommit(self, context): - """Update resources of a subnet. - - :param context: SubnetContext instance describing the new - state of the subnet, as well as the original state prior - to the update_subnet call. - - Update values of a subnet, updating the associated resources - in the database. Called inside transaction context on session. - Raising an exception will result in rollback of the - transaction. - - update_subnet_precommit is called for all changes to the - subnet state. It is up to the mechanism driver to ignore - state or state changes that it does not know or care about. - """ - pass - - def update_subnet_postcommit(self, context): - """Update a subnet. - - :param context: SubnetContext instance describing the new - state of the subnet, as well as the original state prior - to the update_subnet call. - - Called after the transaction commits. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Raising an exception will - cause the deletion of the resource. - - update_subnet_postcommit is called for all changes to the - subnet state. It is up to the mechanism driver to ignore - state or state changes that it does not know or care about. - """ - pass - - def delete_subnet_precommit(self, context): - """Delete resources for a subnet. - - :param context: SubnetContext instance describing the current - state of the subnet, prior to the call to delete it. - - Delete subnet resources previously allocated by this - mechanism driver for a subnet. Called inside transaction - context on session. Runtime errors are not expected, but - raising an exception will result in rollback of the - transaction. - """ - pass - - def delete_subnet_postcommit(self, context): - """Delete a subnet. - - :param context: SubnetContext instance describing the current - state of the subnet, prior to the call to delete it. - - Called after the transaction commits. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Runtime errors are not - expected, and will not prevent the resource from being - deleted. - """ - pass - - def create_port_precommit(self, context): - """Allocate resources for a new port. - - :param context: PortContext instance describing the port. - - Create a new port, allocating resources as necessary in the - database. Called inside transaction context on session. Call - cannot block. Raising an exception will result in a rollback - of the current transaction. - """ - pass - - def create_port_postcommit(self, context): - """Create a port. - - :param context: PortContext instance describing the port. - - Called after the transaction completes. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Raising an exception will - result in the deletion of the resource. - """ - pass - - def update_port_precommit(self, context): - """Update resources of a port. - - :param context: PortContext instance describing the new - state of the port, as well as the original state prior - to the update_port call. - - Called inside transaction context on session to complete a - port update as defined by this mechanism driver. Raising an - exception will result in rollback of the transaction. - - update_port_precommit is called for all changes to the port - state. It is up to the mechanism driver to ignore state or - state changes that it does not know or care about. - """ - pass - - def update_port_postcommit(self, context): - """Update a port. - - :param context: PortContext instance describing the new - state of the port, as well as the original state prior - to the update_port call. - - Called after the transaction completes. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Raising an exception will - result in the deletion of the resource. - - update_port_postcommit is called for all changes to the port - state. It is up to the mechanism driver to ignore state or - state changes that it does not know or care about. - """ - pass - - def delete_port_precommit(self, context): - """Delete resources of a port. - - :param context: PortContext instance describing the current - state of the port, prior to the call to delete it. - - Called inside transaction context on session. Runtime errors - are not expected, but raising an exception will result in - rollback of the transaction. - """ - pass - - def delete_port_postcommit(self, context): - """Delete a port. - - :param context: PortContext instance describing the current - state of the port, prior to the call to delete it. - - Called after the transaction completes. Call can block, though - will block the entire process so care should be taken to not - drastically affect performance. Runtime errors are not - expected, and will not prevent the resource from being - deleted. - """ - pass - - def bind_port(self, context): - """Attempt to bind a port. - - :param context: PortContext instance describing the port - - Called inside transaction context on session, prior to - create_port_precommit or update_port_precommit, to - attempt to establish a port binding. If the driver is able to - bind the port, it calls context.set_binding with the binding - details. - """ - pass diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py deleted file mode 100644 index 0c1180619..000000000 --- a/neutron/plugins/ml2/driver_context.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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.openstack.common import jsonutils -from neutron.plugins.ml2 import db -from neutron.plugins.ml2 import driver_api as api - - -class MechanismDriverContext(object): - """MechanismDriver context base class.""" - def __init__(self, plugin, plugin_context): - self._plugin = plugin - # This temporarily creates a reference loop, but the - # lifetime of PortContext is limited to a single - # method call of the plugin. - self._plugin_context = plugin_context - - -class NetworkContext(MechanismDriverContext, api.NetworkContext): - - def __init__(self, plugin, plugin_context, network, - original_network=None): - super(NetworkContext, self).__init__(plugin, plugin_context) - self._network = network - self._original_network = original_network - self._segments = db.get_network_segments(plugin_context.session, - network['id']) - - @property - def current(self): - return self._network - - @property - def original(self): - return self._original_network - - @property - def network_segments(self): - return self._segments - - -class SubnetContext(MechanismDriverContext, api.SubnetContext): - - def __init__(self, plugin, plugin_context, subnet, original_subnet=None): - super(SubnetContext, self).__init__(plugin, plugin_context) - self._subnet = subnet - self._original_subnet = original_subnet - - @property - def current(self): - return self._subnet - - @property - def original(self): - return self._original_subnet - - -class PortContext(MechanismDriverContext, api.PortContext): - - def __init__(self, plugin, plugin_context, port, network, - original_port=None): - super(PortContext, self).__init__(plugin, plugin_context) - self._port = port - self._original_port = original_port - self._network_context = NetworkContext(plugin, plugin_context, - network) - self._binding = db.ensure_port_binding(plugin_context.session, - port['id']) - if original_port: - self._original_bound_segment_id = self._binding.segment - self._original_bound_driver = self._binding.driver - else: - self._original_bound_segment_id = None - self._original_bound_driver = None - self._new_port_status = None - - @property - def current(self): - return self._port - - @property - def original(self): - return self._original_port - - @property - def network(self): - return self._network_context - - @property - def bound_segment(self): - id = self._binding.segment - if id: - for segment in self._network_context.network_segments: - if segment[api.ID] == id: - return segment - - @property - def original_bound_segment(self): - if self._original_bound_segment_id: - for segment in self._network_context.network_segments: - if segment[api.ID] == self._original_bound_segment_id: - return segment - - @property - def bound_driver(self): - return self._binding.driver - - @property - def original_bound_driver(self): - return self._original_bound_driver - - def host_agents(self, agent_type): - return self._plugin.get_agents(self._plugin_context, - filters={'agent_type': [agent_type], - 'host': [self._binding.host]}) - - def set_binding(self, segment_id, vif_type, vif_details, - status=None): - # TODO(rkukura) Verify binding allowed, segment in network - self._binding.segment = segment_id - self._binding.vif_type = vif_type - self._binding.vif_details = jsonutils.dumps(vif_details) - self._new_port_status = status diff --git a/neutron/plugins/ml2/drivers/README.fslsdn b/neutron/plugins/ml2/drivers/README.fslsdn deleted file mode 100644 index 09017284c..000000000 --- a/neutron/plugins/ml2/drivers/README.fslsdn +++ /dev/null @@ -1,102 +0,0 @@ -===================================================== -Freescale SDN Mechanism Driver for Neutron ML2 plugin -===================================================== - -Introduction -============ - -Freescale SDN (FSL-SDN) Mechanism Driver is an add-on support for ML2 plugin -for Neutron. - -It supports the Cloud Resource Discovery (CRD) service by updating -Network, Subnet and Port Create/Update/Delete data into the CRD database. - -CRD service manages network nodes, virtual network appliances and openflow -controller based network applications. - -Basic work flow ---------------- - -:: - - +---------------------------------+ - | | - | Neutron Server | - | (with ML2 plugin) | - | | - | +-------------------------------+ - | | Freescale SDN | - | | Mechanism Driver | - +-+--------+----------------------+ - | - | ReST API - | - +----------+-------------+ - | CRD server | - +------------------------+ - - - -How does Freescale SDN Mechanism Driver work? -=========================================== - -- Freescale Mechanism driver handles the following postcommit operations. - - Network create/update/delete - - Subnet create/update/delete - - Port create/delete - -Sequence diagram : create_network ---------------------------------- - -:: - - create_network - { - neutron -> ML2_plugin - ML2_plugin -> FSL-SDN-MD - FSL-SDN-MD -> crd_service - FSL-SDN-MD <-- crd_service - ML2_plugin <-- FSL-SDN-MD - neutron <-- ML2_plugin - } - -- Supported network types by FSL OF Controller include vlan and vxlan. - -- Freescale SDN mechanism driver handles VM port binding within in the - mechanism driver (like ODL MD). - -- 'bind_port' function verifies the supported network types (vlan,vxlan) - and calls context.set_binding with binding details. - -- Flow management in OVS is handled by Freescale Openflow Controller. - - -How to use Freescale SDN Mechanism Driver? -========================================== - -Configuring ML2 Plugin ----------------------- - -In [ml2] section of /etc/neutron/plugins/ml2/ml2_conf.ini, -modify 'mechanism_drivers' attributes as: - -:: - - mechanism_drivers = fslsdn - -Configuring FSLSDN Mechanism Driver ------------------------------------ - -Update /etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini, as below. - -:: - - [ml2_fslsdn] - crd_auth_strategy = keystone - crd_url = http://127.0.0.1:9797 - crd_auth_url = http://127.0.0.1:5000/v2.0/ - crd_tenant_name = service - crd_password = <-service-password-> - crd_user_name = <-service-username-> - -CRD service must be running in the controller. diff --git a/neutron/plugins/ml2/drivers/README.odl b/neutron/plugins/ml2/drivers/README.odl deleted file mode 100644 index eef8d4441..000000000 --- a/neutron/plugins/ml2/drivers/README.odl +++ /dev/null @@ -1,41 +0,0 @@ -OpenDaylight ML2 MechanismDriver -================================ -OpenDaylight is an Open Source SDN Controller developed by a plethora of -companies and hosted by the Linux Foundation. The OpenDaylight website -contains more information on the capabilities OpenDaylight provides: - - http://www.opendaylight.org - -Theory of operation -=================== -The OpenStack Neutron integration with OpenDaylight consists of the ML2 -MechanismDriver which acts as a REST proxy and passess all Neutron API -calls into OpenDaylight. OpenDaylight contains a NB REST service (called -the NeutronAPIService) which caches data from these proxied API calls and -makes it available to other services inside of OpenDaylight. One current -user of the SB side of the NeutronAPIService is the OVSDB code in -OpenDaylight. OVSDB uses the neutron information to isolate tenant networks -using GRE or VXLAN tunnels. - -How to use the OpenDaylight ML2 MechanismDriver -=============================================== -To use the ML2 MechanismDriver, you need to ensure you have it configured -as one of the "mechanism_drivers" in ML2: - - mechanism_drivers=opendaylight - -The next step is to setup the "[ml2_odl]" section in either the ml2_conf.ini -file or in a separate ml2_conf_odl.ini file. An example is shown below: - - [ml2_odl] - password = admin - username = admin - url = http://192.168.100.1:8080/controller/nb/v2/neutron - -When starting OpenDaylight, ensure you have the SimpleForwarding application -disabled or remove the .jar file from the plugins directory. Also ensure you -start OpenDaylight before you start OpenStack Neutron. - -There is devstack support for this which will automatically pull down OpenDaylight -and start it as part of devstack as well. The patch for this will likely merge -around the same time as this patch merges. diff --git a/neutron/plugins/ml2/drivers/__init__.py b/neutron/plugins/ml2/drivers/__init__.py deleted file mode 100644 index 788cea1f7..000000000 --- a/neutron/plugins/ml2/drivers/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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. diff --git a/neutron/plugins/ml2/drivers/brocade/README.md b/neutron/plugins/ml2/drivers/brocade/README.md deleted file mode 100644 index 5cf5a7571..000000000 --- a/neutron/plugins/ml2/drivers/brocade/README.md +++ /dev/null @@ -1,60 +0,0 @@ -Brocade ML2 Mechanism driver from ML2 plugin -============================================ - -* up-to-date version of these instructions are located at: - http://50.56.236.34/docs/brocade-ml2-mechanism.txt -* N.B.: Please see Prerequisites section regarding ncclient (netconf client library) -* Supports VCS (Virtual Cluster of Switches) -* Issues/Questions/Bugs: sharis@brocade.com - - - - 1. VDX 67xx series of switches - 2. VDX 87xx series of switches - -ML2 plugin requires mechanism driver to support configuring of hardware switches. -Brocade Mechanism for ML2 uses NETCONF at the backend to configure the Brocade switch. -Currently the mechanism drivers support VLANs only. - - +------------+ +------------+ +-------------+ - | | | | | | - Neutron | | | | | Brocade | - v2.0 | Openstack | | Brocade | NETCONF | VCS Switch | - ----+ Neutron +--------+ Mechanism +----------+ | - | ML2 | | Driver | | VDX 67xx | - | Plugin | | | | VDX 87xx | - | | | | | | - | | | | | | - +------------+ +------------+ +-------------+ - - -Configuration - -In order to use this mechnism the brocade configuration file needs to be edited with the appropriate -configuration information: - - % cat /etc/neutron/plugins/ml2/ml2_conf_brocade.ini - [switch] - username = admin - password = password - address = - ostype = NOS - physical_networks = phys1 - -Additionally the brocade mechanism driver needs to be enabled from the ml2 config file: - - % cat /etc/neutron/plugins/ml2/ml2_conf.ini - - [ml2] - tenant_network_types = vlan - type_drivers = local,flat,vlan,gre,vxlan - mechanism_drivers = openvswitch,brocade - # OR mechanism_drivers = openvswitch,linuxbridge,hyperv,brocade - ... - ... - ... - - -Required L2 Agent - -This mechanism driver works in conjunction with an L2 Agent. The agent should be loaded as well in order for it to configure the virtual network int the host machine. Please see the configuration above. Atleast one of linuxbridge or openvswitch must be specified. diff --git a/neutron/plugins/ml2/drivers/brocade/__init__.py b/neutron/plugins/ml2/drivers/brocade/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/ml2/drivers/brocade/db/__init__.py b/neutron/plugins/ml2/drivers/brocade/db/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/ml2/drivers/brocade/db/models.py b/neutron/plugins/ml2/drivers/brocade/db/models.py deleted file mode 100644 index 249540527..000000000 --- a/neutron/plugins/ml2/drivers/brocade/db/models.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 2014 Brocade Communications System, 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. -# -# Authors: -# Shiv Haris (sharis@brocade.com) -# Varma Bhupatiraju (vbhupati@#brocade.com) - - -"""Brocade specific database schema/model.""" -import sqlalchemy as sa - -from neutron.db import model_base -from neutron.db import models_v2 - - -class ML2_BrocadeNetwork(model_base.BASEV2, models_v2.HasId, - models_v2.HasTenant): - """Schema for brocade network.""" - - vlan = sa.Column(sa.String(10)) - segment_id = sa.Column(sa.String(36)) - network_type = sa.Column(sa.String(10)) - - -class ML2_BrocadePort(model_base.BASEV2, models_v2.HasId, - models_v2.HasTenant): - """Schema for brocade port.""" - - network_id = sa.Column(sa.String(36), - sa.ForeignKey("ml2_brocadenetworks.id"), - nullable=False) - admin_state_up = sa.Column(sa.Boolean, nullable=False) - physical_interface = sa.Column(sa.String(36)) - vlan_id = sa.Column(sa.String(36)) - - -def create_network(context, net_id, vlan, segment_id, network_type, tenant_id): - """Create a brocade specific network/port-profiles.""" - - # only network_type of vlan is supported - session = context.session - with session.begin(subtransactions=True): - net = get_network(context, net_id, None) - if not net: - net = ML2_BrocadeNetwork(id=net_id, vlan=vlan, - segment_id=segment_id, - network_type='vlan', - tenant_id=tenant_id) - session.add(net) - return net - - -def delete_network(context, net_id): - """Delete a brocade specific network/port-profiles.""" - - session = context.session - with session.begin(subtransactions=True): - net = get_network(context, net_id, None) - if net: - session.delete(net) - - -def get_network(context, net_id, fields=None): - """Get brocade specific network, with vlan extension.""" - - session = context.session - return session.query(ML2_BrocadeNetwork).filter_by(id=net_id).first() - - -def get_networks(context, filters=None, fields=None): - """Get all brocade specific networks.""" - - session = context.session - return session.query(ML2_BrocadeNetwork).all() - - -def create_port(context, port_id, network_id, physical_interface, - vlan_id, tenant_id, admin_state_up): - """Create a brocade specific port, has policy like vlan.""" - - session = context.session - with session.begin(subtransactions=True): - port = get_port(context, port_id) - if not port: - port = ML2_BrocadePort(id=port_id, - network_id=network_id, - physical_interface=physical_interface, - vlan_id=vlan_id, - admin_state_up=admin_state_up, - tenant_id=tenant_id) - session.add(port) - - return port - - -def get_port(context, port_id): - """get a brocade specific port.""" - - session = context.session - return session.query(ML2_BrocadePort).filter_by(id=port_id).first() - - -def get_ports(context, network_id=None): - """get a brocade specific port.""" - - session = context.session - return session.query(ML2_BrocadePort).filter_by( - network_id=network_id).all() - - -def delete_port(context, port_id): - """delete brocade specific port.""" - - session = context.session - with session.begin(subtransactions=True): - port = get_port(context, port_id) - if port: - session.delete(port) - - -def update_port_state(context, port_id, admin_state_up): - """Update port attributes.""" - - session = context.session - with session.begin(subtransactions=True): - session.query(ML2_BrocadePort).filter_by( - id=port_id).update({'admin_state_up': admin_state_up}) diff --git a/neutron/plugins/ml2/drivers/brocade/mechanism_brocade.py b/neutron/plugins/ml2/drivers/brocade/mechanism_brocade.py deleted file mode 100644 index 015921df5..000000000 --- a/neutron/plugins/ml2/drivers/brocade/mechanism_brocade.py +++ /dev/null @@ -1,385 +0,0 @@ -# Copyright 2014 Brocade Communications System, 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. -# -# Author: -# Shiv Haris (shivharis@hotmail.com) - - -"""Implentation of Brocade ML2 Mechanism driver for ML2 Plugin.""" - -from oslo.config import cfg - -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.ml2 import driver_api -from neutron.plugins.ml2.drivers.brocade.db import models as brocade_db - -LOG = logging.getLogger(__name__) -MECHANISM_VERSION = 0.9 -NOS_DRIVER = 'neutron.plugins.ml2.drivers.brocade.nos.nosdriver.NOSdriver' - -ML2_BROCADE = [cfg.StrOpt('address', default='', - help=_('The address of the host to SSH to')), - cfg.StrOpt('username', default='admin', - help=_('The SSH username to use')), - cfg.StrOpt('password', default='password', secret=True, - help=_('The SSH password to use')), - cfg.StrOpt('physical_networks', default='', - help=_('Allowed physical networks')), - cfg.StrOpt('ostype', default='NOS', - help=_('Unused')) - ] - -cfg.CONF.register_opts(ML2_BROCADE, "ml2_brocade") - - -class BrocadeMechanism(driver_api.MechanismDriver): - """ML2 Mechanism driver for Brocade VDX switches. This is the upper - layer driver class that interfaces to lower layer (NETCONF) below. - - """ - - def __init__(self): - self._driver = None - self._physical_networks = None - self._switch = None - self.initialize() - - def initialize(self): - """Initilize of variables needed by this class.""" - - self._physical_networks = cfg.CONF.ml2_brocade.physical_networks - self.brocade_init() - - def brocade_init(self): - """Brocade specific initialization for this class.""" - - self._switch = {'address': cfg.CONF.ml2_brocade.address, - 'username': cfg.CONF.ml2_brocade.username, - 'password': cfg.CONF.ml2_brocade.password - } - self._driver = importutils.import_object(NOS_DRIVER) - - def create_network_precommit(self, mech_context): - """Create Network in the mechanism specific database table.""" - - network = mech_context.current - context = mech_context._plugin_context - tenant_id = network['tenant_id'] - network_id = network['id'] - - segments = mech_context.network_segments - # currently supports only one segment per network - segment = segments[0] - - network_type = segment['network_type'] - vlan_id = segment['segmentation_id'] - segment_id = segment['id'] - - if segment['physical_network'] not in self._physical_networks: - raise Exception( - _("Brocade Mechanism: failed to create network, " - "network cannot be created in the configured " - "physical network")) - - if network_type != 'vlan': - raise Exception( - _("Brocade Mechanism: failed to create network, " - "only network type vlan is supported")) - - try: - brocade_db.create_network(context, network_id, vlan_id, - segment_id, network_type, tenant_id) - except Exception: - LOG.exception( - _("Brocade Mechanism: failed to create network in db")) - raise Exception( - _("Brocade Mechanism: create_network_precommit failed")) - - LOG.info(_("create network (precommit): %(network_id)s " - "of network type = %(network_type)s " - "with vlan = %(vlan_id)s " - "for tenant %(tenant_id)s"), - {'network_id': network_id, - 'network_type': network_type, - 'vlan_id': vlan_id, - 'tenant_id': tenant_id}) - - def create_network_postcommit(self, mech_context): - """Create Network as a portprofile on the switch.""" - - LOG.debug(_("create_network_postcommit: called")) - - network = mech_context.current - # use network_id to get the network attributes - # ONLY depend on our db for getting back network attributes - # this is so we can replay postcommit from db - context = mech_context._plugin_context - - network_id = network['id'] - network = brocade_db.get_network(context, network_id) - network_type = network['network_type'] - tenant_id = network['tenant_id'] - vlan_id = network['vlan'] - - try: - self._driver.create_network(self._switch['address'], - self._switch['username'], - self._switch['password'], - vlan_id) - except Exception: - LOG.exception(_("Brocade NOS driver: failed in create network")) - brocade_db.delete_network(context, network_id) - raise Exception( - _("Brocade Mechanism: create_network_postcommmit failed")) - - LOG.info(_("created network (postcommit): %(network_id)s" - " of network type = %(network_type)s" - " with vlan = %(vlan_id)s" - " for tenant %(tenant_id)s"), - {'network_id': network_id, - 'network_type': network_type, - 'vlan_id': vlan_id, - 'tenant_id': tenant_id}) - - def delete_network_precommit(self, mech_context): - """Delete Network from the plugin specific database table.""" - - LOG.debug(_("delete_network_precommit: called")) - - network = mech_context.current - network_id = network['id'] - vlan_id = network['provider:segmentation_id'] - tenant_id = network['tenant_id'] - - context = mech_context._plugin_context - - try: - brocade_db.delete_network(context, network_id) - except Exception: - LOG.exception( - _("Brocade Mechanism: failed to delete network in db")) - raise Exception( - _("Brocade Mechanism: delete_network_precommit failed")) - - LOG.info(_("delete network (precommit): %(network_id)s" - " with vlan = %(vlan_id)s" - " for tenant %(tenant_id)s"), - {'network_id': network_id, - 'vlan_id': vlan_id, - 'tenant_id': tenant_id}) - - def delete_network_postcommit(self, mech_context): - """Delete network which translates to removng portprofile - from the switch. - """ - - LOG.debug(_("delete_network_postcommit: called")) - network = mech_context.current - network_id = network['id'] - vlan_id = network['provider:segmentation_id'] - tenant_id = network['tenant_id'] - - try: - self._driver.delete_network(self._switch['address'], - self._switch['username'], - self._switch['password'], - vlan_id) - except Exception: - LOG.exception(_("Brocade NOS driver: failed to delete network")) - raise Exception( - _("Brocade switch exception, " - "delete_network_postcommit failed")) - - LOG.info(_("delete network (postcommit): %(network_id)s" - " with vlan = %(vlan_id)s" - " for tenant %(tenant_id)s"), - {'network_id': network_id, - 'vlan_id': vlan_id, - 'tenant_id': tenant_id}) - - def update_network_precommit(self, mech_context): - """Noop now, it is left here for future.""" - pass - - def update_network_postcommit(self, mech_context): - """Noop now, it is left here for future.""" - pass - - def create_port_precommit(self, mech_context): - """Create logical port on the switch (db update).""" - - LOG.debug(_("create_port_precommit: called")) - - port = mech_context.current - port_id = port['id'] - network_id = port['network_id'] - tenant_id = port['tenant_id'] - admin_state_up = port['admin_state_up'] - - context = mech_context._plugin_context - - network = brocade_db.get_network(context, network_id) - vlan_id = network['vlan'] - - try: - brocade_db.create_port(context, port_id, network_id, - None, - vlan_id, tenant_id, admin_state_up) - except Exception: - LOG.exception(_("Brocade Mechanism: failed to create port in db")) - raise Exception( - _("Brocade Mechanism: create_port_precommit failed")) - - def create_port_postcommit(self, mech_context): - """Associate the assigned MAC address to the portprofile.""" - - LOG.debug(_("create_port_postcommit: called")) - - port = mech_context.current - port_id = port['id'] - network_id = port['network_id'] - tenant_id = port['tenant_id'] - - context = mech_context._plugin_context - - network = brocade_db.get_network(context, network_id) - vlan_id = network['vlan'] - - interface_mac = port['mac_address'] - - # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx - mac = self.mac_reformat_62to34(interface_mac) - try: - self._driver.associate_mac_to_network(self._switch['address'], - self._switch['username'], - self._switch['password'], - vlan_id, - mac) - except Exception: - LOG.exception( - _("Brocade NOS driver: failed to associate mac %s") - % interface_mac) - raise Exception( - _("Brocade switch exception: create_port_postcommit failed")) - - LOG.info( - _("created port (postcommit): port_id=%(port_id)s" - " network_id=%(network_id)s tenant_id=%(tenant_id)s"), - {'port_id': port_id, - 'network_id': network_id, 'tenant_id': tenant_id}) - - def delete_port_precommit(self, mech_context): - """Delete logical port on the switch (db update).""" - - LOG.debug(_("delete_port_precommit: called")) - port = mech_context.current - port_id = port['id'] - - context = mech_context._plugin_context - - try: - brocade_db.delete_port(context, port_id) - except Exception: - LOG.exception(_("Brocade Mechanism: failed to delete port in db")) - raise Exception( - _("Brocade Mechanism: delete_port_precommit failed")) - - def delete_port_postcommit(self, mech_context): - """Dissociate MAC address from the portprofile.""" - - LOG.debug(_("delete_port_postcommit: called")) - port = mech_context.current - port_id = port['id'] - network_id = port['network_id'] - tenant_id = port['tenant_id'] - - context = mech_context._plugin_context - - network = brocade_db.get_network(context, network_id) - vlan_id = network['vlan'] - - interface_mac = port['mac_address'] - - # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx - mac = self.mac_reformat_62to34(interface_mac) - try: - self._driver.dissociate_mac_from_network( - self._switch['address'], - self._switch['username'], - self._switch['password'], - vlan_id, - mac) - except Exception: - LOG.exception( - _("Brocade NOS driver: failed to dissociate MAC %s") % - interface_mac) - raise Exception( - _("Brocade switch exception, delete_port_postcommit failed")) - - LOG.info( - _("delete port (postcommit): port_id=%(port_id)s" - " network_id=%(network_id)s tenant_id=%(tenant_id)s"), - {'port_id': port_id, - 'network_id': network_id, 'tenant_id': tenant_id}) - - def update_port_precommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("update_port_precommit(self: called")) - - def update_port_postcommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("update_port_postcommit: called")) - - def create_subnet_precommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("create_subnetwork_precommit: called")) - - def create_subnet_postcommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("create_subnetwork_postcommit: called")) - - def delete_subnet_precommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("delete_subnetwork_precommit: called")) - - def delete_subnet_postcommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("delete_subnetwork_postcommit: called")) - - def update_subnet_precommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("update_subnet_precommit(self: called")) - - def update_subnet_postcommit(self, mech_context): - """Noop now, it is left here for future.""" - LOG.debug(_("update_subnet_postcommit: called")) - - @staticmethod - def mac_reformat_62to34(interface_mac): - """Transform MAC address format. - - Transforms from 6 groups of 2 hexadecimal numbers delimited by ":" - to 3 groups of 4 hexadecimals numbers delimited by ".". - - :param interface_mac: MAC address in the format xx:xx:xx:xx:xx:xx - :type interface_mac: string - :returns: MAC address in the format xxxx.xxxx.xxxx - :rtype: string - """ - - mac = interface_mac.replace(":", "") - mac = mac[0:4] + "." + mac[4:8] + "." + mac[8:12] - return mac diff --git a/neutron/plugins/ml2/drivers/brocade/nos/__init__.py b/neutron/plugins/ml2/drivers/brocade/nos/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/ml2/drivers/brocade/nos/nctemplates.py b/neutron/plugins/ml2/drivers/brocade/nos/nctemplates.py deleted file mode 100644 index dbf7575de..000000000 --- a/neutron/plugins/ml2/drivers/brocade/nos/nctemplates.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (c) 2014 Brocade Communications 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. -# -# Authors: -# Varma Bhupatiraju (vbhupati@#brocade.com) -# Shiv Haris (sharis@brocade.com) - - -"""NOS NETCONF XML Configuration Command Templates. - -Interface Configuration Commands -""" - -# Create VLAN (vlan_id) -CREATE_VLAN_INTERFACE = """ - - - - - {vlan_id} - - - - -""" - -# Delete VLAN (vlan_id) -DELETE_VLAN_INTERFACE = """ - - - - - {vlan_id} - - - - -""" - -# -# AMPP Life-cycle Management Configuration Commands -# - -# Create AMPP port-profile (port_profile_name) -CREATE_PORT_PROFILE = """ - - - {name} - - -""" - -# Create VLAN sub-profile for port-profile (port_profile_name) -CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """ - - - {name} - - - -""" - -# Configure L2 mode for VLAN sub-profile (port_profile_name) -CONFIGURE_L2_MODE_FOR_VLAN_PROFILE = """ - - - {name} - - - - - -""" - -# Configure trunk mode for VLAN sub-profile (port_profile_name) -CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE = """ - - - {name} - - - - trunk - - - - - -""" - -# Configure allowed VLANs for VLAN sub-profile -# (port_profile_name, allowed_vlan, native_vlan) -CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE = """ - - - {name} - - - - - - {vlan_id} - - - - - - - -""" - -# Delete port-profile (port_profile_name) -DELETE_PORT_PROFILE = """ - - - {name} - - -""" - -# Activate port-profile (port_profile_name) -ACTIVATE_PORT_PROFILE = """ - - - - {name} - - - - -""" - -# Deactivate port-profile (port_profile_name) -DEACTIVATE_PORT_PROFILE = """ - - - - {name} - - - - -""" - -# Associate MAC address to port-profile (port_profile_name, mac_address) -ASSOCIATE_MAC_TO_PORT_PROFILE = """ - - - - {name} - - {mac_address} - - - - -""" - -# Dissociate MAC address from port-profile (port_profile_name, mac_address) -DISSOCIATE_MAC_FROM_PORT_PROFILE = """ - - - - {name} - - {mac_address} - - - - -""" - -# -# Constants -# - -# Port profile naming convention for Neutron networks -OS_PORT_PROFILE_NAME = "openstack-profile-{id}" - -# Port profile filter expressions -PORT_PROFILE_XPATH_FILTER = "/port-profile" -PORT_PROFILE_NAME_XPATH_FILTER = "/port-profile[name='{name}']" diff --git a/neutron/plugins/ml2/drivers/brocade/nos/nosdriver.py b/neutron/plugins/ml2/drivers/brocade/nos/nosdriver.py deleted file mode 100644 index f647370ae..000000000 --- a/neutron/plugins/ml2/drivers/brocade/nos/nosdriver.py +++ /dev/null @@ -1,236 +0,0 @@ -# Copyright 2014 Brocade Communications System, 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. -# -# Authors: -# Varma Bhupatiraju (vbhupati@brocade.com) -# Shiv Haris (shivharis@hotmail.com) - - -"""Brocade NOS Driver implements NETCONF over SSHv2 for -Neutron network life-cycle management. -""" - -from ncclient import manager - -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.ml2.drivers.brocade.nos import nctemplates as template - - -LOG = logging.getLogger(__name__) -SSH_PORT = 22 - - -def nos_unknown_host_cb(host, fingerprint): - """An unknown host callback. - - Returns `True` if it finds the key acceptable, - and `False` if not. This default callback for NOS always returns 'True' - (i.e. trusts all hosts for now). - """ - return True - - -class NOSdriver(): - """NOS NETCONF interface driver for Neutron network. - - Handles life-cycle management of Neutron network (leverages AMPP on NOS) - """ - - def __init__(self): - self.mgr = None - - def connect(self, host, username, password): - """Connect via SSH and initialize the NETCONF session.""" - - # Use the persisted NETCONF connection - if self.mgr and self.mgr.connected: - return self.mgr - - # check if someone forgot to edit the conf file with real values - if host == '': - raise Exception(_("Brocade Switch IP address is not set, " - "check config ml2_conf_brocade.ini file")) - - # Open new NETCONF connection - try: - self.mgr = manager.connect(host=host, port=SSH_PORT, - username=username, password=password, - unknown_host_cb=nos_unknown_host_cb) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_("Connect failed to switch")) - - LOG.debug(_("Connect success to host %(host)s:%(ssh_port)d"), - dict(host=host, ssh_port=SSH_PORT)) - return self.mgr - - def close_session(self): - """Close NETCONF session.""" - if self.mgr: - self.mgr.close_session() - self.mgr = None - - def create_network(self, host, username, password, net_id): - """Creates a new virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.create_vlan_interface(mgr, net_id) - self.create_port_profile(mgr, name) - self.create_vlan_profile_for_port_profile(mgr, name) - self.configure_l2_mode_for_vlan_profile(mgr, name) - self.configure_trunk_mode_for_vlan_profile(mgr, name) - self.configure_allowed_vlans_for_vlan_profile(mgr, name, net_id) - self.activate_port_profile(mgr, name) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error")) - self.close_session() - - def delete_network(self, host, username, password, net_id): - """Deletes a virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.deactivate_port_profile(mgr, name) - self.delete_port_profile(mgr, name) - self.delete_vlan_interface(mgr, net_id) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error")) - self.close_session() - - def associate_mac_to_network(self, host, username, password, - net_id, mac): - """Associates a MAC address to virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.associate_mac_to_port_profile(mgr, name, mac) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error")) - self.close_session() - - def dissociate_mac_from_network(self, host, username, password, - net_id, mac): - """Dissociates a MAC address from virtual network.""" - - name = template.OS_PORT_PROFILE_NAME.format(id=net_id) - try: - mgr = self.connect(host, username, password) - self.dissociate_mac_from_port_profile(mgr, name, mac) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_("NETCONF error")) - self.close_session() - - def create_vlan_interface(self, mgr, vlan_id): - """Configures a VLAN interface.""" - - confstr = template.CREATE_VLAN_INTERFACE.format(vlan_id=vlan_id) - mgr.edit_config(target='running', config=confstr) - - def delete_vlan_interface(self, mgr, vlan_id): - """Deletes a VLAN interface.""" - - confstr = template.DELETE_VLAN_INTERFACE.format(vlan_id=vlan_id) - mgr.edit_config(target='running', config=confstr) - - def get_port_profiles(self, mgr): - """Retrieves all port profiles.""" - - filterstr = template.PORT_PROFILE_XPATH_FILTER - response = mgr.get_config(source='running', - filter=('xpath', filterstr)).data_xml - return response - - def get_port_profile(self, mgr, name): - """Retrieves a port profile.""" - - filterstr = template.PORT_PROFILE_NAME_XPATH_FILTER.format(name=name) - response = mgr.get_config(source='running', - filter=('xpath', filterstr)).data_xml - return response - - def create_port_profile(self, mgr, name): - """Creates a port profile.""" - - confstr = template.CREATE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def delete_port_profile(self, mgr, name): - """Deletes a port profile.""" - - confstr = template.DELETE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def activate_port_profile(self, mgr, name): - """Activates a port profile.""" - - confstr = template.ACTIVATE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def deactivate_port_profile(self, mgr, name): - """Deactivates a port profile.""" - - confstr = template.DEACTIVATE_PORT_PROFILE.format(name=name) - mgr.edit_config(target='running', config=confstr) - - def associate_mac_to_port_profile(self, mgr, name, mac_address): - """Associates a MAC address to a port profile.""" - - confstr = template.ASSOCIATE_MAC_TO_PORT_PROFILE.format( - name=name, mac_address=mac_address) - mgr.edit_config(target='running', config=confstr) - - def dissociate_mac_from_port_profile(self, mgr, name, mac_address): - """Dissociates a MAC address from a port profile.""" - - confstr = template.DISSOCIATE_MAC_FROM_PORT_PROFILE.format( - name=name, mac_address=mac_address) - mgr.edit_config(target='running', config=confstr) - - def create_vlan_profile_for_port_profile(self, mgr, name): - """Creates VLAN sub-profile for port profile.""" - - confstr = template.CREATE_VLAN_PROFILE_FOR_PORT_PROFILE.format( - name=name) - mgr.edit_config(target='running', config=confstr) - - def configure_l2_mode_for_vlan_profile(self, mgr, name): - """Configures L2 mode for VLAN sub-profile.""" - - confstr = template.CONFIGURE_L2_MODE_FOR_VLAN_PROFILE.format( - name=name) - mgr.edit_config(target='running', config=confstr) - - def configure_trunk_mode_for_vlan_profile(self, mgr, name): - """Configures trunk mode for VLAN sub-profile.""" - - confstr = template.CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE.format( - name=name) - mgr.edit_config(target='running', config=confstr) - - def configure_allowed_vlans_for_vlan_profile(self, mgr, name, vlan_id): - """Configures allowed VLANs for VLAN sub-profile.""" - - confstr = template.CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE.format( - name=name, vlan_id=vlan_id) - mgr.edit_config(target='running', config=confstr) diff --git a/neutron/plugins/ml2/drivers/cisco/__init__.py b/neutron/plugins/ml2/drivers/cisco/__init__.py deleted file mode 100644 index 788cea1f7..000000000 --- a/neutron/plugins/ml2/drivers/cisco/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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. diff --git a/neutron/plugins/ml2/drivers/cisco/apic/__init__.py b/neutron/plugins/ml2/drivers/cisco/apic/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/ml2/drivers/cisco/apic/apic_client.py b/neutron/plugins/ml2/drivers/cisco/apic/apic_client.py deleted file mode 100644 index 202e84c1c..000000000 --- a/neutron/plugins/ml2/drivers/cisco/apic/apic_client.py +++ /dev/null @@ -1,416 +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. -# -# @author: Henry Gessau, Cisco Systems - -import collections -import time - -import requests -import requests.exceptions - -from neutron.openstack.common import jsonutils as json -from neutron.openstack.common import log as logging -from neutron.plugins.ml2.drivers.cisco.apic import exceptions as cexc - - -LOG = logging.getLogger(__name__) - -APIC_CODE_FORBIDDEN = str(requests.codes.forbidden) - - -# Info about a Managed Object's relative name (RN) and container. -class ManagedObjectName(collections.namedtuple( - 'MoPath', ['container', 'rn_fmt', 'can_create'])): - def __new__(cls, container, rn_fmt, can_create=True): - return super(ManagedObjectName, cls).__new__(cls, container, rn_fmt, - can_create) - - -class ManagedObjectClass(object): - - """Information about a Managed Object (MO) class. - - Constructs and keeps track of the distinguished name (DN) and relative - name (RN) of a managed object (MO) class. The DN is the RN of the MO - appended to the recursive RNs of its containers, i.e.: - DN = uni/container-RN/.../container-RN/object-RN - - Also keeps track of whether the MO can be created in the APIC, as some - MOs are read-only or used for specifying relationships. - """ - - supported_mos = { - 'fvTenant': ManagedObjectName(None, 'tn-%s'), - 'fvBD': ManagedObjectName('fvTenant', 'BD-%s'), - 'fvRsBd': ManagedObjectName('fvAEPg', 'rsbd'), - 'fvSubnet': ManagedObjectName('fvBD', 'subnet-[%s]'), - 'fvCtx': ManagedObjectName('fvTenant', 'ctx-%s'), - 'fvRsCtx': ManagedObjectName('fvBD', 'rsctx'), - 'fvAp': ManagedObjectName('fvTenant', 'ap-%s'), - 'fvAEPg': ManagedObjectName('fvAp', 'epg-%s'), - 'fvRsProv': ManagedObjectName('fvAEPg', 'rsprov-%s'), - 'fvRsCons': ManagedObjectName('fvAEPg', 'rscons-%s'), - 'fvRsConsIf': ManagedObjectName('fvAEPg', 'rsconsif-%s'), - 'fvRsDomAtt': ManagedObjectName('fvAEPg', 'rsdomAtt-[%s]'), - 'fvRsPathAtt': ManagedObjectName('fvAEPg', 'rspathAtt-[%s]'), - - 'vzBrCP': ManagedObjectName('fvTenant', 'brc-%s'), - 'vzSubj': ManagedObjectName('vzBrCP', 'subj-%s'), - 'vzFilter': ManagedObjectName('fvTenant', 'flt-%s'), - 'vzRsFiltAtt': ManagedObjectName('vzSubj', 'rsfiltAtt-%s'), - 'vzEntry': ManagedObjectName('vzFilter', 'e-%s'), - 'vzInTerm': ManagedObjectName('vzSubj', 'intmnl'), - 'vzRsFiltAtt__In': ManagedObjectName('vzInTerm', 'rsfiltAtt-%s'), - 'vzOutTerm': ManagedObjectName('vzSubj', 'outtmnl'), - 'vzRsFiltAtt__Out': ManagedObjectName('vzOutTerm', 'rsf