Decompose the NCS ML2 Mechanism Driver
The last of the Cisco drivers to decompose. Closes-bug: #1416713 Co-Authored-By: Nikolay Fedotov <nfedotov@cisco.com> Change-Id: Icd2b358fb0db3d859ee287225ab8eeb10d7da871
This commit is contained in:
parent
fca57ab604
commit
9cac5c3a9f
@ -402,14 +402,3 @@ will be removed. The following aspects are captured:
|
|||||||
+===============================+=======================+===========+==================+=========+==============+
|
+===============================+=======================+===========+==================+=========+==============+
|
||||||
| freescale-nscs | ml2,fw | no | no | [D] | |
|
| freescale-nscs | ml2,fw | no | no | [D] | |
|
||||||
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
||||||
| networking-cisco_ | core,ml2,l3,fw,vpn | yes | yes | [B] | |
|
|
||||||
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
|
|
||||||
|
|
||||||
.. _networking-cisco:
|
|
||||||
|
|
||||||
Cisco
|
|
||||||
-----
|
|
||||||
|
|
||||||
* Git: https://git.openstack.org/stackforge/networking-cisco
|
|
||||||
* Launchpad: https://launchpad.net/networking-cisco
|
|
||||||
* PyPI: https://pypi.python.org/pypi/networking-cisco
|
|
||||||
|
@ -92,6 +92,8 @@ capabilities of Neutron, the Neutron API, or a combination of both.
|
|||||||
+-------------------------------+-----------------------+
|
+-------------------------------+-----------------------+
|
||||||
| networking-brocade_ | ml2,l3 |
|
| networking-brocade_ | ml2,l3 |
|
||||||
+-------------------------------+-----------------------+
|
+-------------------------------+-----------------------+
|
||||||
|
| networking-cisco_ | core,ml2,l3,fw,vpn |
|
||||||
|
+-------------------------------+-----------------------+
|
||||||
| networking-edge-vpn_ | vpn |
|
| networking-edge-vpn_ | vpn |
|
||||||
+-------------------------------+-----------------------+
|
+-------------------------------+-----------------------+
|
||||||
| networking-hyperv_ | ml2 |
|
| networking-hyperv_ | ml2 |
|
||||||
@ -185,6 +187,17 @@ Brocade
|
|||||||
* Launchpad: https://launchpad.net/networking-brocade
|
* Launchpad: https://launchpad.net/networking-brocade
|
||||||
* PyPI: https://pypi.python.org/pypi/networking-brocade
|
* PyPI: https://pypi.python.org/pypi/networking-brocade
|
||||||
|
|
||||||
|
.. _networking-cisco:
|
||||||
|
|
||||||
|
Cisco
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Git: https://github.com/stackforge/networking-cisco
|
||||||
|
* Launchpad: https://launchpad.net/networking-cisco
|
||||||
|
* PyPI: https://pypi.python.org/pypi/networking-cisco
|
||||||
|
|
||||||
|
.. _dragonflow:
|
||||||
|
|
||||||
DragonFlow
|
DragonFlow
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
# Defines configuration options specific to the Tail-f NCS Mechanism Driver
|
|
||||||
|
|
||||||
[ml2_ncs]
|
|
||||||
# (StrOpt) Tail-f NCS HTTP endpoint for REST access to the OpenStack
|
|
||||||
# subtree.
|
|
||||||
# If this is not set then no HTTP requests will be made.
|
|
||||||
#
|
|
||||||
# url =
|
|
||||||
# Example: url = http://ncs/api/running/services/openstack
|
|
||||||
|
|
||||||
# (StrOpt) Username for HTTP basic authentication to NCS.
|
|
||||||
# This is an optional parameter. If unspecified then no authentication is used.
|
|
||||||
#
|
|
||||||
# username =
|
|
||||||
# Example: username = admin
|
|
||||||
|
|
||||||
# (StrOpt) Password for HTTP basic authentication to NCS.
|
|
||||||
# This is an optional parameter. If unspecified then no authentication is used.
|
|
||||||
#
|
|
||||||
# password =
|
|
||||||
# Example: password = admin
|
|
||||||
|
|
||||||
# (IntOpt) Timeout in seconds to wait for NCS HTTP request completion.
|
|
||||||
# This is an optional parameter, default value is 10 seconds.
|
|
||||||
#
|
|
||||||
# timeout =
|
|
||||||
# Example: timeout = 15
|
|
||||||
|
|
@ -13,171 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import re
|
"""
|
||||||
|
ML2 Mechanism Driver for Cisco NCS.
|
||||||
|
"""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from networking_cisco.plugins.ml2.drivers.cisco.ncs import driver as cisco
|
||||||
from oslo_log import log
|
|
||||||
from oslo_serialization import jsonutils
|
|
||||||
import requests
|
|
||||||
import six
|
|
||||||
|
|
||||||
from neutron.plugins.ml2 import driver_api as api
|
NCSMechanismDriver = cisco.NCSMechanismDriver
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
ncs_opts = [
|
|
||||||
cfg.StrOpt('url',
|
|
||||||
help=_("HTTP URL of Tail-f NCS REST interface.")),
|
|
||||||
cfg.StrOpt('username',
|
|
||||||
help=_("HTTP username for authentication")),
|
|
||||||
cfg.StrOpt('password', secret=True,
|
|
||||||
help=_("HTTP password for authentication")),
|
|
||||||
cfg.IntOpt('timeout', default=10,
|
|
||||||
help=_("HTTP timeout in seconds."))
|
|
||||||
]
|
|
||||||
|
|
||||||
cfg.CONF.register_opts(ncs_opts, "ml2_ncs")
|
|
||||||
|
|
||||||
|
|
||||||
class NCSMechanismDriver(api.MechanismDriver):
|
|
||||||
|
|
||||||
"""Mechanism Driver for Tail-f Network Control System (NCS).
|
|
||||||
|
|
||||||
This driver makes portions of the Neutron database available for
|
|
||||||
service provisioning in NCS. For example, NCS can use this
|
|
||||||
information to provision physical switches and routers in response
|
|
||||||
to OpenStack configuration changes.
|
|
||||||
|
|
||||||
The database is replicated from Neutron to NCS using HTTP and JSON.
|
|
||||||
|
|
||||||
The driver has two states: out-of-sync (initially) and in-sync.
|
|
||||||
|
|
||||||
In the out-of-sync state each driver event triggers an attempt
|
|
||||||
to synchronize the complete database. On success the driver
|
|
||||||
transitions to the in-sync state.
|
|
||||||
|
|
||||||
In the in-sync state each driver event triggers synchronization
|
|
||||||
of one network or port. On success the driver stays in-sync and
|
|
||||||
on failure it transitions to the out-of-sync state.
|
|
||||||
"""
|
|
||||||
out_of_sync = True
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
self.url = cfg.CONF.ml2_ncs.url
|
|
||||||
self.timeout = cfg.CONF.ml2_ncs.timeout
|
|
||||||
self.username = cfg.CONF.ml2_ncs.username
|
|
||||||
self.password = cfg.CONF.ml2_ncs.password
|
|
||||||
|
|
||||||
# Postcommit hooks are used to trigger synchronization.
|
|
||||||
|
|
||||||
def create_network_postcommit(self, context):
|
|
||||||
self.synchronize('create', 'network', context)
|
|
||||||
|
|
||||||
def update_network_postcommit(self, context):
|
|
||||||
self.synchronize('update', 'network', context)
|
|
||||||
|
|
||||||
def delete_network_postcommit(self, context):
|
|
||||||
self.synchronize('delete', 'network', context)
|
|
||||||
|
|
||||||
def create_subnet_postcommit(self, context):
|
|
||||||
self.synchronize('create', 'subnet', context)
|
|
||||||
|
|
||||||
def update_subnet_postcommit(self, context):
|
|
||||||
self.synchronize('update', 'subnet', context)
|
|
||||||
|
|
||||||
def delete_subnet_postcommit(self, context):
|
|
||||||
self.synchronize('delete', 'subnet', context)
|
|
||||||
|
|
||||||
def create_port_postcommit(self, context):
|
|
||||||
self.synchronize('create', 'port', context)
|
|
||||||
|
|
||||||
def update_port_postcommit(self, context):
|
|
||||||
self.synchronize('update', 'port', context)
|
|
||||||
|
|
||||||
def delete_port_postcommit(self, context):
|
|
||||||
self.synchronize('delete', 'port', context)
|
|
||||||
|
|
||||||
def synchronize(self, operation, object_type, context):
|
|
||||||
"""Synchronize NCS with Neutron following a configuration change."""
|
|
||||||
if self.out_of_sync:
|
|
||||||
self.sync_full(context)
|
|
||||||
else:
|
|
||||||
self.sync_object(operation, object_type, context)
|
|
||||||
|
|
||||||
def sync_full(self, context):
|
|
||||||
"""Resync the entire database to NCS.
|
|
||||||
Transition to the in-sync state on success.
|
|
||||||
"""
|
|
||||||
dbcontext = context._plugin_context
|
|
||||||
networks = context._plugin.get_networks(dbcontext)
|
|
||||||
subnets = context._plugin.get_subnets(dbcontext)
|
|
||||||
ports = context._plugin.get_ports(dbcontext)
|
|
||||||
for port in ports:
|
|
||||||
self.add_security_groups(context, dbcontext, port)
|
|
||||||
json = {'openstack': {'network': networks,
|
|
||||||
'subnet': subnets,
|
|
||||||
'port': ports}}
|
|
||||||
self.sendjson('put', '', json)
|
|
||||||
self.out_of_sync = False
|
|
||||||
|
|
||||||
def sync_object(self, operation, object_type, context):
|
|
||||||
"""Synchronize the single modified record to NCS.
|
|
||||||
Transition to the out-of-sync state on failure.
|
|
||||||
"""
|
|
||||||
self.out_of_sync = True
|
|
||||||
dbcontext = context._plugin_context
|
|
||||||
id = context.current['id']
|
|
||||||
urlpath = object_type + '/' + id
|
|
||||||
if operation == 'delete':
|
|
||||||
self.sendjson('delete', urlpath, None)
|
|
||||||
else:
|
|
||||||
assert operation == 'create' or operation == 'update'
|
|
||||||
if object_type == 'network':
|
|
||||||
network = context._plugin.get_network(dbcontext, id)
|
|
||||||
self.sendjson('put', urlpath, {'network': network})
|
|
||||||
elif object_type == 'subnet':
|
|
||||||
subnet = context._plugin.get_subnet(dbcontext, id)
|
|
||||||
self.sendjson('put', urlpath, {'subnet': subnet})
|
|
||||||
else:
|
|
||||||
assert object_type == 'port'
|
|
||||||
port = context._plugin.get_port(dbcontext, id)
|
|
||||||
self.add_security_groups(context, dbcontext, port)
|
|
||||||
self.sendjson('put', urlpath, {'port': port})
|
|
||||||
self.out_of_sync = False
|
|
||||||
|
|
||||||
def add_security_groups(self, context, dbcontext, port):
|
|
||||||
"""Populate the 'security_groups' field with entire records."""
|
|
||||||
groups = [context._plugin.get_security_group(dbcontext, sg)
|
|
||||||
for sg in port['security_groups']]
|
|
||||||
port['security_groups'] = groups
|
|
||||||
|
|
||||||
def sendjson(self, method, urlpath, obj):
|
|
||||||
obj = self.escape_keys(obj)
|
|
||||||
headers = {'Content-Type': 'application/vnd.yang.data+json'}
|
|
||||||
if obj is None:
|
|
||||||
data = None
|
|
||||||
else:
|
|
||||||
data = jsonutils.dumps(obj, indent=2)
|
|
||||||
auth = None
|
|
||||||
if self.username and self.password:
|
|
||||||
auth = (self.username, self.password)
|
|
||||||
if self.url:
|
|
||||||
url = '/'.join([self.url, urlpath])
|
|
||||||
r = requests.request(method, url=url,
|
|
||||||
headers=headers, data=data,
|
|
||||||
auth=auth, timeout=self.timeout)
|
|
||||||
r.raise_for_status()
|
|
||||||
|
|
||||||
def escape_keys(self, obj):
|
|
||||||
"""Escape JSON keys to be NCS compatible.
|
|
||||||
NCS does not allow period (.) or colon (:) characters.
|
|
||||||
"""
|
|
||||||
if isinstance(obj, dict):
|
|
||||||
obj = dict((self.escape(k), self.escape_keys(v))
|
|
||||||
for k, v in six.iteritems(obj))
|
|
||||||
if isinstance(obj, list):
|
|
||||||
obj = [self.escape_keys(x) for x in obj]
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def escape(self, string):
|
|
||||||
return re.sub('[:._]', '-', string)
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from neutron.plugins.ml2.drivers.cisco.ncs import driver
|
|
||||||
from neutron.tests.unit.plugins.ml2 import test_plugin
|
|
||||||
|
|
||||||
|
|
||||||
class NCSTestCase(test_plugin.Ml2PluginV2TestCase):
|
|
||||||
_mechanism_drivers = ['logger', 'ncs']
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
# Enable the test mechanism driver to ensure that
|
|
||||||
# we can successfully call through to all mechanism
|
|
||||||
# driver apis.
|
|
||||||
super(NCSTestCase, self).setUp()
|
|
||||||
self.port_create_status = 'DOWN'
|
|
||||||
driver.NCSMechanismDriver.sendjson = self.check_sendjson
|
|
||||||
|
|
||||||
def check_sendjson(self, method, urlpath, obj):
|
|
||||||
# Confirm fix for bug #1224981
|
|
||||||
self.assertFalse(urlpath.startswith("http://"))
|
|
||||||
|
|
||||||
|
|
||||||
class NCSMechanismTestBasicGet(test_plugin.TestMl2BasicGet, NCSTestCase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NCSMechanismTestNetworksV2(test_plugin.TestMl2NetworksV2, NCSTestCase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NCSMechanismTestPortsV2(test_plugin.TestMl2PortsV2, NCSTestCase):
|
|
||||||
pass
|
|
@ -68,7 +68,6 @@ data_files =
|
|||||||
etc/neutron/plugins/ml2/ml2_conf_brocade.ini
|
etc/neutron/plugins/ml2/ml2_conf_brocade.ini
|
||||||
etc/neutron/plugins/ml2/ml2_conf_brocade_fi_ni.ini
|
etc/neutron/plugins/ml2/ml2_conf_brocade_fi_ni.ini
|
||||||
etc/neutron/plugins/ml2/ml2_conf_cisco.ini
|
etc/neutron/plugins/ml2/ml2_conf_cisco.ini
|
||||||
etc/neutron/plugins/ml2/ml2_conf_ncs.ini
|
|
||||||
etc/neutron/plugins/ml2/ml2_conf_ofa.ini
|
etc/neutron/plugins/ml2/ml2_conf_ofa.ini
|
||||||
etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini
|
etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini
|
||||||
etc/neutron/plugins/ml2/ml2_conf_sriov.ini
|
etc/neutron/plugins/ml2/ml2_conf_sriov.ini
|
||||||
|
Loading…
Reference in New Issue
Block a user