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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. # Copyright 2014 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 neutron.plugins.ml2.drivers.brocade.db import models as brocade_db
  20. from oslo_config import cfg
  21. from oslo_log import log as logging
  22. from oslo_utils import importutils
  23. LOG = logging.getLogger(__name__)
  24. MECHANISM_VERSION = 0.9
  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. def set_features_enabled(self, nos_version, virtual_fabric_enabled):
  81. self._virtual_fabric_enabled = virtual_fabric_enabled
  82. version = nos_version.split(".", 2)
  83. # Starting 4.1.0 port profile domains are supported
  84. if int(version[0]) >= 5 or (int(version[0]) >= 4
  85. and int(version[1]) >= 1):
  86. self._pp_domains_supported = True
  87. else:
  88. self._pp_domains_supported = False
  89. self._driver.set_features_enabled(self._pp_domains_supported,
  90. self._virtual_fabric_enabled)
  91. def get_features_enabled(self):
  92. return self._pp_domains_supported, self._virtual_fabric_enabled
  93. def create_network_precommit(self, mech_context):
  94. """Create Network in the mechanism specific database table."""
  95. network = mech_context.current
  96. context = mech_context._plugin_context
  97. tenant_id = network['tenant_id']
  98. network_id = network['id']
  99. segments = mech_context.network_segments
  100. # currently supports only one segment per network
  101. segment = segments[0]
  102. network_type = segment['network_type']
  103. vlan_id = segment['segmentation_id']
  104. segment_id = segment['id']
  105. if segment['physical_network'] not in self._physical_networks:
  106. raise Exception(
  107. _("Brocade Mechanism: failed to create network, "
  108. "network cannot be created in the configured "
  109. "physical network"))
  110. if network_type != 'vlan':
  111. raise Exception(
  112. _("Brocade Mechanism: failed to create network, "
  113. "only network type vlan is supported"))
  114. try:
  115. brocade_db.create_network(context, network_id, vlan_id,
  116. segment_id, network_type, tenant_id)
  117. except Exception:
  118. LOG.exception(
  119. _LE("Brocade Mechanism: failed to create network in db"))
  120. raise Exception(
  121. _("Brocade Mechanism: create_network_precommit failed"))
  122. LOG.info(_LI("create network (precommit): %(network_id)s "
  123. "of network type = %(network_type)s "
  124. "with vlan = %(vlan_id)s "
  125. "for tenant %(tenant_id)s"),
  126. {'network_id': network_id,
  127. 'network_type': network_type,
  128. 'vlan_id': vlan_id,
  129. 'tenant_id': tenant_id})
  130. def create_network_postcommit(self, mech_context):
  131. """Create Network as a portprofile on the switch."""
  132. LOG.debug("create_network_postcommit: called")
  133. network = mech_context.current
  134. # use network_id to get the network attributes
  135. # ONLY depend on our db for getting back network attributes
  136. # this is so we can replay postcommit from db
  137. context = mech_context._plugin_context
  138. network_id = network['id']
  139. network = brocade_db.get_network(context, network_id)
  140. network_type = network['network_type']
  141. tenant_id = network['tenant_id']
  142. vlan_id = network['vlan']
  143. try:
  144. self._driver.create_network(self._switch['address'],
  145. self._switch['username'],
  146. self._switch['password'],
  147. vlan_id)
  148. except Exception:
  149. LOG.exception(_LE("Brocade NOS driver: failed in create network"))
  150. brocade_db.delete_network(context, network_id)
  151. raise Exception(
  152. _("Brocade Mechanism: create_network_postcommmit failed"))
  153. LOG.info(_LI("created network (postcommit): %(network_id)s"
  154. " of network type = %(network_type)s"
  155. " with vlan = %(vlan_id)s"
  156. " for tenant %(tenant_id)s"),
  157. {'network_id': network_id,
  158. 'network_type': network_type,
  159. 'vlan_id': vlan_id,
  160. 'tenant_id': tenant_id})
  161. def delete_network_precommit(self, mech_context):
  162. """Delete Network from the plugin specific database table."""
  163. LOG.debug("delete_network_precommit: called")
  164. network = mech_context.current
  165. network_id = network['id']
  166. vlan_id = network['provider:segmentation_id']
  167. tenant_id = network['tenant_id']
  168. context = mech_context._plugin_context
  169. try:
  170. brocade_db.delete_network(context, network_id)
  171. except Exception:
  172. LOG.exception(
  173. _LE("Brocade Mechanism: failed to delete network in db"))
  174. raise Exception(
  175. _("Brocade Mechanism: delete_network_precommit failed"))
  176. LOG.info(_LI("delete network (precommit): %(network_id)s"
  177. " with vlan = %(vlan_id)s"
  178. " for tenant %(tenant_id)s"),
  179. {'network_id': network_id,
  180. 'vlan_id': vlan_id,
  181. 'tenant_id': tenant_id})
  182. def delete_network_postcommit(self, mech_context):
  183. """Delete network.
  184. This translates to removng portprofile
  185. from the switch.
  186. """
  187. LOG.debug("delete_network_postcommit: called")
  188. network = mech_context.current
  189. network_id = network['id']
  190. vlan_id = network['provider:segmentation_id']
  191. tenant_id = network['tenant_id']
  192. try:
  193. self._driver.delete_network(self._switch['address'],
  194. self._switch['username'],
  195. self._switch['password'],
  196. vlan_id)
  197. except Exception:
  198. LOG.exception(_LE("Brocade NOS driver: failed to delete network"))
  199. raise Exception(
  200. _("Brocade switch exception, "
  201. "delete_network_postcommit failed"))
  202. LOG.info(_LI("delete network (postcommit): %(network_id)s"
  203. " with vlan = %(vlan_id)s"
  204. " for tenant %(tenant_id)s"),
  205. {'network_id': network_id,
  206. 'vlan_id': vlan_id,
  207. 'tenant_id': tenant_id})
  208. def update_network_precommit(self, mech_context):
  209. """Noop now, it is left here for future."""
  210. pass
  211. def update_network_postcommit(self, mech_context):
  212. """Noop now, it is left here for future."""
  213. pass
  214. def create_port_precommit(self, mech_context):
  215. """Create logical port on the switch (db update)."""
  216. LOG.debug("create_port_precommit: called")
  217. port = mech_context.current
  218. port_id = port['id']
  219. network_id = port['network_id']
  220. tenant_id = port['tenant_id']
  221. admin_state_up = port['admin_state_up']
  222. context = mech_context._plugin_context
  223. network = brocade_db.get_network(context, network_id)
  224. vlan_id = network['vlan']
  225. try:
  226. brocade_db.create_port(context, port_id, network_id,
  227. None,
  228. vlan_id, tenant_id, admin_state_up)
  229. except Exception:
  230. LOG.exception(_LE("Brocade Mechanism: failed to create port"
  231. " in db"))
  232. raise Exception(
  233. _("Brocade Mechanism: create_port_precommit failed"))
  234. def create_port_postcommit(self, mech_context):
  235. """Associate the assigned MAC address to the portprofile."""
  236. LOG.debug("create_port_postcommit: called")
  237. port = mech_context.current
  238. port_id = port['id']
  239. network_id = port['network_id']
  240. tenant_id = port['tenant_id']
  241. context = mech_context._plugin_context
  242. network = brocade_db.get_network(context, network_id)
  243. vlan_id = network['vlan']
  244. interface_mac = port['mac_address']
  245. # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx
  246. mac = self.mac_reformat_62to34(interface_mac)
  247. try:
  248. self._driver.associate_mac_to_network(self._switch['address'],
  249. self._switch['username'],
  250. self._switch['password'],
  251. vlan_id,
  252. mac)
  253. except Exception:
  254. LOG.exception(
  255. _LE("Brocade NOS driver: failed to associate mac %s"),
  256. interface_mac)
  257. raise Exception(
  258. _("Brocade switch exception: create_port_postcommit failed"))
  259. LOG.info(
  260. _LI("created port (postcommit): port_id=%(port_id)s"
  261. " network_id=%(network_id)s tenant_id=%(tenant_id)s"),
  262. {'port_id': port_id,
  263. 'network_id': network_id, 'tenant_id': tenant_id})
  264. def delete_port_precommit(self, mech_context):
  265. """Delete logical port on the switch (db update)."""
  266. LOG.debug("delete_port_precommit: called")
  267. port = mech_context.current
  268. port_id = port['id']
  269. context = mech_context._plugin_context
  270. try:
  271. brocade_db.delete_port(context, port_id)
  272. except Exception:
  273. LOG.exception(_LE("Brocade Mechanism: failed to delete port"
  274. " in db"))
  275. raise Exception(
  276. _("Brocade Mechanism: delete_port_precommit failed"))
  277. def delete_port_postcommit(self, mech_context):
  278. """Dissociate MAC address from the portprofile."""
  279. LOG.debug("delete_port_postcommit: called")
  280. port = mech_context.current
  281. port_id = port['id']
  282. network_id = port['network_id']
  283. tenant_id = port['tenant_id']
  284. context = mech_context._plugin_context
  285. network = brocade_db.get_network(context, network_id)
  286. vlan_id = network['vlan']
  287. interface_mac = port['mac_address']
  288. # convert mac format: xx:xx:xx:xx:xx:xx -> xxxx.xxxx.xxxx
  289. mac = self.mac_reformat_62to34(interface_mac)
  290. try:
  291. self._driver.dissociate_mac_from_network(
  292. self._switch['address'],
  293. self._switch['username'],
  294. self._switch['password'],
  295. vlan_id,
  296. mac)
  297. except Exception:
  298. LOG.exception(
  299. _LE("Brocade NOS driver: failed to dissociate MAC %s"),
  300. interface_mac)
  301. raise Exception(
  302. _("Brocade switch exception, delete_port_postcommit failed"))
  303. LOG.info(
  304. _LI("delete port (postcommit): port_id=%(port_id)s"
  305. " network_id=%(network_id)s tenant_id=%(tenant_id)s"),
  306. {'port_id': port_id,
  307. 'network_id': network_id, 'tenant_id': tenant_id})
  308. def update_port_precommit(self, mech_context):
  309. """Noop now, it is left here for future."""
  310. LOG.debug("update_port_precommit(self: called")
  311. def update_port_postcommit(self, mech_context):
  312. """Noop now, it is left here for future."""
  313. LOG.debug("update_port_postcommit: called")
  314. def create_subnet_precommit(self, mech_context):
  315. """Noop now, it is left here for future."""
  316. LOG.debug("create_subnetwork_precommit: called")
  317. def create_subnet_postcommit(self, mech_context):
  318. """Noop now, it is left here for future."""
  319. LOG.debug("create_subnetwork_postcommit: called")
  320. def delete_subnet_precommit(self, mech_context):
  321. """Noop now, it is left here for future."""
  322. LOG.debug("delete_subnetwork_precommit: called")
  323. def delete_subnet_postcommit(self, mech_context):
  324. """Noop now, it is left here for future."""
  325. LOG.debug("delete_subnetwork_postcommit: called")
  326. def update_subnet_precommit(self, mech_context):
  327. """Noop now, it is left here for future."""
  328. LOG.debug("update_subnet_precommit(self: called")
  329. def update_subnet_postcommit(self, mech_context):
  330. """Noop now, it is left here for future."""
  331. LOG.debug("update_subnet_postcommit: called")
  332. @staticmethod
  333. def mac_reformat_62to34(interface_mac):
  334. """Transform MAC address format.
  335. Transforms from 6 groups of 2 hexadecimal numbers delimited by ":"
  336. to 3 groups of 4 hexadecimals numbers delimited by ".".
  337. :param interface_mac: MAC address in the format xx:xx:xx:xx:xx:xx
  338. :type interface_mac: string
  339. :returns: MAC address in the format xxxx.xxxx.xxxx
  340. :rtype: string
  341. """
  342. mac = interface_mac.replace(":", "")
  343. mac = mac[0:4] + "." + mac[4:8] + "." + mac[8:12]
  344. return mac