Shiv Haris f807023246 Added support for NOS version 4.1.0, 5.0.0 and greater
NETCONF temaplates for NOS version greater than 4.1.0 are slightly
different (argh). An init time check of the NOS version is done
to enable selection of the correct NETCONF templates.

Change-Id: I01e82ad402fbbb25d92a99a3325ca2608dd514cb
Closes-bug: #1332719
2014-07-09 13:25:01 -07:00

318 lines
12 KiB
Python

# Copyright 2014 Brocade Communications System, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Authors:
# Varma Bhupatiraju (vbhupati@brocade.com)
# Shiv Haris (shivharis@hotmail.com)
"""Brocade NOS Driver implements NETCONF over SSHv2 for
Neutron network life-cycle management.
"""
from ncclient import manager
from xml.etree import ElementTree
from neutron.openstack.common import excutils
from neutron.openstack.common import log as logging
from neutron.plugins.ml2.drivers.brocade.nos import nctemplates as template
LOG = logging.getLogger(__name__)
SSH_PORT = 22
def nos_unknown_host_cb(host, fingerprint):
"""An unknown host callback.
Returns `True` if it finds the key acceptable,
and `False` if not. This default callback for NOS always returns 'True'
(i.e. trusts all hosts for now).
"""
return True
class NOSdriver():
"""NOS NETCONF interface driver for Neutron network.
Handles life-cycle management of Neutron network (leverages AMPP on NOS)
"""
def __init__(self):
self.mgr = None
self._virtual_fabric_enabled = False
self._pp_domains_supported = False
def set_features_enabled(self, pp_domains_supported,
virtual_fabric_enabled):
"""Set features in the driver based on what was detected by the MD."""
self._pp_domains_supported = pp_domains_supported
self._virtual_fabric_enabled = virtual_fabric_enabled
def get_features_enabled(self):
"""Respond to status of features enabled."""
return self._pp_domains_supported, self._virtual_fabric_enabled
def connect(self, host, username, password):
"""Connect via SSH and initialize the NETCONF session."""
# Use the persisted NETCONF connection
if self.mgr and self.mgr.connected:
return self.mgr
# check if someone forgot to edit the conf file with real values
if host == '':
raise Exception(_("Brocade Switch IP address is not set, "
"check config ml2_conf_brocade.ini file"))
# Open new NETCONF connection
try:
self.mgr = manager.connect(host=host, port=SSH_PORT,
username=username, password=password,
unknown_host_cb=nos_unknown_host_cb)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("Connect failed to switch"))
LOG.debug(_("Connect success to host %(host)s:%(ssh_port)d"),
dict(host=host, ssh_port=SSH_PORT))
return self.mgr
def close_session(self):
"""Close NETCONF session."""
if self.mgr:
self.mgr.close_session()
self.mgr = None
def get_nos_version(self, host, username, password):
"""Show version of NOS."""
try:
mgr = self.connect(host, username, password)
return self.nos_version_request(mgr)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("NETCONF error"))
self.close_session()
def is_virtual_fabric_enabled(self, host, username, password):
"""Show version of NOS."""
try:
mgr = self.connect(host, username, password)
return (self.virtual_fabric_info(mgr) == "enabled")
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("NETCONF error"))
self.close_session()
def create_network(self, host, username, password, net_id):
"""Creates a new virtual network."""
domain_name = "default"
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)
if self._pp_domains_supported and self._virtual_fabric_enabled:
self.configure_port_profile_in_domain(mgr, domain_name, name)
self.create_vlan_profile_for_port_profile(mgr, name)
if self._pp_domains_supported:
self.configure_l2_mode_for_vlan_profile_with_domains(mgr, name)
else:
self.configure_l2_mode_for_vlan_profile(mgr, name)
self.configure_trunk_mode_for_vlan_profile(mgr, name)
self.configure_allowed_vlans_for_vlan_profile(mgr, name, net_id)
self.activate_port_profile(mgr, name)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("NETCONF error"))
self.close_session()
def delete_network(self, host, username, password, net_id):
"""Deletes a virtual network."""
domain_name = "default"
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
if self._pp_domains_supported and self._virtual_fabric_enabled:
self.remove_port_profile_from_domain(mgr, domain_name, name)
self.deactivate_port_profile(mgr, name)
self.delete_port_profile(mgr, name)
self.delete_vlan_interface(mgr, net_id)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("NETCONF error"))
self.close_session()
def associate_mac_to_network(self, host, username, password,
net_id, mac):
"""Associates a MAC address to virtual network."""
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
self.associate_mac_to_port_profile(mgr, name, mac)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("NETCONF error"))
self.close_session()
def dissociate_mac_from_network(self, host, username, password,
net_id, mac):
"""Dissociates a MAC address from virtual network."""
name = template.OS_PORT_PROFILE_NAME.format(id=net_id)
try:
mgr = self.connect(host, username, password)
self.dissociate_mac_from_port_profile(mgr, name, mac)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_("NETCONF error"))
self.close_session()
def create_vlan_interface(self, mgr, vlan_id):
"""Configures a VLAN interface."""
confstr = template.CREATE_VLAN_INTERFACE.format(vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)
def delete_vlan_interface(self, mgr, vlan_id):
"""Deletes a VLAN interface."""
confstr = template.DELETE_VLAN_INTERFACE.format(vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)
def get_port_profiles(self, mgr):
"""Retrieves all port profiles."""
filterstr = template.PORT_PROFILE_XPATH_FILTER
response = mgr.get_config(source='running',
filter=('xpath', filterstr)).data_xml
return response
def get_port_profile(self, mgr, name):
"""Retrieves a port profile."""
filterstr = template.PORT_PROFILE_NAME_XPATH_FILTER.format(name=name)
response = mgr.get_config(source='running',
filter=('xpath', filterstr)).data_xml
return response
def create_port_profile(self, mgr, name):
"""Creates a port profile."""
confstr = template.CREATE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)
def delete_port_profile(self, mgr, name):
"""Deletes a port profile."""
confstr = template.DELETE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)
def activate_port_profile(self, mgr, name):
"""Activates a port profile."""
confstr = template.ACTIVATE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)
def deactivate_port_profile(self, mgr, name):
"""Deactivates a port profile."""
confstr = template.DEACTIVATE_PORT_PROFILE.format(name=name)
mgr.edit_config(target='running', config=confstr)
def associate_mac_to_port_profile(self, mgr, name, mac_address):
"""Associates a MAC address to a port profile."""
confstr = template.ASSOCIATE_MAC_TO_PORT_PROFILE.format(
name=name, mac_address=mac_address)
mgr.edit_config(target='running', config=confstr)
def dissociate_mac_from_port_profile(self, mgr, name, mac_address):
"""Dissociates a MAC address from a port profile."""
confstr = template.DISSOCIATE_MAC_FROM_PORT_PROFILE.format(
name=name, mac_address=mac_address)
mgr.edit_config(target='running', config=confstr)
def create_vlan_profile_for_port_profile(self, mgr, name):
"""Creates VLAN sub-profile for port profile."""
confstr = template.CREATE_VLAN_PROFILE_FOR_PORT_PROFILE.format(
name=name)
mgr.edit_config(target='running', config=confstr)
def configure_l2_mode_for_vlan_profile(self, mgr, name):
"""Configures L2 mode for VLAN sub-profile."""
confstr = template.CONFIGURE_L2_MODE_FOR_VLAN_PROFILE.format(
name=name)
mgr.edit_config(target='running', config=confstr)
def configure_trunk_mode_for_vlan_profile(self, mgr, name):
"""Configures trunk mode for VLAN sub-profile."""
confstr = template.CONFIGURE_TRUNK_MODE_FOR_VLAN_PROFILE.format(
name=name)
mgr.edit_config(target='running', config=confstr)
def configure_allowed_vlans_for_vlan_profile(self, mgr, name, vlan_id):
"""Configures allowed VLANs for VLAN sub-profile."""
confstr = template.CONFIGURE_ALLOWED_VLANS_FOR_VLAN_PROFILE.format(
name=name, vlan_id=vlan_id)
mgr.edit_config(target='running', config=confstr)
def remove_port_profile_from_domain(self, mgr, domain_name, name):
"""Remove port-profile from default domain."""
confstr = template.REMOVE_PORTPROFILE_FROM_DOMAIN.format(
domain_name=domain_name, name=name)
mgr.edit_config(target='running', config=confstr)
def configure_port_profile_in_domain(self, mgr, domain_name, name):
"""put port-profile in default domain."""
confstr = template.CONFIGURE_PORTPROFILE_IN_DOMAIN.format(
domain_name=domain_name, name=name)
mgr.edit_config(target='running', config=confstr)
def configure_l2_mode_for_vlan_profile_with_domains(self, mgr, name):
"""Configures L2 mode for VLAN sub-profile."""
confstr = template.CONFIGURE_L2_MODE_FOR_VLAN_PROFILE_IN_DOMAIN.format(
name=name)
mgr.edit_config(target='running', config=confstr)
def nos_version_request(self, mgr):
"""Get firmware information using NETCONF rpc."""
reply = mgr.dispatch(template.SHOW_FIRMWARE_VERSION, None, None)
et = ElementTree.fromstring(str(reply))
return et.find(template.NOS_VERSION).text
def virtual_fabric_info(self, mgr):
"""Get virtual fabric info using NETCONF get-config."""
response = mgr.get_config('running',
filter=("xpath", "/vcs/virtual-fabric"))
et = ElementTree.fromstring(str(response))
vfab_enable = et.find(template.VFAB_ENABLE)
if vfab_enable is not None:
return "enabled"
return "disabled"