remove unnecessary neutron files under neutron/plugins

Change-Id: Ia27aa8af3701b740ea6750560f96fbf9ec704465
This commit is contained in:
Isaku Yamahata 2014-06-26 16:04:55 +09:00
parent ff34dae544
commit 34b7393388
397 changed files with 0 additions and 64347 deletions

View File

@ -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

View File

@ -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.
#

View File

@ -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()

View File

@ -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 <tenant>:<source>:<destination>:<action>"
" 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)

View File

@ -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.

View File

@ -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})

View File

@ -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)

View File

@ -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.

View File

@ -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},
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 <ip>:<port>. '
'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)

View File

@ -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.
#

View File

@ -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()

View File

@ -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

View File

@ -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())

View File

@ -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

View File

@ -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 = <switch mgmt ip 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)

View File

@ -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.

View File

@ -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.

View File

@ -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})

View File

@ -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.

View File

@ -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

View File

@ -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 = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface-vlan xmlns="urn:brocade.com:mgmt:brocade-interface">
<interface>
<vlan>
<name>{vlan_id}</name>
</vlan>
</interface>
</interface-vlan>
</config>
"""
# Delete VLAN (vlan_id)
DELETE_VLAN_INTERFACE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface-vlan xmlns="urn:brocade.com:mgmt:brocade-interface">
<interface>
<vlan operation="delete">
<name>{vlan_id}</name>
</vlan>
</interface>
</interface-vlan>
</config>
"""
#
# AMPP Life-cycle Management Configuration Commands
#
# Create AMPP port-profile (port_profile_name)
CREATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
</port-profile>
</config>
"""
# Create VLAN sub-profile for port-profile (port_profile_name)
CREATE_VLAN_PROFILE_FOR_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile/>
</port-profile>
</config>
"""
# Configure L2 mode for VLAN sub-profile (port_profile_name)
CONFIGURE_L2_MODE_FOR_VLAN_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile>
<switchport/>
</vlan-profile>
</port-profile>
</config>
"""
# Configure trunk mode for VLAN sub-profile (port_profile_name)
CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile>
<switchport>
<mode>
<vlan-mode>trunk</vlan-mode>
</mode>
</switchport>
</vlan-profile>
</port-profile>
</config>
"""
# Configure allowed VLANs for VLAN sub-profile
# (port_profile_name, allowed_vlan, native_vlan)
CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<name>{name}</name>
<vlan-profile>
<switchport>
<trunk>
<allowed>
<vlan>
<add>{vlan_id}</add>
</vlan>
</allowed>
</trunk>
</switchport>
</vlan-profile>
</port-profile>
</config>
"""
# Delete port-profile (port_profile_name)
DELETE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile
xmlns="urn:brocade.com:mgmt:brocade-port-profile" operation="delete">
<name>{name}</name>
</port-profile>
</config>
"""
# Activate port-profile (port_profile_name)
ACTIVATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<activate/>
</port-profile>
</port-profile-global>
</config>
"""
# Deactivate port-profile (port_profile_name)
DEACTIVATE_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<activate
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete" />
</port-profile>
</port-profile-global>
</config>
"""
# Associate MAC address to port-profile (port_profile_name, mac_address)
ASSOCIATE_MAC_TO_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<static>
<mac-address>{mac_address}</mac-address>
</static>
</port-profile>
</port-profile-global>
</config>
"""
# Dissociate MAC address from port-profile (port_profile_name, mac_address)
DISSOCIATE_MAC_FROM_PORT_PROFILE = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<port-profile-global xmlns="urn:brocade.com:mgmt:brocade-port-profile">
<port-profile>
<name>{name}</name>
<static
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
<mac-address>{mac_address}</mac-address>
</static>
</port-profile>
</port-profile-global>
</config>
"""
#
# 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}']"

View File

@ -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)

View File

@ -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 <network>
(after running check that PP is created on the switch)
% noscli.py delete <network>
(after running check that PP is deleted from the switch)
% noscli.py associate <network> <mac>
(after running check that MAC is associated with PP)
% noscli.py dissociate <network> <mac>
(after running check that MAC is dissociated from the PP)

View File

@ -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 <id>
delete <id>
associate <id> <mac>
dissociate <id> <mac>
"""
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)

View File

@ -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])

View File

@ -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)

View File

@ -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

View File

@ -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.
#

View File

@ -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.

View File

@ -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

View File

@ -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]

View File

@ -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.")

View File

@ -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.')

View File

@ -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:
# {('<device ID>', '<device ipaddr>', '<keyword>'): '<value>', ...}
#
# 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

View File

@ -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.
#

File diff suppressed because it is too large Load Diff

View File

@ -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 - '<integer>-<integer>'
multicast_ip_index - <integer>
multicast_ip_range - '<ip>-<ip>'
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))

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 ("<NexusPortBinding(%s,%s,%s,%s)>" %
(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
)

View File

@ -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.

View File

@ -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']))

View File

@ -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']))

View File

@ -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)]

View File

@ -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 <tenant-id> <net-name> \
--n1kv:profile_id <id>
(shell) port-create --tenant_id <tenant-id> <net-name> \
--n1kv:profile_id <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 {}

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.
#

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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
"""

View File

@ -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)

View File

@ -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

View File

@ -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 = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<configure>
<__XML__MODE__exec_configure>%s
</__XML__MODE__exec_configure>
</configure>
</config>
"""
CMD_VLAN_CONF_SNIPPET = """
<vlan>
<vlan-id-create-delete>
<__XML__PARAM_value>%s</__XML__PARAM_value>
<__XML__MODE_vlan>
<name>
<vlan-name>%s</vlan-name>
</name>
</__XML__MODE_vlan>
</vlan-id-create-delete>
</vlan>
"""
CMD_VLAN_ACTIVE_SNIPPET = """
<vlan>
<vlan-id-create-delete>
<__XML__PARAM_value>%s</__XML__PARAM_value>
<__XML__MODE_vlan>
<state>
<vstate>active</vstate>
</state>
</__XML__MODE_vlan>
</vlan-id-create-delete>
</vlan>
"""
CMD_VLAN_NO_SHUTDOWN_SNIPPET = """
<vlan>
<vlan-id-create-delete>
<__XML__PARAM_value>%s</__XML__PARAM_value>
<__XML__MODE_vlan>
<no>
<shutdown/>
</no>
</__XML__MODE_vlan>
</vlan-id-create-delete>
</vlan>
"""
CMD_NO_VLAN_CONF_SNIPPET = """
<no>
<vlan>
<vlan-id-create-delete>
<__XML__PARAM_value>%s</__XML__PARAM_value>
</vlan-id-create-delete>
</vlan>
</no>
"""
CMD_INT_VLAN_HEADER = """
<interface>
<%s>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport>
<trunk>
<allowed>
<vlan>"""
CMD_VLAN_ID = """
<vlan_id>%s</vlan_id>"""
CMD_VLAN_ADD_ID = """
<add>%s
</add>""" % CMD_VLAN_ID
CMD_INT_VLAN_TRAILER = """
</vlan>
</allowed>
</trunk>
</switchport>
</__XML__MODE_if-ethernet-switch>
</%s>
</interface>
"""
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 = """
<interface>
<%s>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport></switchport>
<switchport>
<trunk>
<allowed>
<vlan>
<remove>
<vlan>%s</vlan>
</remove>
</vlan>
</allowed>
</trunk>
</switchport>
</__XML__MODE_if-ethernet-switch>
</%s>
</interface>
"""
FILTER_SHOW_VLAN_BRIEF_SNIPPET = """
<show xmlns="http://www.cisco.com/nxos:1.0:vlan_mgr_cli">
<vlan>
<brief/>
</vlan>
</show>
"""
CMD_VLAN_SVI_SNIPPET = """
<interface>
<vlan>
<vlan>%s</vlan>
<__XML__MODE_vlan>
<no>
<shutdown/>
</no>
<ip>
<address>
<address>%s</address>
</address>
</ip>
</__XML__MODE_vlan>
</vlan>
</interface>
"""
CMD_NO_VLAN_SVI_SNIPPET = """
<no>
<interface>
<vlan>
<vlan>%s</vlan>
</vlan>
</interface>
</no>
"""

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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"))

View File

@ -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.

View File

@ -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

View File

@ -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,))

View File

@ -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.

View File

@ -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")

View File

@ -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}

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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 = []

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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
"""

View File

@ -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")

View File

@ -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.

View File

@ -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()

View File

@ -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()

View File

@ -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.

View File

@ -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.

View File

@ -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 <physical_network>:<vswitch> '
'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()

View File

@ -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']

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)

Some files were not shown because too many files have changed in this diff Show More