Brocade plugins/drivers for ML2 and Service Plugins
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mechanism_brocade.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. # Copyright 2016 Brocade Communications System, Inc.
  2. # All rights reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. """Implentation of Brocade ML2 Mechanism driver for ML2 Plugin."""
  16. from neutron.i18n import _LE
  17. from neutron.i18n import _LI
  18. from neutron.plugins.ml2 import driver_api
  19. from oslo_config import cfg
  20. from oslo_log import log as logging
  21. from oslo_utils import importutils
  22. from networking_brocade.vdx.ml2driver.nos.db import models as brocade_db
  23. LOG = logging.getLogger(__name__)
  24. MECHANISM_VERSION = 1.0
  25. NOS_DRIVER = 'networking_brocade.vdx.ml2driver.nos.nosdriver.NOSdriver'
  26. ML2_BROCADE = [cfg.StrOpt('address', default='',
  27. help=_('The address of the host to SSH to')),
  28. cfg.StrOpt('username', default='admin',
  29. help=_('The SSH username to use')),
  30. cfg.StrOpt('password', default='password', secret=True,
  31. help=_('The SSH password to use')),
  32. cfg.StrOpt('physical_networks', default='',
  33. help=_('Allowed physical networks')),
  34. cfg.StrOpt('ostype', default='NOS',
  35. help=_('OS Type of the switch')),
  36. cfg.StrOpt('osversion', default='4.0.0',
  37. help=_('OS Version number'))
  38. ]
  39. cfg.CONF.register_opts(ML2_BROCADE, "ml2_brocade")
  40. class BrocadeMechanism(driver_api.MechanismDriver):
  41. """ML2 Mechanism driver for Brocade VDX switches.
  42. This is the upper layer driver class that interfaces to
  43. lower layer (NETCONF) below.
  44. """
  45. def __init__(self):
  46. self._driver = None
  47. self._physical_networks = None
  48. self._switch = None
  49. self.initialize()
  50. def initialize(self):
  51. """Initilize of variables needed by this class."""
  52. self._physical_networks = cfg.CONF.ml2_brocade.physical_networks
  53. self.brocade_init()
  54. def brocade_init(self):
  55. """Brocade specific initialization for this class."""
  56. osversion = None
  57. self._switch = {
  58. 'address': cfg.CONF.ml2_brocade.address,
  59. 'username': cfg.CONF.ml2_brocade.username,
  60. 'password': cfg.CONF.ml2_brocade.password,
  61. 'ostype': cfg.CONF.ml2_brocade.ostype,
  62. 'osversion': cfg.CONF.ml2_brocade.osversion}
  63. self._driver = importutils.import_object(NOS_DRIVER)
  64. # Detect version of NOS on the switch
  65. osversion = self._switch['osversion']
  66. if osversion == "autodetect":
  67. osversion = self._driver.get_nos_version(
  68. self._switch['address'],
  69. self._switch['username'],
  70. self._switch['password'])
  71. virtual_fabric_enabled = self._driver.is_virtual_fabric_enabled(
  72. self._switch['address'],
  73. self._switch['username'],
  74. self._switch['password'])
  75. if virtual_fabric_enabled:
  76. LOG.debug("Virtual Fabric: enabled")
  77. else:
  78. LOG.debug("Virtual Fabric: not enabled")
  79. self.set_features_enabled(osversion, virtual_fabric_enabled)
  80. self._driver.close_session()
  81. def set_features_enabled(self, nos_version, virtual_fabric_enabled):
  82. self._virtual_fabric_enabled = virtual_fabric_enabled
  83. version = nos_version.split(".", 2)
  84. # Starting 4.1.0 port profile domains are supported
  85. if int(version[0]) >= 5 or (int(version[0]) >= 4
  86. and int(version[1]) >= 1):
  87. self._pp_domains_supported = True
  88. else:
  89. self._pp_domains_supported = False
  90. self._driver.set_features_enabled(self._pp_domains_supported,
  91. self._virtual_fabric_enabled)
  92. def get_features_enabled(self):
  93. return self._pp_domains_supported, self._virtual_fabric_enabled
  94. def create_network_precommit(self, mech_context):
  95. """Create Network in the mechanism specific database table."""
  96. network = mech_context.current
  97. context = mech_context._plugin_context
  98. tenant_id = network['tenant_id']
  99. network_id = network['id']
  100. segments = mech_context.network_segments
  101. # currently supports only one segment per network
  102. segment = segments[0]
  103. network_type = segment['network_type']
  104. vlan_id = segment['segmentation_id']
  105. segment_id = segment['id']
  106. if segment['physical_network'] not in self._physical_networks:
  107. raise Exception(
  108. _("Brocade Mechanism: failed to create network, "
  109. "network cannot be created in the configured "
  110. "physical network"))
  111. if network_type != 'vlan':
  112. raise Exception(
  113. _("Brocade Mechanism: failed to create network, "
  114. "only network type vlan is supported"))
  115. try:
  116. brocade_db.create_network(context, network_id, vlan_id,
  117. segment_id, network_type, tenant_id)
  118. except Exception:
  119. LOG.exception(
  120. _LE("Brocade Mechanism: failed to create network in db"))
  121. raise Exception(
  122. _("Brocade Mechanism: create_network_precommit failed"))
  123. LOG.info(_LI("create network (precommit): %(network_id)s "
  124. "of network type = %(network_type)s "
  125. "with vlan = %(vlan_id)s "
  126. "for tenant %(tenant_id)s"),
  127. {'network_id': network_id,
  128. 'network_type': network_type,
  129. 'vlan_id': vlan_id,
  130. 'tenant_id': tenant_id})
  131. def create_network_postcommit(self, mech_context):
  132. """Create Network as a portprofile on the switch."""
  133. LOG.debug("create_network_postcommit: called")
  134. network = mech_context.current
  135. # use network_id to get the network attributes
  136. # ONLY depend on our db for getting back network attributes
  137. # this is so we can replay postcommit from db
  138. context = mech_context._plugin_context
  139. network_id = network['id']
  140. network = brocade_db.get_network(context, network_id)
  141. network_type = network['network_type']
  142. tenant_id = network['tenant_id']
  143. vlan_id = network['vlan']
  144. try:
  145. self._driver.create_network(self._switch['address'],
  146. self._switch['username'],
  147. self._switch['password'],
  148. vlan_id)
  149. except Exception:
  150. LOG.exception(_LE("Brocade NOS driver: failed in create network"))
  151. brocade_db.delete_network(context, network_id)
  152. raise Exception(
  153. _("Brocade Mechanism: create_network_postcommmit failed"))
  154. LOG.info(_LI("created network (postcommit): %(network_id)s"
  155. " of network type = %(network_type)s"
  156. " with vlan = %(vlan_id)s"
  157. " for tenant %(tenant_id)s"),
  158. {'network_id': network_id,
  159. 'network_type': network_type,
  160. 'vlan_id': vlan_id,
  161. 'tenant_id': tenant_id})
  162. def delete_network_precommit(self, mech_context):
  163. """Delete Network from the plugin specific database table."""
  164. LOG.debug("delete_network_precommit: called")
  165. network = mech_context.current
  166. network_id = network['id']
  167. vlan_id = network['provider:segmentation_id']
  168. tenant_id = network['tenant_id']
  169. context = mech_context._plugin_context
  170. try:
  171. brocade_db.delete_network(context, network_id)
  172. except Exception:
  173. LOG.exception(
  174. _LE("Brocade Mechanism: failed to delete network in db"))
  175. raise Exception(
  176. _("Brocade Mechanism: delete_network_precommit failed"))
  177. LOG.info(_LI("delete network (precommit): %(network_id)s"
  178. " with vlan = %(vlan_id)s"
  179. " for tenant %(tenant_id)s"),
  180. {'network_id': network_id,
  181. 'vlan_id': vlan_id,
  182. 'tenant_id': tenant_id})
  183. def delete_network_postcommit(self, mech_context):
  184. """Delete network.
  185. This translates to removng portprofile
  186. from the switch.
  187. """
  188. LOG.debug("delete_network_postcommit: called")
  189. network = mech_context.current
  190. network_id = network['id']
  191. vlan_id = network['provider:segmentation_id']
  192. tenant_id = network['tenant_id']
  193. try:
  194. self._driver.delete_network(self._switch['address'],
  195. self._switch['username'],
  196. self._switch['password'],
  197. vlan_id)
  198. except Exception:
  199. LOG.exception(_LE("Brocade NOS driver: failed to delete network"))
  200. raise Exception(
  201. _("Brocade switch exception, "
  202. "delete_network_postcommit failed"))
  203. LOG.info(_LI("delete network (postcommit): %(network_id)s"
  204. " with vlan = %(vlan_id)s"
  205. " for tenant %(tenant_id)s"),
  206. {'network_id': network_id,
  207. 'vlan_id': vlan_id,
  208. 'tenant_id': tenant_id})
  209. def update_network_precommit(self, mech_context):
  210. """Noop now, it is left here for future."""
  211. pass
  212. def update_network_postcommit(self, mech_context):
  213. """Noop now, it is left here for future."""
  214. pass
  215. def create_port_precommit(self, mech_context):
  216. """Create logical port on the switch (db update)."""
  217. LOG.debug("create_port_precommit: called")
  218. port = mech_context.current
  219. port_id = port['id']
  220. network_id = port['network_id']
  221. tenant_id = port['tenant_id']
  222. admin_state_up = port['admin_state_up']
  223. context = mech_context._plugin_context
  224. network = brocade_db.get_network(context, network_id)
  225. vlan_id = network['vlan']
  226. try:
  227. brocade_db.create_port(context, port_id, network_id,
  228. None,
  229. vlan_id, tenant_id, admin_state_up)
  230. except Exception:
  231. LOG.exception(_LE("Brocade Mechanism: failed to create port"
  232. " in db"))
  233. raise Exception(
  234. _("Brocade Mechanism: create_port_precommit failed"))
  235. def create_port_postcommit(self, mech_context):
  236. """Associate the assigned MAC address to the portprofile."""
  237. LOG.debug("create_port_postcommit: called")
  238. port = mech_context.current
  239. port_id = port['id']
  240. network_id = port['network_id']
  241. tenant_id = port['tenant_id']
  242. context = mech_context._plugin_context
  243. network = brocade_db.get_network(context, network_id)
  244. vlan_id = network['vlan']
  245. interface_mac = port['mac_address']
  246. # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx
  247. mac = self.mac_reformat_62to34(interface_mac)
  248. try:
  249. self._driver.associate_mac_to_network(self._switch['address'],
  250. self._switch['username'],
  251. self._switch['password'],
  252. vlan_id,
  253. mac)
  254. except Exception:
  255. LOG.exception(
  256. _LE("Brocade NOS driver: failed to associate mac %s"),
  257. interface_mac)
  258. raise Exception(
  259. _("Brocade switch exception: create_port_postcommit failed"))
  260. LOG.info(
  261. _LI("created port (postcommit): port_id=%(port_id)s"
  262. " network_id=%(network_id)s tenant_id=%(tenant_id)s"),
  263. {'port_id': port_id,
  264. 'network_id': network_id, 'tenant_id': tenant_id})
  265. def delete_port_precommit(self, mech_context):
  266. """Delete logical port on the switch (db update)."""
  267. LOG.debug("delete_port_precommit: called")
  268. port = mech_context.current
  269. port_id = port['id']
  270. context = mech_context._plugin_context
  271. try:
  272. brocade_db.delete_port(context, port_id)
  273. except Exception:
  274. LOG.exception(_LE("Brocade Mechanism: failed to delete port"
  275. " in db"))
  276. raise Exception(
  277. _("Brocade Mechanism: delete_port_precommit failed"))
  278. def delete_port_postcommit(self, mech_context):
  279. """Dissociate MAC address from the portprofile."""
  280. LOG.debug("delete_port_postcommit: called")
  281. port = mech_context.current
  282. port_id = port['id']
  283. network_id = port['network_id']
  284. tenant_id = port['tenant_id']
  285. context = mech_context._plugin_context
  286. network = brocade_db.get_network(context, network_id)
  287. vlan_id = network['vlan']
  288. interface_mac = port['mac_address']
  289. # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx
  290. mac = self.mac_reformat_62to34(interface_mac)
  291. try:
  292. self._driver.dissociate_mac_from_network(
  293. self._switch['address'],
  294. self._switch['username'],
  295. self._switch['password'],
  296. vlan_id,
  297. mac)
  298. except Exception:
  299. LOG.exception(
  300. _LE("Brocade NOS driver: failed to dissociate MAC %s"),
  301. interface_mac)
  302. raise Exception(
  303. _("Brocade switch exception, delete_port_postcommit failed"))
  304. LOG.info(
  305. _LI("delete port (postcommit): port_id=%(port_id)s"
  306. " network_id=%(network_id)s tenant_id=%(tenant_id)s"),
  307. {'port_id': port_id,
  308. 'network_id': network_id, 'tenant_id': tenant_id})
  309. def update_port_precommit(self, mech_context):
  310. """Noop now, it is left here for future."""
  311. LOG.debug("update_port_precommit(self: called")
  312. def update_port_postcommit(self, mech_context):
  313. """Noop now, it is left here for future."""
  314. LOG.debug("update_port_postcommit: called")
  315. def create_subnet_precommit(self, mech_context):
  316. """Noop now, it is left here for future."""
  317. LOG.debug("create_subnetwork_precommit: called")
  318. def create_subnet_postcommit(self, mech_context):
  319. """Noop now, it is left here for future."""
  320. LOG.debug("create_subnetwork_postcommit: called")
  321. def delete_subnet_precommit(self, mech_context):
  322. """Noop now, it is left here for future."""
  323. LOG.debug("delete_subnetwork_precommit: called")
  324. def delete_subnet_postcommit(self, mech_context):
  325. """Noop now, it is left here for future."""
  326. LOG.debug("delete_subnetwork_postcommit: called")
  327. def update_subnet_precommit(self, mech_context):
  328. """Noop now, it is left here for future."""
  329. LOG.debug("update_subnet_precommit(self: called")
  330. def update_subnet_postcommit(self, mech_context):
  331. """Noop now, it is left here for future."""
  332. LOG.debug("update_subnet_postcommit: called")
  333. @staticmethod
  334. def mac_reformat_62to34(interface_mac):
  335. """Transform MAC address format.
  336. Transforms from 6 groups of 2 hexadecimal numbers delimited by ":"
  337. to 3 groups of 4 hexadecimals numbers delimited by ".".
  338. :param interface_mac: MAC address in the format xx:xx:xx:xx:xx:xx
  339. :type interface_mac: string
  340. :returns: MAC address in the format xxxx.xxxx.xxxx
  341. :rtype: string
  342. """
  343. mac = interface_mac.replace(":", "")
  344. mac = mac[0:4] + "." + mac[4:8] + "." + mac[8:12]
  345. return mac