diff -r 097063f324c0 -r 4b1def16fe9b components/openstack/cinder/files/zfssa/zfssarest.py --- a/components/openstack/cinder/files/zfssa/zfssarest.py Thu Apr 16 01:36:32 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,614 +0,0 @@ -# Copyright (c) 2014, Oracle and/or its affiliates. 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. -""" -ZFS Storage Appliance Proxy -""" -import json -import socket - -from cinder import exception -from cinder.openstack.common import log - -from cinder.volume.drivers.zfssa import restclient - -LOG = log.getLogger(__name__) - - -#pylint: disable=R0913 -#pylint: disable=R0904 -class ZFSSAApi(object): - """ZFSSA API proxy class""" - def __init__(self, host): - self.host = host - self.url = "https://" + self.host + ":215" - self.rclient = restclient.RestClientURL(self.url) - - def __del__(self): - if self.rclient and self.rclient.islogin(): - self.rclient.logout() - - def _is_pool_owned(self, pdata): - """returns True if the pool's owner is the - same as the host. - """ - svc = '/api/system/v1/version' - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error getting version: ' - 'svc: %(svc)s.' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'svc': svc, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - vdata = json.loads(ret.data) - return vdata['version']['asn'] == pdata['pool']['asn'] and \ - vdata['version']['nodename'] == pdata['pool']['owner'] - - def login(self, auth_str): - """Login to the appliance""" - if self.rclient: - self.rclient.login(auth_str) - - def get_pool_stats(self, pool): - """Get space_available and used properties of a pool - returns (avail, used) - """ - svc = '/api/storage/v1/pools/' + pool - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Getting Pool Stats: ' - 'Pool: %(pool)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'pool': pool, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.InvalidVolume(reason=exception_msg) - - val = json.loads(ret.data) - - if not self._is_pool_owned(val): - exception_msg = (_('Error Pool ownership: ' - 'Pool %(pool)s is not owned ' - 'by %(host)s.') - % {'pool': pool, - 'host': self.host}) - LOG.error(exception_msg) - raise exception.InstanceNotFound(instance_id=pool) - - avail = val['pool']['usage']['available'] - used = val['pool']['usage']['used'] - - return (avail, used) - - def create_project(self, pool, project, compression=None, logbias=None): - """Create a project on a pool - Check first whether the pool exists. - """ - self.verify_pool(pool) - svc = '/api/storage/v1/pools/' + pool + '/projects/' + project - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - svc = '/api/storage/v1/pools/' + pool + '/projects' - arg = { - 'name': project - } - if compression and compression != '': - arg.update({'compression': compression}) - if logbias and logbias != '': - arg.update({'logbias': logbias}) - - ret = self.rclient.post(svc, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Creating Project: ' - '%(project)s on ' - 'Pool: %(pool)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s .') - % {'project': project, - 'pool': pool, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def create_initiator(self, initiator, alias, chapuser=None, - chapsecret=None): - """Create an iSCSI initiator""" - - svc = '/api/san/v1/iscsi/initiators/alias=' + alias - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - svc = '/api/san/v1/iscsi/initiators' - arg = { - 'initiator': initiator, - 'alias': alias - } - if chapuser and chapuser != '' and chapsecret and chapsecret != '': - arg.update({'chapuser': chapuser, - 'chapsecret': chapsecret}) - - ret = self.rclient.post(svc, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Creating Initator: ' - '%(initiator)s on ' - 'Alias: %(alias)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s .') - % {'initiator': initiator, - 'alias': alias, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def add_to_initiatorgroup(self, initiator, initiatorgroup): - """Add an iSCSI initiator to initiatorgroup""" - svc = '/api/san/v1/iscsi/initiator-groups/' + initiatorgroup - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - svc = '/api/san/v1/iscsi/initiator-groups' - arg = { - 'name': initiatorgroup, - 'initiators': [initiator] - } - ret = self.rclient.post(svc, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Adding Initator: ' - '%(initiator)s on group' - 'InitiatorGroup: %(initiatorgroup)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s .') - % {'initiator': initiator, - 'initiatorgroup': initiatorgroup, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - else: - svc = '/api/san/v1/iscsi/initiator-groups/' + initiatorgroup - arg = { - 'initiators': [initiator] - } - ret = self.rclient.put(svc, arg) - if ret.status != restclient.Status.ACCEPTED: - exception_msg = (_('Error Adding Initator: ' - '%(initiator)s on group' - 'InitiatorGroup: %(initiatorgroup)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s .') - % {'initiator': initiator, - 'initiatorgroup': initiatorgroup, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def create_target(self, alias, interfaces=None, tchapuser=None, - tchapsecret=None): - """Create an iSCSI target - interfaces: an array with network interfaces - tchapuser, tchapsecret: target's chapuser and chapsecret - returns target iqn - """ - svc = '/api/san/v1/iscsi/targets/alias=' + alias - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - svc = '/api/san/v1/iscsi/targets' - arg = { - 'alias': alias - } - - if tchapuser and tchapuser != '' and tchapsecret and \ - tchapsecret != '': - arg.update({'targetchapuser': tchapuser, - 'targetchapsecret': tchapsecret, - 'auth': 'chap'}) - - if interfaces is not None and len(interfaces) > 0: - arg.update({'interfaces': interfaces}) - - ret = self.rclient.post(svc, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Creating Target: ' - '%(alias)s' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s .') - % {'alias': alias, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - val = json.loads(ret.data) - return val['target']['iqn'] - - def get_target(self, alias): - """Get an iSCSI target iqn""" - svc = '/api/san/v1/iscsi/targets/alias=' + alias - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Getting Target: ' - '%(alias)s' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s .') - % {'alias': alias, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - val = json.loads(ret.data) - return val['target']['iqn'] - - def add_to_targetgroup(self, iqn, targetgroup): - """Add an iSCSI target to targetgroup""" - svc = '/api/san/v1/iscsi/target-groups/' + targetgroup - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - svccrt = '/api/san/v1/iscsi/target-groups' - arg = { - 'name': targetgroup, - 'targets': [iqn] - } - - ret = self.rclient.post(svccrt, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Creating TargetGroup: ' - '%(targetgroup)s with' - 'IQN: %(iqn)s' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s .') - % {'targetgroup': targetgroup, - 'iqn': iqn, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - return - - arg = { - 'targets': [iqn] - } - - ret = self.rclient.put(svc, arg) - if ret.status != restclient.Status.ACCEPTED: - exception_msg = (_('Error Adding to TargetGroup: ' - '%(targetgroup)s with' - 'IQN: %(iqn)s' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'targetgroup': targetgroup, - 'iqn': iqn, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def verify_pool(self, pool): - """Checks whether pool exists""" - svc = '/api/storage/v1/pools/' + pool - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Verifying Pool: ' - '%(pool)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'pool': pool, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def verify_project(self, pool, project): - """Checks whether project exists""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + project - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Verifying ' - 'Project: %(project)s on ' - 'Pool: %(pool)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'project': project, - 'pool': pool, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def verify_initiator(self, iqn): - """Check whether initiator iqn exists""" - svc = '/api/san/v1/iscsi/initiators/' + iqn - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Verifying ' - 'Initiator: %(iqn)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'initiator': iqn, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def verify_target(self, alias): - """Check whether target alias exists.""" - svc = '/api/san/v1/iscsi/targets/alias=' + alias - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Verifying ' - 'Target: %(alias)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'alias': alias, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def create_lun(self, pool, project, lun, volsize, targetgroup, - volblocksize='8k', sparse=False, compression=None, - logbias=None): - """Create a LUN - required - pool, project, lun, volsize, targetgroup. - optional - volblocksize, sparse, compression, logbias - """ - - svc = '/api/storage/v1/pools/' + pool + '/projects/' + project + \ - '/luns/' + lun - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns' - arg = { - 'name': lun, - 'volsize': volsize, - 'targetgroup': targetgroup, - 'initiatorgroup': 'com.sun.ms.vss.hg.maskAll', - 'volblocksize': volblocksize, - 'sparse': sparse - } - if compression and compression != '': - arg.update({'compression': compression}) - if logbias and logbias != '': - arg.update({'logbias': logbias}) - - ret = self.rclient.post(svc, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Creating ' - 'Volume: %(lun)s ' - 'Size: %(size)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'lun': lun, - 'size': volsize, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def get_lun(self, pool, project, lun): - """return iscsi lun properties""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + "/luns/" + lun - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Getting ' - 'Volume: %(lun)s on ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'lun': lun, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - val = json.loads(ret.data) - ret = { - 'guid': val['lun']['lunguid'], - 'number': val['lun']['assignednumber'], - 'initiatorgroup': val['lun']['initiatorgroup'], - 'size': val['lun']['volsize'] - } - if 'origin' in val['lun']: - ret.update({'origin': val['lun']['origin']}) - - return ret - - def set_lun_initiatorgroup(self, pool, project, lun, initiatorgroup): - """Set the initiatorgroup property of a LUN""" - if initiatorgroup == '': - initiatorgroup = 'com.sun.ms.vss.hg.maskAll' - - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns/' + lun - arg = { - 'initiatorgroup': initiatorgroup - } - - ret = self.rclient.put(svc, arg) - if ret.status != restclient.Status.ACCEPTED: - exception_msg = (_('Error Setting ' - 'Volume: %(lun)s to ' - 'InitiatorGroup: %(initiatorgroup)s ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'lun': lun, - 'initiatorgroup': initiatorgroup, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - - def delete_lun(self, pool, project, lun): - """delete iscsi lun""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns/' + lun - - ret = self.rclient.delete(svc) - if ret.status != restclient.Status.NO_CONTENT: - exception_msg = (_('Error Deleting ' - 'Volume: %(lun)s to ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'lun': lun, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - - def create_snapshot(self, pool, project, lun, snapshot): - """create snapshot""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns/' + lun + '/snapshots' - arg = { - 'name': snapshot - } - - ret = self.rclient.post(svc, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Creating ' - 'Snapshot: %(snapshot)s on' - 'Volume: %(lun)s to ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'snapshot': snapshot, - 'lun': lun, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def delete_snapshot(self, pool, project, lun, snapshot): - """delete snapshot""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns/' + lun + '/snapshots/' + snapshot - - ret = self.rclient.delete(svc) - if ret.status != restclient.Status.NO_CONTENT: - exception_msg = (_('Error Deleting ' - 'Snapshot: %(snapshot)s on ' - 'Volume: %(lun)s to ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'snapshot': snapshot, - 'lun': lun, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def clone_snapshot(self, pool, project, lun, snapshot, clone): - """clone snapshot""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns/' + lun + '/snapshots/' + snapshot + '/clone' - arg = { - 'project': project, - 'share': clone - } - - ret = self.rclient.put(svc, arg) - if ret.status != restclient.Status.CREATED: - exception_msg = (_('Error Cloning ' - 'Snapshot: %(snapshot)s on ' - 'Volume: %(lun)s of ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'snapshot': snapshot, - 'lun': lun, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def set_lun_size(self, pool, project, lun, size): - """increase lun size capacity""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns/' + lun - arg = { - 'volsize': size - } - - ret = self.rclient.put(svc, arg) - if ret.status != restclient.Status.ACCEPTED: - exception_msg = (_('Error Setting size on ' - 'Size: %(size)s on ' - 'Volume: %(lun)s of ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'size': size, - 'lun': lun, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - def has_clones(self, pool, project, lun, snapshot): - """Checks whether snapshot has clones or not.""" - svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ - project + '/luns/' + lun + '/snapshots/' + snapshot - - ret = self.rclient.get(svc) - if ret.status != restclient.Status.OK: - exception_msg = (_('Error Getting ' - 'Snapshot: %(snapshot)s on ' - 'Volume: %(lun)s to ' - 'Pool: %(pool)s ' - 'Project: %(project)s ' - 'Return code: %(ret.status)d ' - 'Message: %(ret.data)s.') - % {'snapshot': snapshot, - 'lun': lun, - 'pool': pool, - 'project': project, - 'ret.status': ret.status, - 'ret.data': ret.data}) - LOG.error(exception_msg) - raise exception.VolumeBackendAPIException(data=exception_msg) - - val = json.loads(ret.data) - return (val['snapshot']['numclones'] != 0)