REST connector for Brocade zone driver
Add REST connector for switches with firmware FOS 8.2.1 and greater. Change-Id: I4a205c0e1040d743d94c900318fd3b6edfe667ef
This commit is contained in:
parent
d7240f1cab
commit
cad1dcb5f2
@ -1085,6 +1085,10 @@ class BrocadeZoningHttpException(CinderException):
|
|||||||
message = _("Brocade Fibre Channel Zoning HTTP error: %(reason)s")
|
message = _("Brocade Fibre Channel Zoning HTTP error: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class BrocadeZoningRestException(CinderException):
|
||||||
|
message = _("Brocade Fibre Channel Zoning REST error: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class CiscoZoningCliException(CinderException):
|
class CiscoZoningCliException(CinderException):
|
||||||
message = _("Cisco Fibre Channel Zoning CLI error: %(reason)s")
|
message = _("Cisco Fibre Channel Zoning CLI error: %(reason)s")
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# (c) Copyright 2014 Brocade Communications Systems Inc.
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -20,8 +20,8 @@ from cinder.volume import configuration
|
|||||||
|
|
||||||
brcd_zone_opts = [
|
brcd_zone_opts = [
|
||||||
cfg.StrOpt('fc_southbound_protocol',
|
cfg.StrOpt('fc_southbound_protocol',
|
||||||
default='HTTP',
|
default='REST_HTTP',
|
||||||
choices=('SSH', 'HTTP', 'HTTPS'),
|
choices=('SSH', 'HTTP', 'HTTPS', 'REST_HTTP', 'REST_HTTPS'),
|
||||||
help='South bound connector for the fabric.'),
|
help='South bound connector for the fabric.'),
|
||||||
cfg.StrOpt('fc_fabric_address',
|
cfg.StrOpt('fc_fabric_address',
|
||||||
default='',
|
default='',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# (c) Copyright 2016 Brocade Communications Systems Inc.
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -121,7 +121,7 @@ class BrcdFCSanLookupService(fc_service.FCSanLookupService):
|
|||||||
LOG.error("Failed collecting name server info from"
|
LOG.error("Failed collecting name server info from"
|
||||||
" fabric %s", fabric_ip)
|
" fabric %s", fabric_ip)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = _("SSH connection failed "
|
msg = _("Connection failed "
|
||||||
"for %(fabric)s with error: %(err)s"
|
"for %(fabric)s with error: %(err)s"
|
||||||
) % {'fabric': fabric_ip, 'err': e}
|
) % {'fabric': fabric_ip, 'err': e}
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# (c) Copyright 2016 Brocade Communications Systems Inc.
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# (c) Copyright 2015 Brocade Communications Systems Inc.
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -59,8 +59,20 @@ class BrcdFCZoneFactory(object):
|
|||||||
'port': fabric_port,
|
'port': fabric_port,
|
||||||
'vf_id': fc_vfid})
|
'vf_id': fc_vfid})
|
||||||
|
|
||||||
if sb_connector.lower() in (fc_zone_constants.HTTP,
|
if sb_connector.lower() in (fc_zone_constants.REST_HTTP,
|
||||||
fc_zone_constants.HTTPS):
|
fc_zone_constants.REST_HTTPS):
|
||||||
|
client = importutils.import_object(
|
||||||
|
"cinder.zonemanager.drivers.brocade."
|
||||||
|
"brcd_rest_fc_zone_client.BrcdRestFCZoneClient",
|
||||||
|
ipaddress=fabric_ip,
|
||||||
|
username=fabric_user,
|
||||||
|
password=fabric_pwd,
|
||||||
|
port=fabric_port,
|
||||||
|
vfid=fc_vfid,
|
||||||
|
protocol=sb_connector
|
||||||
|
)
|
||||||
|
elif sb_connector.lower() in (fc_zone_constants.HTTP,
|
||||||
|
fc_zone_constants.HTTPS):
|
||||||
client = importutils.import_object(
|
client = importutils.import_object(
|
||||||
"cinder.zonemanager.drivers.brocade."
|
"cinder.zonemanager.drivers.brocade."
|
||||||
"brcd_http_fc_zone_client.BrcdHTTPFCZoneClient",
|
"brcd_http_fc_zone_client.BrcdHTTPFCZoneClient",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# (c) Copyright 2016 Brocade Communications Systems Inc.
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -27,6 +27,7 @@ add_connection and delete_connection interfaces.
|
|||||||
:zone_name_prefix: Used by: class: 'FCZoneDriver'. Defaults to 'openstack'
|
:zone_name_prefix: Used by: class: 'FCZoneDriver'. Defaults to 'openstack'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import string
|
||||||
|
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -34,7 +35,6 @@ from oslo_log import log as logging
|
|||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
import six
|
import six
|
||||||
import string
|
|
||||||
|
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.i18n import _
|
from cinder.i18n import _
|
||||||
@ -72,9 +72,10 @@ class BrcdFCZoneDriver(fc_zone_driver.FCZoneDriver):
|
|||||||
1.3 - Added HTTP connector support
|
1.3 - Added HTTP connector support
|
||||||
1.4 - Adds support to zone in Virtual Fabrics
|
1.4 - Adds support to zone in Virtual Fabrics
|
||||||
1.5 - Initiator zoning updates through zoneadd/zoneremove
|
1.5 - Initiator zoning updates through zoneadd/zoneremove
|
||||||
|
1.6 - Add REST connector
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "1.5"
|
VERSION = "1.6"
|
||||||
|
|
||||||
# ThirdPartySystems wiki page
|
# ThirdPartySystems wiki page
|
||||||
CI_WIKI_NAME = "Brocade_OpenStack_CI"
|
CI_WIKI_NAME = "Brocade_OpenStack_CI"
|
||||||
@ -226,7 +227,8 @@ class BrcdFCZoneDriver(fc_zone_driver.FCZoneDriver):
|
|||||||
LOG.debug("Zones updated successfully: %(updatemap)s",
|
LOG.debug("Zones updated successfully: %(updatemap)s",
|
||||||
{'updatemap': zone_update_map})
|
{'updatemap': zone_update_map})
|
||||||
except (exception.BrocadeZoningCliException,
|
except (exception.BrocadeZoningCliException,
|
||||||
exception.BrocadeZoningHttpException) as brocade_ex:
|
exception.BrocadeZoningHttpException,
|
||||||
|
exception.BrocadeZoningRestException) as brocade_ex:
|
||||||
raise exception.FCZoneDriverException(brocade_ex)
|
raise exception.FCZoneDriverException(brocade_ex)
|
||||||
except Exception:
|
except Exception:
|
||||||
msg = _("Failed to add or update zoning configuration.")
|
msg = _("Failed to add or update zoning configuration.")
|
||||||
@ -373,7 +375,8 @@ class BrcdFCZoneDriver(fc_zone_driver.FCZoneDriver):
|
|||||||
zone_name_string, zone_activate,
|
zone_name_string, zone_activate,
|
||||||
cfgmap_from_fabric)
|
cfgmap_from_fabric)
|
||||||
except (exception.BrocadeZoningCliException,
|
except (exception.BrocadeZoningCliException,
|
||||||
exception.BrocadeZoningHttpException) as brocade_ex:
|
exception.BrocadeZoningHttpException,
|
||||||
|
exception.BrocadeZoningRestException) as brocade_ex:
|
||||||
raise exception.FCZoneDriverException(brocade_ex)
|
raise exception.FCZoneDriverException(brocade_ex)
|
||||||
except Exception:
|
except Exception:
|
||||||
msg = _("Failed to update or delete zoning "
|
msg = _("Failed to update or delete zoning "
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# (c) Copyright 2016 Brocade Communications Systems Inc.
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -18,13 +18,14 @@ Brocade south bound connector to communicate with switch using
|
|||||||
HTTP or HTTPS protocol.
|
HTTP or HTTPS protocol.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from oslo_serialization import base64
|
|
||||||
from oslo_utils import encodeutils
|
|
||||||
import requests
|
import requests
|
||||||
import six
|
import six
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_serialization import base64
|
||||||
|
from oslo_utils import encodeutils
|
||||||
|
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.i18n import _
|
from cinder.i18n import _
|
||||||
import cinder.zonemanager.drivers.brocade.fc_zone_constants as zone_constant
|
import cinder.zonemanager.drivers.brocade.fc_zone_constants as zone_constant
|
||||||
|
412
cinder/zonemanager/drivers/brocade/brcd_rest_fc_zone_client.py
Normal file
412
cinder/zonemanager/drivers/brocade/brcd_rest_fc_zone_client.py
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
"""
|
||||||
|
Brocade south bound connector to communicate with switch using
|
||||||
|
REST over HTTP or HTTPS protocol.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_serialization import base64
|
||||||
|
import requests
|
||||||
|
import six
|
||||||
|
|
||||||
|
from cinder import exception
|
||||||
|
from cinder.i18n import _
|
||||||
|
from cinder.zonemanager.drivers.brocade import fc_zone_constants
|
||||||
|
from cinder.zonemanager.drivers.brocade import rest_constants
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BrcdRestFCZoneClient(object):
|
||||||
|
|
||||||
|
def __init__(self, ipaddress, username,
|
||||||
|
password, port, vfid, protocol):
|
||||||
|
"""Initializing the client with the parameters passed.
|
||||||
|
|
||||||
|
:param ipaddress: IP Address of the device.
|
||||||
|
:param username: User id to login.
|
||||||
|
:param password: User password.
|
||||||
|
:param port: Device Communication port
|
||||||
|
:param vfid: Virtual Fabric ID.
|
||||||
|
:param protocol: Communication Protocol.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.sw_ip = ipaddress
|
||||||
|
self.sw_user = username
|
||||||
|
self.sw_pwd = password
|
||||||
|
self.protocol = protocol
|
||||||
|
self.vfid = vfid
|
||||||
|
self.status_code = ''
|
||||||
|
self.session = None
|
||||||
|
self._login()
|
||||||
|
|
||||||
|
def is_supported_firmware(self):
|
||||||
|
is_supported_firmware = False
|
||||||
|
fw_version = self._get_firmware_version()
|
||||||
|
ver = fw_version.split(".")
|
||||||
|
if len(ver[0]) > 1:
|
||||||
|
major_ver = ver[0]
|
||||||
|
ver[0] = major_ver[1]
|
||||||
|
if len(ver[2]) > 1:
|
||||||
|
patch_ver = ver[2]
|
||||||
|
ver[2] = patch_ver[0]
|
||||||
|
LOG.debug("Firmware version: %(version)s.", {'version': ver})
|
||||||
|
if int(ver[0] + ver[1] + ver[2]) > 820:
|
||||||
|
is_supported_firmware = True
|
||||||
|
return is_supported_firmware
|
||||||
|
|
||||||
|
def get_active_zone_set(self):
|
||||||
|
active_zone_set, checksum = self._get_effective_zone_set()
|
||||||
|
return active_zone_set
|
||||||
|
|
||||||
|
def get_nameserver_info(self):
|
||||||
|
return self._get_name_server()
|
||||||
|
|
||||||
|
def add_zones(self, add_zone_map, activate, active_zone_set=None):
|
||||||
|
self._add_zones(add_zone_map, activate)
|
||||||
|
|
||||||
|
def update_zones(self, update_zone_map, activate, operation,
|
||||||
|
active_zone_set=None):
|
||||||
|
self._update_zones(update_zone_map, activate, operation)
|
||||||
|
|
||||||
|
def delete_zones(self, zone_names_to_delete, activate,
|
||||||
|
active_zone_set=None):
|
||||||
|
self._delete_zones(zone_names_to_delete, activate)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self._logout()
|
||||||
|
|
||||||
|
def _login(self):
|
||||||
|
if self.protocol == fc_zone_constants.REST_HTTPS:
|
||||||
|
self.protocol = fc_zone_constants.HTTPS
|
||||||
|
else:
|
||||||
|
self.protocol = fc_zone_constants.HTTP
|
||||||
|
if self.session is None:
|
||||||
|
self.session = requests.Session()
|
||||||
|
adapter = requests.adapters.HTTPAdapter(pool_connections=1,
|
||||||
|
pool_maxsize=1)
|
||||||
|
self.session.mount(self.protocol + '://', adapter)
|
||||||
|
credentials = base64.encode_as_text('%s:%s' % (self.sw_user,
|
||||||
|
self.sw_pwd)).replace('\n', '')
|
||||||
|
self.session.headers = {rest_constants.USER_AGENT:
|
||||||
|
rest_constants.ZONE_DRIVER,
|
||||||
|
rest_constants.ACCEPT: rest_constants.YANG,
|
||||||
|
rest_constants.AUTHORIZATION:
|
||||||
|
"Basic %s" % credentials}
|
||||||
|
response = self.session.post(self._build_url(rest_constants.LOGIN))
|
||||||
|
if response.status_code == 200:
|
||||||
|
auth = response.headers.get('Authorization')
|
||||||
|
LOG.info("REST login success, setting auth: %s", auth)
|
||||||
|
self.session.headers = {rest_constants.USER_AGENT:
|
||||||
|
rest_constants.ZONE_DRIVER,
|
||||||
|
rest_constants.ACCEPT: rest_constants.YANG,
|
||||||
|
rest_constants.CONTENT_TYPE:
|
||||||
|
rest_constants.YANG,
|
||||||
|
rest_constants.AUTHORIZATION: auth}
|
||||||
|
else:
|
||||||
|
msg = (_("REST login failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def _logout(self):
|
||||||
|
response = self.session.post(self._build_url(rest_constants.LOGOUT))
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST logout success")
|
||||||
|
else:
|
||||||
|
msg = (_("REST logout failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
|
||||||
|
def _get_firmware_version(self):
|
||||||
|
response = self.session.get(self._build_url(rest_constants.GET_SWITCH))
|
||||||
|
firmware_version = ''
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
json_response = data[rest_constants.RESPONSE]
|
||||||
|
switch = json_response[rest_constants.SWITCH]
|
||||||
|
firmware_version = switch[rest_constants.FIRMWARE_VERSION]
|
||||||
|
LOG.info("REST firmware version: %s", firmware_version)
|
||||||
|
else:
|
||||||
|
msg = (_("REST get switch fw version failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
return firmware_version
|
||||||
|
|
||||||
|
def _get_name_server(self):
|
||||||
|
port_names = []
|
||||||
|
url = self._build_url(rest_constants.GET_NAMESERVER)
|
||||||
|
response = self.session.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
json_response = data[rest_constants.RESPONSE]
|
||||||
|
nsinfos = json_response[rest_constants.FC_NAME_SERVER]
|
||||||
|
i = 0
|
||||||
|
for nsinfo in nsinfos:
|
||||||
|
port_names.append(nsinfos[i][rest_constants.PORT_NAME])
|
||||||
|
i = i + 1
|
||||||
|
else:
|
||||||
|
msg = (_("REST get NS info failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
return port_names
|
||||||
|
|
||||||
|
def _get_effective_zone_set(self):
|
||||||
|
active_zone_set = {}
|
||||||
|
zones_map = {}
|
||||||
|
url = self._build_url(rest_constants.GET_ACTIVE_ZONE_CFG)
|
||||||
|
response = self.session.get(url)
|
||||||
|
checksum = ''
|
||||||
|
active_cfg_name = ''
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
json_response = data[rest_constants.RESPONSE]
|
||||||
|
effective_cfg = json_response[rest_constants.EFFECTIVE_CFG]
|
||||||
|
checksum = effective_cfg[rest_constants.CHECKSUM]
|
||||||
|
try:
|
||||||
|
active_cfg_name = effective_cfg[rest_constants.CFG_NAME]
|
||||||
|
zones = effective_cfg[rest_constants.ENABLED_ZONE]
|
||||||
|
if type(zones) is list:
|
||||||
|
for i, zone in enumerate(zones):
|
||||||
|
zones_map.update({zones[i][rest_constants.ZONE_NAME]:
|
||||||
|
zones[i][rest_constants.MEMBER_ENTRY]
|
||||||
|
[rest_constants.ENTRY_NAME]})
|
||||||
|
else:
|
||||||
|
zones_map.update({zones[rest_constants.ZONE_NAME]:
|
||||||
|
zones[rest_constants.MEMBER_ENTRY]
|
||||||
|
[rest_constants.ENTRY_NAME]})
|
||||||
|
except Exception:
|
||||||
|
active_cfg_name = ''
|
||||||
|
LOG.info("REST get effective zoneset success: "
|
||||||
|
"active cfg: %(cfg_name)s, checksum: %(chksum)s",
|
||||||
|
{'cfg_name': active_cfg_name, 'chksum': checksum})
|
||||||
|
else:
|
||||||
|
msg = (_("REST get effective zoneset failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
active_zone_set = {"active_zone_config": active_cfg_name,
|
||||||
|
"zones": zones_map}
|
||||||
|
return active_zone_set, checksum
|
||||||
|
|
||||||
|
def _add_zones(self, add_zone_map, activate):
|
||||||
|
active_zone_set, checksum = self._get_effective_zone_set()
|
||||||
|
# if activate, get the zones already configured in the active cfg
|
||||||
|
if activate:
|
||||||
|
zones_in_active_cfg = active_zone_set.get("zones")
|
||||||
|
# for each new zone, create a zone entry in defined zone db
|
||||||
|
for zone_name, members in add_zone_map.items():
|
||||||
|
if zone_name not in zones_in_active_cfg:
|
||||||
|
body = {rest_constants.MEMBER_ENTRY:
|
||||||
|
{rest_constants.ENTRY_NAME:
|
||||||
|
add_zone_map.get(zone_name)}}
|
||||||
|
json_str = json.dumps(body)
|
||||||
|
url = self._build_url(rest_constants.POST_ZONE + zone_name)
|
||||||
|
response = self.session.post(url, data=json_str)
|
||||||
|
if response.status_code == 201:
|
||||||
|
LOG.info("REST create zone success: %s", zone_name)
|
||||||
|
else:
|
||||||
|
msg = (_("REST create zone failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
# update the cfg with the new zones
|
||||||
|
active_cfg_name = active_zone_set.get("active_zone_config")
|
||||||
|
active_zones = active_zone_set.get("zones")
|
||||||
|
active_zone_names = active_zones.keys()
|
||||||
|
active_zone_names.extend(add_zone_map.keys())
|
||||||
|
body = {rest_constants.MEMBER_ZONE:
|
||||||
|
{rest_constants.ZONE_NAME: active_zone_names}}
|
||||||
|
json_str = json.dumps(body)
|
||||||
|
if active_cfg_name == '':
|
||||||
|
active_cfg_name = fc_zone_constants.CFG_NAME
|
||||||
|
url = self._build_url(rest_constants.POST_CFG + active_cfg_name)
|
||||||
|
response = self.session.post(url, data=json_str)
|
||||||
|
if response.status_code == 201:
|
||||||
|
LOG.info("REST cfg create success: %s", active_cfg_name)
|
||||||
|
self._save_and_activate_cfg(checksum, activate,
|
||||||
|
active_cfg_name)
|
||||||
|
else:
|
||||||
|
msg = (_("REST cfg create failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
else:
|
||||||
|
url = self._build_url(rest_constants.PATCH_CFG + active_cfg_name)
|
||||||
|
response = self.session.patch(url, data=json_str)
|
||||||
|
# if update successful, save the configuration changes
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST cfg update success: %s", active_cfg_name)
|
||||||
|
self._save_and_activate_cfg(checksum, activate,
|
||||||
|
active_cfg_name)
|
||||||
|
else:
|
||||||
|
msg = (_("REST cfg update failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
|
||||||
|
def _update_zones(self, update_zone_map, activate, operation):
|
||||||
|
active_zone_set, checksum = self._get_effective_zone_set()
|
||||||
|
active_cfg_name = active_zone_set.get("active_zone_config")
|
||||||
|
active_zones = active_zone_set.get("zones")
|
||||||
|
# for each zone, update the zone members in defined zone db
|
||||||
|
for zone_name, members in update_zone_map.items():
|
||||||
|
current_members = active_zones.get(zone_name)
|
||||||
|
if operation == "ADD":
|
||||||
|
new_members = set(members).difference(set(current_members))
|
||||||
|
if new_members:
|
||||||
|
update_zone_map.update({zone_name: new_members})
|
||||||
|
elif operation == "REMOVE":
|
||||||
|
new_members = set(current_members).difference(set(members))
|
||||||
|
if new_members:
|
||||||
|
update_zone_map.update({zone_name: new_members})
|
||||||
|
# for each zone to be updated, make REST PATCH call to update
|
||||||
|
for zone in update_zone_map.keys():
|
||||||
|
body = {rest_constants.MEMBER_ENTRY:
|
||||||
|
{rest_constants.ENTRY_NAME: update_zone_map.get(zone)}}
|
||||||
|
json_str = json.dumps(body)
|
||||||
|
url = self._build_url(rest_constants.POST_ZONE + zone)
|
||||||
|
response = self.session.patch(url, data=json_str)
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST zone update success: %s", zone)
|
||||||
|
else:
|
||||||
|
msg = (_("REST zone update failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
# save and activate the config changes
|
||||||
|
self._save_and_activate_cfg(checksum, activate, active_cfg_name)
|
||||||
|
|
||||||
|
def _delete_zones(self, zone_names_to_delete, activate):
|
||||||
|
zone_names_to_delete = zone_names_to_delete.split(";")
|
||||||
|
active_zone_set, checksum = self._get_effective_zone_set()
|
||||||
|
# for each zone name, make REST DELETE call
|
||||||
|
for zone in zone_names_to_delete:
|
||||||
|
url = self._build_url(rest_constants.DELETE_ZONE + zone)
|
||||||
|
response = self.session.delete(url)
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST delete zone success: %s", zone)
|
||||||
|
else:
|
||||||
|
msg = (_("REST delete zone failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
# update the cfg removing the deleted zones
|
||||||
|
active_cfg_name = active_zone_set.get("active_zone_config")
|
||||||
|
active_zones = active_zone_set.get("zones")
|
||||||
|
active_zone_names = active_zones.keys()
|
||||||
|
if len(active_zone_names) == len(zone_names_to_delete):
|
||||||
|
# disable the cfg
|
||||||
|
url = self._build_url(rest_constants.PATCH_CFG_DISABLE)
|
||||||
|
body = {"checksum": checksum}
|
||||||
|
json_str = json.dumps(body)
|
||||||
|
response = self.session.patch(url, data=json_str)
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST cfg disable success")
|
||||||
|
else:
|
||||||
|
msg = (_("REST cfg disable failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
# delete the cfg
|
||||||
|
url = self._build_url(rest_constants.DELETE_CFG + active_cfg_name)
|
||||||
|
response = self.session.delete(url)
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST cfg delete success: %s", active_cfg_name)
|
||||||
|
else:
|
||||||
|
msg = (_("REST cfg delete failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
checksum = self._get_checksum()
|
||||||
|
self._save_and_activate_cfg(checksum, False, active_cfg_name)
|
||||||
|
else:
|
||||||
|
# update the cfg by removing the deleted zones
|
||||||
|
zone_names_in_cfg = list(set(active_zone_names)
|
||||||
|
.difference(set(zone_names_to_delete)))
|
||||||
|
body = {rest_constants.MEMBER_ZONE:
|
||||||
|
{rest_constants.ZONE_NAME: zone_names_in_cfg}}
|
||||||
|
json_str = json.dumps(body)
|
||||||
|
url = self._build_url(rest_constants.PATCH_CFG + active_cfg_name)
|
||||||
|
response = self.session.patch(url, data=json_str)
|
||||||
|
# if update successful, save the configuration changes
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST cfg update success: %s", active_cfg_name)
|
||||||
|
self._save_and_activate_cfg(checksum, activate,
|
||||||
|
active_cfg_name)
|
||||||
|
else:
|
||||||
|
msg = (_("REST cfg update failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
|
||||||
|
def _save_and_activate_cfg(self, checksum, activate, active_cfg_name):
|
||||||
|
body = {"checksum": checksum}
|
||||||
|
json_str = json.dumps(body)
|
||||||
|
url = self._build_url(rest_constants.PATCH_CFG_SAVE)
|
||||||
|
response = self.session.patch(url, data=json_str)
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST cfg save success")
|
||||||
|
else:
|
||||||
|
msg = (_("REST cfg save failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
# if activate=true, then enable the cfg changes to effective cfg
|
||||||
|
if activate:
|
||||||
|
checksum = self._get_checksum()
|
||||||
|
body = {"checksum": checksum}
|
||||||
|
json_str = json.dumps(body)
|
||||||
|
url = self._build_url(rest_constants.PATCH_CFG_ENABLE
|
||||||
|
+ active_cfg_name)
|
||||||
|
response = self.session.patch(url, data=json_str)
|
||||||
|
if response.status_code == 204:
|
||||||
|
LOG.info("REST cfg activate success: %s", active_cfg_name)
|
||||||
|
else:
|
||||||
|
msg = (_("REST cfg activate failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
|
||||||
|
def _get_checksum(self):
|
||||||
|
url = self._build_url(rest_constants.GET_CHECKSUM)
|
||||||
|
response = self.session.get(url)
|
||||||
|
checksum = ''
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
json_response = data[rest_constants.RESPONSE]
|
||||||
|
effective_cfg = json_response[rest_constants.EFFECTIVE_CFG]
|
||||||
|
checksum = effective_cfg[rest_constants.CHECKSUM]
|
||||||
|
LOG.info("REST get checksum success: %s", checksum)
|
||||||
|
else:
|
||||||
|
msg = (_("REST get checksum failed: %s")
|
||||||
|
% six.text_type(response.text))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.BrocadeZoningRestException(reason=msg)
|
||||||
|
return checksum
|
||||||
|
|
||||||
|
def _build_url(self, path):
|
||||||
|
url = '%s://%s%s' % (self.protocol, self.sw_ip, path)
|
||||||
|
if self.vfid is not None:
|
||||||
|
url = '%s?vf-id=%s' % (url, self.vfid)
|
||||||
|
return url
|
@ -1,4 +1,4 @@
|
|||||||
# (c) Copyright 2016 Brocade Communications Systems Inc.
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -102,3 +102,5 @@ ZONE_TX_STATUS = "status"
|
|||||||
SESSION_LF_ID = "sessionLFId"
|
SESSION_LF_ID = "sessionLFId"
|
||||||
HTTP = "http"
|
HTTP = "http"
|
||||||
HTTPS = "https"
|
HTTPS = "https"
|
||||||
|
REST_HTTP = "rest_http"
|
||||||
|
REST_HTTPS = "rest_https"
|
||||||
|
59
cinder/zonemanager/drivers/brocade/rest_constants.py
Normal file
59
cinder/zonemanager/drivers/brocade/rest_constants.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# (c) Copyright 2019 Brocade, a Broadcom Company
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
YANG = "application/yang-data+json"
|
||||||
|
ACCEPT = "Accept"
|
||||||
|
CONTENT_TYPE = "Content-Type"
|
||||||
|
AUTHORIZATION = "Authorization"
|
||||||
|
USER_AGENT = "User-Agent"
|
||||||
|
ZONE_DRIVER = "OpenStack Zone Driver"
|
||||||
|
LOGIN = "/rest/login"
|
||||||
|
LOGOUT = "/rest/logout"
|
||||||
|
NAME_SERVER = "/rest/running/brocade-name-server"
|
||||||
|
ZONING = "/rest/running/zoning"
|
||||||
|
DEFINED_CFG = "/defined-configuration"
|
||||||
|
EFFECTIVE_CFG = "/effective-configuration"
|
||||||
|
GET_SWITCH = "/rest/running/switch/fibrechannel-switch"
|
||||||
|
GET_NAMESERVER = NAME_SERVER + "/fibrechannel-name-server"
|
||||||
|
GET_DEFINED_ZONE_CFG = ZONING + DEFINED_CFG
|
||||||
|
GET_ACTIVE_ZONE_CFG = ZONING + EFFECTIVE_CFG
|
||||||
|
GET_CHECKSUM = ZONING + EFFECTIVE_CFG + "/checksum"
|
||||||
|
POST_ZONE = ZONING + DEFINED_CFG + "/zone/zone-name/"
|
||||||
|
POST_CFG = ZONING + DEFINED_CFG + "/cfg/cfg-name/"
|
||||||
|
PATCH_CFG = ZONING + DEFINED_CFG + "/cfg/cfg-name/"
|
||||||
|
PATCH_CFG_SAVE = ZONING + EFFECTIVE_CFG + "/cfg-action/1"
|
||||||
|
PATCH_CFG_DISABLE = ZONING + EFFECTIVE_CFG + "/cfg-action/2"
|
||||||
|
PATCH_CFG_ENABLE = ZONING + EFFECTIVE_CFG + "/cfg-name/"
|
||||||
|
DELETE_ZONE = POST_ZONE
|
||||||
|
DELETE_CFG = POST_CFG
|
||||||
|
RESPONSE = "Response"
|
||||||
|
SWITCH = "fibrechannel-switch"
|
||||||
|
FIRMWARE_VERSION = "firmware-version"
|
||||||
|
FC_NAME_SERVER = "fibrechannel-name-server"
|
||||||
|
PORT_NAME = "port-name"
|
||||||
|
DEFINED_CFG = "defined-configuration"
|
||||||
|
CFG = "cfg"
|
||||||
|
CFG_NAME = "cfg-name"
|
||||||
|
MEMBER_ZONE = "member-zone"
|
||||||
|
ZONE_NAME = "zone-name"
|
||||||
|
ZONE = "zone"
|
||||||
|
MEMBER_ENTRY = "member-entry"
|
||||||
|
ENTRY_NAME = "entry-name"
|
||||||
|
ALIAS = "alias"
|
||||||
|
ALIAS_ENTRY_NAME = "alias-entry-name"
|
||||||
|
EFFECTIVE_CFG = "effective-configuration"
|
||||||
|
CHECKSUM = "checksum"
|
||||||
|
ENABLED_ZONE = "enabled-zone"
|
@ -75,9 +75,17 @@ Configure SAN fabric parameters under a section matching the name used in
|
|||||||
|
|
||||||
To define a fabric group for a switch which has Virtual Fabrics
|
To define a fabric group for a switch which has Virtual Fabrics
|
||||||
enabled, include the ``fc_virtual_fabric_id`` configuration option
|
enabled, include the ``fc_virtual_fabric_id`` configuration option
|
||||||
and ``fc_southbound_protocol`` configuration option set to ``HTTP``
|
and ``fc_southbound_protocol`` configuration option set to ``HTTP``,
|
||||||
or ``HTTPS`` in the fabric group. Zoning on VF enabled fabric using
|
``HTTPS``, ``REST_HTTP`` or ``REST_HTTPS`` in the fabric group.
|
||||||
``SSH`` southbound protocol is not supported.
|
Zoning on VF enabled fabric using ``SSH`` southbound protocol is
|
||||||
|
not supported.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
On switches running Fabric OS v8.2.1 or greater, the use of the
|
||||||
|
REST interface is recommended for southbound communication. Set
|
||||||
|
the ``fc_southbound_protocol`` configuration option to ``REST_HTTP``
|
||||||
|
or ``REST_HTTPS`` in the fabric group.
|
||||||
|
|
||||||
System requirements
|
System requirements
|
||||||
-------------------
|
-------------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user