daisycloud-core/code/daisy/daisy/api/v1/disk_array.py

973 lines
41 KiB
Python
Executable File

# Copyright 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.
"""
/hosts endpoint for Daisy v1 API
"""
import ast
from oslo_log import log as logging
from webob.exc import HTTPBadRequest
from webob.exc import HTTPForbidden
from webob.exc import HTTPNotFound
from webob.exc import HTTPConflict
from webob import Response
from daisy import i18n
from daisy import notifier
from daisy.api import policy
import daisy.api.v1
from daisy.common import exception
from daisy.common import utils
from daisy.common import wsgi
import daisy.registry.client.v1.api as registry
from daisy.api.v1 import controller
from daisy.api.v1 import filters
LOG = logging.getLogger(__name__)
_ = i18n._
_LE = i18n._LE
_LI = i18n._LI
_LW = i18n._LW
SUPPORTED_PARAMS = daisy.api.v1.SUPPORTED_PARAMS
SUPPORTED_FILTERS = daisy.api.v1.SUPPORTED_FILTERS
ACTIVE_IMMUTABLE = daisy.api.v1.ACTIVE_IMMUTABLE
SERVICE_DISK_SERVICE = ('db', 'glance', 'db_backup', 'mongodb', 'nova',
'provider', 'cinder', 'ceph')
DISK_LOCATION = ('local', 'share', 'share_cluster')
PROTOCOL_TYPE = ('FIBER', 'ISCSI', 'CEPH', 'LVM', 'RAW')
FC_DRIVER = ('brocade', 'cisco')
FC_ZONEING_POLICY = ('initiator-target', 'initiator')
CINDER_VOLUME_BACKEND_PARAMS = ('management_ips', 'data_ips', 'pools',
'volume_driver', 'volume_type',
'resource_pools', 'role_id', 'user_name',
'user_pwd', 'root_pwd')
OPTICAL_SWITCH_PARAMS = ('user_name', 'user_pwd', 'switch_ip',
'switch_port', 'fc_driver',
'fc_zoneing_policy', 'role_id')
CINDER_VOLUME_BACKEND_DRIVER = ['KS3200_IPSAN', 'KS3200_FCSAN',
'FUJITSU_ETERNUS', 'HP3PAR_FCSAN',
'FUJITSU_FCSAN', 'MacroSAN_FCSAN',
'NETAPP_FCSAN']
class Controller(controller.BaseController):
"""
WSGI controller for hosts resource in Daisy v1 API
The hosts resource API is a RESTful web service for host data. The API
is as follows::
GET /hosts -- Returns a set of brief metadata about hosts
GET /hosts/detail -- Returns a set of detailed metadata about
hosts
HEAD /hosts/<ID> -- Return metadata about an host with id <ID>
GET /hosts/<ID> -- Return host data for host with id <ID>
POST /hosts -- Store host data and return metadata about the
newly-stored host
PUT /hosts/<ID> -- Update host metadata and/or upload host
data for a previously-reserved host
DELETE /hosts/<ID> -- Delete the host with id <ID>
"""
def __init__(self):
self.notifier = notifier.Notifier()
registry.configure_registry_client()
self.policy = policy.Enforcer()
def _enforce(self, req, action, target=None):
"""Authorize an action against our policies"""
if target is None:
target = {}
try:
self.policy.enforce(req.context, action, target)
except exception.Forbidden:
raise HTTPForbidden()
def _get_filters(self, req):
"""
Return a dictionary of query param filters from the request
:param req: the Request object coming from the wsgi layer
:retval a dict of key/value filters
"""
query_filters = {}
for param in req.params:
if param in SUPPORTED_FILTERS:
query_filters[param] = req.params.get(param)
if not filters.validate(param, query_filters[param]):
raise HTTPBadRequest(_('Bad value passed to filter '
'%(filter)s got %(val)s')
% {'filter': param,
'val': query_filters[param]})
return query_filters
def _get_query_params(self, req):
"""
Extracts necessary query params from request.
:param req: the WSGI Request object
:retval dict of parameters that can be used by registry client
"""
params = {'filters': self._get_filters(req)}
for PARAM in SUPPORTED_PARAMS:
if PARAM in req.params:
params[PARAM] = req.params.get(PARAM)
return params
def _raise_404_if_role_deleted(self, req, role_id):
role = self.get_role_meta_or_404(req, role_id)
if role is None or role['deleted']:
msg = _("role with identifier %s has been deleted.") % role_id
raise HTTPNotFound(msg)
if role['type'] == 'template':
msg = "role type of %s is 'template'" % role_id
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
def _raise_404_if_service_disk_deleted(self, req, service_disk_id):
service_disk = self.get_service_disk_meta_or_404(req, service_disk_id)
if service_disk is None or service_disk['deleted']:
msg = _(
"service_disk with identifier %s has been deleted.") % \
service_disk_id
raise HTTPNotFound(msg)
def _default_value_set(self, disk_meta):
if ('disk_location' not in disk_meta or
not disk_meta['disk_location'] or
disk_meta['disk_location'] == ''):
disk_meta['disk_location'] = 'local'
if 'lun' not in disk_meta:
disk_meta['lun'] = 0
if 'size' not in disk_meta:
disk_meta['size'] = -1
if 'protocol_type' not in disk_meta:
disk_meta['protocol_type'] = 'ISCSI'
def _unique_service_in_role(self, req, disk_meta):
params = {'filters': {'role_id': disk_meta['role_id']}}
service_disks = registry.list_service_disk_metadata(
req.context, **params)
if disk_meta['disk_location'] == 'share_cluster':
share_cluster_count = 0
for disk in service_disks:
if disk['service'] == disk_meta['service'] and \
disk['disk_location'] != 'share_cluster':
id = disk['id']
registry.delete_service_disk_metadata(req.context, id)
if disk['disk_location'] == 'share_cluster':
share_cluster_count += 1
if share_cluster_count >= 2:
msg = "There were more than two disk services %s in role %s" %\
(disk_meta['service'], disk_meta['role_id'])
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
else:
for service_disk in service_disks:
if service_disk['disk_location'] == 'share_cluster' and \
service_disk['service'] == disk_meta['service']:
id = service_disk['id']
registry.delete_service_disk_metadata(req.context, id)
elif service_disk['service'] == disk_meta['service']:
msg = "disk service %s has existed in role %s" % (
disk_meta['service'], disk_meta['role_id'])
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
def _service_disk_add_meta_valid(self, req, disk_meta):
if 'role_id' not in disk_meta:
msg = "'role_id' must be given"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
else:
self._raise_404_if_role_deleted(req, disk_meta['role_id'])
if 'service' not in disk_meta:
msg = "'service' must be given"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
else:
if disk_meta['service'] not in SERVICE_DISK_SERVICE:
msg = "service '%s' is not supported" % disk_meta['service']
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta['disk_location'] not in DISK_LOCATION:
msg = "disk_location %s is not supported" % disk_meta[
'disk_location']
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta['disk_location'] in ['share', 'share_cluster'] \
and 'data_ips' not in disk_meta:
msg = "'data_ips' must be given when disk_location was not local"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta['lun'] < 0:
msg = "'lun' should not be less than 0"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
disk_meta['size'] = ast.literal_eval(str(disk_meta['size']))
if not isinstance(disk_meta['size'], int):
msg = "'size' is not integer"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta['size'] < -1:
msg = "'size' is invalid"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta.get('protocol_type', None) \
and disk_meta['protocol_type'] not in PROTOCOL_TYPE:
msg = "protocol type %s is not supported" % disk_meta[
'protocol_type']
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta.get('service', None) == 'provider':
# default lv_provider_size > 5G
if disk_meta.get('size', None):
if disk_meta['size'] < 5:
msg = "When the service disk is provider, the disk " + \
"size should be greater than 5G."
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
self._unique_service_in_role(req, disk_meta)
def _service_disk_update_meta_valid(self, req, id, disk_meta):
orig_disk_meta = self.get_service_disk_meta_or_404(req, id)
if 'role_id' in disk_meta:
self._raise_404_if_role_deleted(req, disk_meta['role_id'])
if 'service' in disk_meta:
if disk_meta['service'] not in SERVICE_DISK_SERVICE:
msg = "service '%s' is not supported" % disk_meta['service']
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if 'disk_location' in disk_meta:
if disk_meta['disk_location'] not in DISK_LOCATION:
msg = "disk_location '%s' is not supported" % disk_meta[
'disk_location']
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if (disk_meta['disk_location'] == 'share' and
'data_ips' not in disk_meta and
not orig_disk_meta['data_ips']):
msg = "'data_ips' must be given when disk_location is share"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if 'size' in disk_meta:
disk_meta['size'] = ast.literal_eval(str(disk_meta['size']))
if not isinstance(disk_meta['size'], int):
msg = "'size' is not integer"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta['size'] < -1:
msg = "'size' is invalid"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta.get('service', None) == 'provider' or \
orig_disk_meta['service'] == 'provider':
# default lv_provider_size > 5G
if disk_meta['size'] < 5:
msg = "When the service disk is provider, the disk " + \
"size should be greater than 5G."
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta.get('protocol_type', None) \
and disk_meta['protocol_type'] not in PROTOCOL_TYPE:
msg = "protocol type %s is not supported" % disk_meta[
'protocol_type']
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
@utils.mutating
def service_disk_add(self, req, disk_meta):
"""
Export daisy db data to tecs.conf and HA.conf.
:param req: The WSGI/Webob Request object
:raises HTTPBadRequest if x-install-cluster is missing
"""
self._enforce(req, 'service_disk_add')
self._default_value_set(disk_meta)
self._service_disk_add_meta_valid(req, disk_meta)
# set default size value when service disk was provider
if disk_meta.get('service', None) == 'provider' and not \
disk_meta.get('size', None):
disk_meta['size'] = 5
service_disk_meta = registry.add_service_disk_metadata(
req.context, disk_meta)
return {'disk_meta': service_disk_meta}
@utils.mutating
def service_disk_delete(self, req, id):
"""
Deletes a service_disk from Daisy.
:param req: The WSGI/Webob Request object
:param image_meta: Mapping of metadata about service_disk
:raises HTTPBadRequest if x-service-disk-name is missing
"""
self._enforce(req, 'delete_service_disk')
try:
registry.delete_service_disk_metadata(req.context, id)
except exception.NotFound as e:
msg = (_("Failed to find service_disk to delete: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPNotFound(explanation=msg,
request=req,
content_type="text/plain")
except exception.Forbidden as e:
msg = (_("Forbidden to delete service_disk: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPForbidden(explanation=msg,
request=req,
content_type="text/plain")
except exception.InUseByStore as e:
msg = (_("service_disk %(id)s could not be deleted "
"because it is in use: "
"%(exc)s") % {"id": id, "exc": utils.exception_to_str(e)})
LOG.warning(msg)
raise HTTPConflict(explanation=msg,
request=req,
content_type="text/plain")
else:
return Response(body='', status=200)
@utils.mutating
def service_disk_update(self, req, id, disk_meta):
self._enforce(req, 'service_disk_update')
self._service_disk_update_meta_valid(req, id, disk_meta)
try:
service_disk_meta = registry.update_service_disk_metadata(
req.context, id, disk_meta)
except exception.Invalid as e:
msg = (_("Failed to update role metadata. Got error: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
except exception.NotFound as e:
msg = (_("Failed to find role to update: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPNotFound(explanation=msg,
request=req,
content_type="text/plain")
except exception.Forbidden as e:
msg = (_("Forbidden to update role: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPForbidden(explanation=msg,
request=req,
content_type="text/plain")
except (exception.Conflict, exception.Duplicate) as e:
LOG.warning(utils.exception_to_str(e))
raise HTTPConflict(body=_('Host operation conflicts'),
request=req,
content_type='text/plain')
else:
self.notifier.info('role.update', service_disk_meta)
return {'disk_meta': service_disk_meta}
@utils.mutating
def service_disk_detail(self, req, id):
"""
Returns metadata about an role in the HTTP headers of the
response object
:param req: The WSGI/Webob Request object
:param id: The opaque role identifier
:raises HTTPNotFound if role metadata is not available to user
"""
self._enforce(req, 'service_disk_detail')
service_disk_meta = self.get_service_disk_meta_or_404(req, id)
return {'disk_meta': service_disk_meta}
def service_disk_list(self, req):
self._enforce(req, 'service_disk_list')
params = self._get_query_params(req)
filters = params.get('filters', None)
if 'role_id' in filters:
role_id = filters['role_id']
self._raise_404_if_role_deleted(req, role_id)
try:
service_disks = registry.list_service_disk_metadata(
req.context, **params)
except exception.Invalid as e:
raise HTTPBadRequest(explanation=e.msg, request=req)
return dict(disk_meta=service_disks)
def _cinder_volume_list(self, req, params):
try:
cinder_volumes = registry.list_cinder_volume_metadata(
req.context, **params)
except exception.Invalid as e:
raise HTTPBadRequest(explanation=e.msg, request=req)
return cinder_volumes
def _optical_switch_list(self, req, params):
try:
optical_switchs = registry.list_optical_switch_metadata(
req.context, **params)
except exception.Invalid as e:
raise HTTPBadRequest(explanation=e.msg, request=req)
return optical_switchs
def _get_cluster_roles(self, req, cluster_id):
params = {'filters': {'cluster_id': cluster_id}}
try:
roles = registry.get_roles_detail(req.context, **params)
except exception.Invalid as e:
raise HTTPBadRequest(explanation=e.msg, request=req)
return roles
# backend_index should be unique in cluster
def _get_cinder_volume_backend_index(self, req, disk_array, cluster_id,
cinder_volume_id=None):
cluster_roles = self._get_cluster_roles(req, cluster_id)
cinder_volumes = []
for role in cluster_roles:
params = {'filters': {'role_id': role['id']}}
role_cinder_volumes = self._cinder_volume_list(req, params)
if role_cinder_volumes:
cinder_volumes += role_cinder_volumes
index = 1
while True:
backend_index = "%s-%s" % (disk_array['volume_driver'], index)
flag = True
for cinder_volume in cinder_volumes:
if backend_index == cinder_volume['backend_index']:
if cinder_volume['id'] == cinder_volume_id:
continue
index = index + 1
flag = False
break
if flag:
break
return backend_index
@utils.mutating
def cinder_volume_add(self, req, disk_meta):
"""
Export daisy db data to tecs.conf and HA.conf.
:param req: The WSGI/Webob Request object
:raises HTTPBadRequest if x-install-cluster is missing
"""
self._enforce(req, 'cinder_volume_add')
if 'role_id' not in disk_meta:
msg = "'role_id' must be given"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
else:
role_detail = self.get_role_meta_or_404(
req, disk_meta['role_id'])
disk_arrays = eval(disk_meta['disk_array'])
for disk_array in disk_arrays:
for key in disk_array.keys():
if (key not in CINDER_VOLUME_BACKEND_PARAMS and
key != 'data_ips'):
msg = "'%s' must be given for cinder volume config" % key
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_array[
'volume_driver'] not in CINDER_VOLUME_BACKEND_DRIVER:
msg = "volume_driver %s is not supported" % disk_array[
'volume_driver']
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_array['volume_driver'] == 'FUJITSU_ETERNUS':
if not disk_array.get('data_ips', None):
msg = "data_ips must be given " + \
"when using FUJITSU Disk Array"
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_array['volume_driver'] in ['FUJITSU_ETERNUS',
'FUJITSU_FCSAN']:
if not disk_array.get('root_pwd', None):
msg = "root_pwd must be given " + \
"when using FUJITSU Disk Array"
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_array.get('resource_pools', None):
resource_pools_str = disk_array['resource_pools']
if len(disk_array['resource_pools'].split(',')) != 1:
msg = "Only one resource pool can be specified."
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
disk_array['role_id'] = disk_meta['role_id']
disk_array['backend_index'] = \
self._get_cinder_volume_backend_index(
req, disk_array, role_detail['cluster_id'])
cinder_volumes = registry.add_cinder_volume_metadata(
req.context, disk_array)
return {'disk_meta': cinder_volumes}
@utils.mutating
def cinder_volume_delete(self, req, id):
"""
Deletes a service_disk from Daisy.
:param req: The WSGI/Webob Request object
:param image_meta: Mapping of metadata about service_disk
:raises HTTPBadRequest if x-service-disk-name is missing
"""
self._enforce(req, 'delete_cinder_volume')
try:
registry.delete_cinder_volume_metadata(req.context, id)
except exception.NotFound as e:
msg = (_("Failed to find cinder volume to delete: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPNotFound(explanation=msg,
request=req,
content_type="text/plain")
except exception.Forbidden as e:
msg = (_("Forbidden to delete cinder volume: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPForbidden(explanation=msg,
request=req,
content_type="text/plain")
except exception.InUseByStore as e:
msg = (_("cindre volume %(id)s could not "
"be deleted because it is in use: "
"%(exc)s") % {"id": id, "exc": utils.exception_to_str(e)})
LOG.warning(msg)
raise HTTPConflict(explanation=msg,
request=req,
content_type="text/plain")
else:
return Response(body='', status=200)
def _is_data_ips_valid(self, req, update_id, update_meta):
orgin_cinder_volume = self.get_cinder_volume_meta_or_404(
req, update_id)
new_driver = update_meta.get('volume_driver',
orgin_cinder_volume['volume_driver'])
if new_driver != 'FUJITSU_ETERNUS':
return
new_data_ips = update_meta.get('data_ips',
orgin_cinder_volume['data_ips'])
if not new_data_ips:
msg = "data_ips must be given when using FUJITSU Disk Array"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
@utils.mutating
def cinder_volume_update(self, req, id, disk_meta):
for key in disk_meta.keys():
if key not in CINDER_VOLUME_BACKEND_PARAMS:
msg = "'%s' must be given for cinder volume config" % key
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
orgin_cinder_volume = self.get_cinder_volume_meta_or_404(req, id)
if 'role_id' in disk_meta:
role_detail = self.get_role_meta_or_404(
req, disk_meta['role_id'])
else:
role_detail = self.get_role_meta_or_404(
req, orgin_cinder_volume['role_id'])
if ('volume_driver' in disk_meta and disk_meta[
'volume_driver'] not in CINDER_VOLUME_BACKEND_DRIVER):
msg = "volume_driver %s is not supported" % disk_meta[
'volume_driver']
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if disk_meta.get('volume_driver', None):
volume_driver = disk_meta['volume_driver']
disk_meta['backend_index'] = \
self._get_cinder_volume_backend_index(
req, disk_meta, role_detail['cluster_id'], id)
else:
volume_driver = orgin_cinder_volume['volume_driver']
if volume_driver == 'FUJITSU_ETERNUS':
if not disk_meta.get('root_pwd', None):
disk_meta['root_pwd'] = orgin_cinder_volume['root_pwd']
if not disk_meta['root_pwd']:
msg = "root_pwd must be given " + \
"when using FUJITSU Disk Array"
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
self._is_data_ips_valid(req, id, disk_meta)
try:
cinder_volume_meta = registry.update_cinder_volume_metadata(
req.context, id, disk_meta)
except exception.Invalid as e:
msg = (
_("Failed to update cinder_volume metadata. Got error: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
except exception.NotFound as e:
msg = (_("Failed to find cinder_volume to update: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPNotFound(explanation=msg,
request=req,
content_type="text/plain")
except exception.Forbidden as e:
msg = (_("Forbidden to update cinder_volume: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPForbidden(explanation=msg,
request=req,
content_type="text/plain")
except (exception.Conflict, exception.Duplicate) as e:
LOG.warning(utils.exception_to_str(e))
raise HTTPConflict(body=_('cinder_volume operation conflicts'),
request=req,
content_type='text/plain')
else:
self.notifier.info('cinder_volume.update', cinder_volume_meta)
return {'disk_meta': cinder_volume_meta}
@utils.mutating
def cinder_volume_detail(self, req, id):
"""
Returns metadata about an role in the HTTP headers of the
response object
:param req: The WSGI/Webob Request object
:param id: The opaque role identifier
:raises HTTPNotFound if role metadata is not available to user
"""
self._enforce(req, 'cinder_volume_detail')
cinder_volume_meta = self.get_cinder_volume_meta_or_404(req, id)
return {'disk_meta': cinder_volume_meta}
def cinder_volume_list(self, req):
self._enforce(req, 'cinder_volume_list')
params = self._get_query_params(req)
filters = params.get('filters', None)
if 'role_id' in filters:
role_id = filters['role_id']
self._raise_404_if_role_deleted(req, role_id)
cinder_volumes = self._cinder_volume_list(req, params)
return dict(disk_meta=cinder_volumes)
@utils.mutating
def optical_switch_add(self, req, disk_meta):
"""
Export daisy db data to tecs.conf and HA.conf.
:param req: The WSGI/Webob Request object
:raises HTTPBadRequest if x-install-cluster is missing
"""
self._enforce(req, 'optical_switch_add')
if 'role_id' not in disk_meta:
msg = "'role_id' must be given"
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
else:
self.get_role_meta_or_404(req, disk_meta['role_id'])
optical_switchs = eval(disk_meta['switch_array'])
for optical_switch in optical_switchs:
for switch_config in optical_switch.keys():
if switch_config not in OPTICAL_SWITCH_PARAMS:
msg = "'%s' is not needed when config optical " \
"switch" % switch_config
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if switch_config == 'fc_driver' and \
optical_switch['fc_driver'] not in FC_DRIVER:
msg = "'%s' is not supported as fc driver when " \
"config optical switch" % \
optical_switch['fc_driver']
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if switch_config == 'fc_zoneing_policy' \
and optical_switch['fc_zoneing_policy'] \
not in FC_ZONEING_POLICY:
msg = "'%s' is not supported as " \
"fc_zoneing_policy when config optical " \
"switch" % optical_switch['fc_zoneing_policy']
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
optical_switch['role_id'] = disk_meta['role_id']
optical_switchs = registry.\
add_optical_switch_metadata(req.context, optical_switch)
return {'disk_meta': optical_switchs}
@utils.mutating
def optical_switch_list(self, req):
self._enforce(req, 'optical_switch_list')
params = self._get_query_params(req)
filters = params.get('filters', None)
if 'role_id' in filters:
role_id = filters['role_id']
self._raise_404_if_role_deleted(req, role_id)
optical_switchs = self._optical_switch_list(req, params)
return dict(disk_meta=optical_switchs)
@utils.mutating
def optical_switch_update(self, req, id, disk_meta):
self._enforce(req, 'optical_switch_update')
for key in disk_meta.keys():
if key not in OPTICAL_SWITCH_PARAMS:
msg = "'%s' is not needed for optical " \
"switch config" % key
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if key == 'fc_driver' \
and disk_meta['fc_driver'] not in FC_DRIVER:
msg = "'%s' is not supported as fc driver when " \
"config optical switch" % \
disk_meta['fc_driver']
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if key == 'fc_zoneing_policy' \
and disk_meta['fc_zoneing_policy'] not in \
FC_ZONEING_POLICY:
msg = "'%s' is not supported as " \
"fc_zoneing_policy when config optical " \
"switch" % disk_meta['fc_zoneing_policy']
LOG.error(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
if 'role_id' in disk_meta:
self._raise_404_if_role_deleted(req, disk_meta['role_id'])
orgin_optical_switch = self.get_optical_switch_meta_or_404(req, id)
orgin_optical_switch.update(disk_meta)
try:
optical_switch_meta = registry.update_optical_switch_metadata(
req.context, id, orgin_optical_switch)
except exception.Invalid as e:
msg = (
_("Failed to update optical_switch metadata. "
"Got error: %s") % utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPBadRequest(explanation=msg,
request=req,
content_type="text/plain")
except exception.NotFound as e:
msg = (_("Failed to find optical_switch to update: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPNotFound(explanation=msg,
request=req,
content_type="text/plain")
except exception.Forbidden as e:
msg = (_("Forbidden to update cinder_volume: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPForbidden(explanation=msg,
request=req,
content_type="text/plain")
else:
self.notifier.info('optical_switch.update',
optical_switch_meta)
return {'disk_meta': optical_switch_meta}
@utils.mutating
def optical_switch_delete(self, req, id):
"""
Deletes a optical_switch from Daisy.
:param req: The WSGI/Webob Request object
:param image_meta: Mapping of metadata about optical_switch
:raises HTTPBadRequest if x-service-disk-name is missing
"""
self._enforce(req, 'optical_switch_delete')
try:
registry.delete_optical_switch_metadata(req.context, id)
except exception.NotFound as e:
msg = (_("Failed to find optical switch to delete: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPNotFound(explanation=msg,
request=req,
content_type="text/plain")
except exception.Forbidden as e:
msg = (_("Forbidden to delete optical switch: %s") %
utils.exception_to_str(e))
LOG.warning(msg)
raise HTTPForbidden(explanation=msg,
request=req,
content_type="text/plain")
else:
return Response(body='', status=200)
class DiskArrayDeserializer(wsgi.JSONRequestDeserializer):
"""Handles deserialization of specific controller method requests."""
def _deserialize(self, request):
result = {}
result["disk_meta"] = utils.get_dict_meta(request)
return result
def service_disk_add(self, request):
return self._deserialize(request)
def service_disk_update(self, request):
return self._deserialize(request)
def cinder_volume_add(self, request):
return self._deserialize(request)
def cinder_volume_update(self, request):
return self._deserialize(request)
def optical_switch_add(self, request):
return self._deserialize(request)
def optical_switch_update(self, request):
return self._deserialize(request)
class DiskArraySerializer(wsgi.JSONResponseSerializer):
"""Handles serialization of specific controller method responses."""
def __init__(self):
self.notifier = notifier.Notifier()
def service_disk_add(self, response, result):
response.status = 201
response.headers['Content-Type'] = 'application/json'
response.body = self.to_json(result)
return response
def service_disk_update(self, response, result):
response.status = 201
response.headers['Content-Type'] = 'application/json'
response.body = self.to_json(result)
return response
def cinder_volume_add(self, response, result):
response.status = 201
response.headers['Content-Type'] = 'application/json'
response.body = self.to_json(result)
return response
def cinder_volume_update(self, response, result):
response.status = 201
response.headers['Content-Type'] = 'application/json'
response.body = self.to_json(result)
return response
def optical_switch_add(self, response, result):
response.status = 201
response.headers['Content-Type'] = 'application/json'
response.body = self.to_json(result)
return response
def optical_switch_update(self, response, result):
response.status = 201
response.headers['Content-Type'] = 'application/json'
response.body = self.to_json(result)
return response
def create_resource():
"""Image members resource factory method"""
deserializer = DiskArrayDeserializer()
serializer = DiskArraySerializer()
return wsgi.Resource(Controller(), deserializer, serializer)