def vpsa_send_cmd(self, cmd, **kwargs): try: response = self.vpsa.send_cmd(cmd, **kwargs) except common.exception.UnknownCmd as e: raise cinder_exception.UnknownCmd(cmd=e.cmd) except common.exception.SessionRequestException as e: raise zadara_exception.ZadaraSessionRequestException(msg=e.msg) except common.exception.BadHTTPResponseStatus as e: raise cinder_exception.BadHTTPResponseStatus(status=e.status) except common.exception.FailedCmdWithDump as e: raise cinder_exception.FailedCmdWithDump(status=e.status, data=e.data) except common.exception.ZadaraInvalidAccessKey: raise zadara_exception.ZadaraCinderInvalidAccessKey() return response
def _generate_vpsa_cmd(self, cmd, **kwargs): """Generate command to be sent to VPSA.""" def _joined_params(params): param_str = [] for k, v in params.items(): param_str.append("%s=%s" % (k, v)) return '&'.join(param_str) # Dictionary of applicable VPSA commands in the following format: # 'command': (method, API_URL, {optional parameters}) vpsa_commands = { 'login': ('POST', '/api/users/login.xml', { 'user': self.conf.zadara_user, 'password': self.conf.zadara_password }), # Volume operations 'create_volume': ('POST', '/api/volumes.xml', { 'name': kwargs.get('name'), 'capacity': kwargs.get('size'), 'pool': self.conf.zadara_vpsa_poolname, 'thin': 'YES', 'crypt': 'YES' if self.conf.zadara_vol_encrypt else 'NO', 'attachpolicies': 'NO' if not self.conf.zadara_default_snap_policy else 'YES' }), 'delete_volume': ('DELETE', '/api/volumes/%s.xml' % kwargs.get('vpsa_vol'), { 'force': 'YES' }), 'expand_volume': ('POST', '/api/volumes/%s/expand.xml' % kwargs.get('vpsa_vol'), { 'capacity': kwargs.get('size') }), # Snapshot operations # Snapshot request is triggered for a single volume though the # API call implies that snapshot is triggered for CG (legacy API). 'create_snapshot': ('POST', '/api/consistency_groups/%s/snapshots.xml' % kwargs.get('cg_name'), { 'display_name': kwargs.get('snap_name') }), 'delete_snapshot': ('DELETE', '/api/snapshots/%s.xml' % kwargs.get('snap_id'), {}), 'create_clone_from_snap': ('POST', '/api/consistency_groups/%s/clone.xml' % kwargs.get('cg_name'), { 'name': kwargs.get('name'), 'snapshot': kwargs.get('snap_id') }), 'create_clone': ('POST', '/api/consistency_groups/%s/clone.xml' % kwargs.get('cg_name'), { 'name': kwargs.get('name') }), # Server operations 'create_server': ('POST', '/api/servers.xml', { 'display_name': kwargs.get('initiator'), 'iqn': kwargs.get('initiator') }), # Attach/Detach operations 'attach_volume': ('POST', '/api/servers/%s/volumes.xml' % kwargs.get('vpsa_srv'), { 'volume_name[]': kwargs.get('vpsa_vol'), 'force': 'NO' }), 'detach_volume': ('POST', '/api/volumes/%s/detach.xml' % kwargs.get('vpsa_vol'), { 'server_name[]': kwargs.get('vpsa_srv'), 'force': 'NO' }), # Get operations 'list_volumes': ('GET', '/api/volumes.xml', {}), 'list_pools': ('GET', '/api/pools.xml', {}), 'list_controllers': ('GET', '/api/vcontrollers.xml', {}), 'list_servers': ('GET', '/api/servers.xml', {}), 'list_vol_attachments': ('GET', '/api/volumes/%s/servers.xml' % kwargs.get('vpsa_vol'), {}), 'list_vol_snapshots': ('GET', '/api/consistency_groups/%s/snapshots.xml' % kwargs.get('cg_name'), {}) } if cmd not in vpsa_commands: raise exception.UnknownCmd(cmd=cmd) else: (method, url, params) = vpsa_commands[cmd] if method == 'GET': # For GET commands add parameters to the URL params.update( dict(access_key=self.access_key, page=1, start=0, limit=0)) url += '?' + _joined_params(params) body = '' elif method == 'DELETE': # For DELETE commands add parameters to the URL params.update(dict(access_key=self.access_key)) url += '?' + _joined_params(params) body = '' elif method == 'POST': if self.access_key: params.update(dict(access_key=self.access_key)) body = _joined_params(params) else: msg = (_('Method %(method)s is not defined') % {'method': method}) LOG.error(msg) raise AssertionError(msg) return (method, url, body)
def submit(self, rel_url, method='GET', params=None, user_id=None, project_id=None, req_id=None, action=None, **kwargs): """Submit a request to the configured API endpoint.""" cfg = self._get_api_cfg() if cfg is None: msg = _("Failed to determine blockbridge API configuration") LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) # alter the url appropriately if an action is requested if action: rel_url += "/actions/%s" % action headers = cfg['default_headers'].copy() url = cfg['base_url'] + rel_url body = None # include user, project and req-id, if supplied tsk_ctx = [] if user_id and project_id: tsk_ctx.append("ext_auth=keystone/%s/%s" % (project_id, user_id)) if req_id: tsk_ctx.append("id=%s", req_id) if tsk_ctx: headers['X-Blockbridge-Task'] = ','.join(tsk_ctx) # encode params based on request method if method in ['GET', 'DELETE']: # For GET method add parameters to the URL if params: url += '?' + urllib.parse.urlencode(params) elif method in ['POST', 'PUT', 'PATCH']: body = jsonutils.dumps(params) headers['Content-Type'] = 'application/json' else: raise exception.UnknownCmd(cmd=method) # connect and execute the request connection = http_client.HTTPSConnection(cfg['host'], cfg['port']) connection.request(method, url, body, headers) response = connection.getresponse() # read response data rsp_body = response.read() rsp_data = jsonutils.loads(rsp_body) connection.close() code = response.status if code in [200, 201, 202, 204]: pass elif code == 401: raise exception.NotAuthorized(_("Invalid credentials")) elif code == 403: raise exception.NotAuthorized(_("Insufficient privileges")) else: raise exception.VolumeBackendAPIException(data=rsp_data['message']) return rsp_data
def _generate_vpsa_cmd(self, cmd, **kwargs): """Generate command to be sent to VPSA.""" def _joined_params(params): param_str = [] for k, v in params.items(): param_str.append("%s=%s" % (k, v)) return '&'.join(param_str) # Dictionary of applicable VPSA commands in the following format: # 'command': (method, API_URL, {optional parameters}) vpsa_commands = { 'login': ('POST', '/api/users/login.xml', {'user': self.user, 'password': self.password}), # Volume operations 'create_volume': ('POST', '/api/volumes.xml', {'display_name': kwargs.get('name'), 'virtual_capacity': kwargs.get('size'), 'raid_group_name[]': FLAGS.zadara_vpsa_poolname, 'quantity': 1, 'cache': FLAGS.zadara_default_cache_policy, 'crypt': FLAGS.zadara_default_encryption, 'mode': FLAGS.zadara_default_striping_mode, 'stripesize': FLAGS.zadara_default_stripesize, 'force': 'NO'}), 'delete_volume': ('DELETE', '/api/volumes/%s.xml' % kwargs.get('vpsa_vol'), {}), # Server operations 'create_server': ('POST', '/api/servers.xml', {'display_name': kwargs.get('initiator'), 'iqn': kwargs.get('initiator')}), # Attach/Detach operations 'attach_volume': ('POST', '/api/servers/%s/volumes.xml' % kwargs.get('vpsa_srv'), {'volume_name[]': kwargs.get('vpsa_vol'), 'force': 'NO'}), 'detach_volume': ('POST', '/api/volumes/%s/detach.xml' % kwargs.get('vpsa_vol'), {'server_name[]': kwargs.get('vpsa_srv'), 'force': 'NO'}), # Get operations 'list_volumes': ('GET', '/api/volumes.xml', {}), 'list_controllers': ('GET', '/api/vcontrollers.xml', {}), 'list_servers': ('GET', '/api/servers.xml', {}), 'list_vol_attachments': ('GET', '/api/volumes/%s/servers.xml' % kwargs.get('vpsa_vol'), {}), } if cmd not in vpsa_commands.keys(): raise exception.UnknownCmd(cmd=cmd) else: (method, url, params) = vpsa_commands[cmd] if method == 'GET': # For GET commands add parameters to the URL params.update(dict(access_key=self.access_key, page=1, start=0, limit=0)) url += '?' + _joined_params(params) body = '' elif method == 'DELETE': # For DELETE commands add parameters to the URL params.update(dict(access_key=self.access_key)) url += '?' + _joined_params(params) body = '' elif method == 'POST': if self.access_key: params.update(dict(access_key=self.access_key)) body = _joined_params(params) else: raise exception.UnknownCmd(cmd=method) return (method, url, body)
def generate_vrm_cmd(self, cmd, **kwargs): cpu = {'quantity': 1} memory = {'quantityMB': 1024} disks = { 'datastoreUrn': 'urn:sites:4749082E:datastores:2', 'quantityGB': 10, 'volType': 1, 'isThin': True, 'sequenceNum': 1, } properties = { 'isEnableHa': True, 'reoverByHost': False, 'isEnableFt': False } COMMANDS = { 'v5.1': { 'list_tasks': ('GET', ('/tasks', kwargs.get(self.RESOURCE_URI), None, None), {}, {}, False), 'list_hosts': ('GET', ('/hosts', kwargs.get(self.RESOURCE_URI), None, None), { 'limit': kwargs.get('limit'), 'offset': kwargs.get('offset'), 'scope': kwargs.get('scope') }, {}, False), 'list_datastores': ('GET', ('/datastores', kwargs.get(self.RESOURCE_URI), None, None), { 'limit': kwargs.get('limit'), 'offset': kwargs.get('offset'), 'scope': kwargs.get('scope') }, {}, False), 'list_volumes': ('GET', ('/volumes', kwargs.get(self.RESOURCE_URI), None, kwargs.get('id')), { 'limit': kwargs.get('limit'), 'offset': kwargs.get('offset'), 'scope': kwargs.get('scope') }, {}, False), 'create_volume': ('POST', ('/volumes', None, None, None), {}, { 'name': kwargs.get('name'), 'quantityGB': kwargs.get('quantityGB'), 'datastoreUrn': kwargs.get('datastoreUrn'), 'uuid': kwargs.get('uuid'), 'isThin': kwargs.get('isThin'), 'type': kwargs.get('type'), 'indepDisk': kwargs.get('indepDisk'), 'persistentDisk': kwargs.get('persistentDisk'), 'volumeId': kwargs.get('volumeId'), }, True), 'delete_volume': ('DELETE', ('/volumes', kwargs.get(self.RESOURCE_URI), None, None), {}, {}, True), 'list_volumesnapshot': ('GET', ('/volumesnapshots', None, kwargs.get('uuid'), None), { 'limit': kwargs.get('limit'), 'offset': kwargs.get('offset'), 'scope': kwargs.get('scope') }, {}, False), 'create_volumesnapshot': ('POST', ('/volumesnapshots', None, None, None), {}, { 'volumeUrn': kwargs.get('vol_urn'), 'snapshotUuid': kwargs.get('uuid'), }, False), 'delete_volumesnapshot': ('DELETE', ('/volumesnapshots', None, None, kwargs.get('id')), {}, { 'snapshotUuid': kwargs.get('snapshotUuid'), }, True), 'createvolumefromsnapshot': ('POST', ('/volumesnapshots', None, "createvol", None), {}, { 'snapshotUuid': kwargs.get('uuid'), 'volumeName': kwargs.get('name'), }, True), 'clone_volume': ('POST', ('/volumes', None, kwargs.get('src_name'), 'action/copyVol'), {}, { 'destinationVolumeID': kwargs.get('dest_name') }, True), 'copy_image_to_volume': ('POST', ('/volumes/imagetovolume', None, None, None), {}, { 'volumePara': { 'quantityGB': kwargs.get('volume_size'), 'urn': kwargs.get('volume_urn') }, 'imagePara': { 'id': kwargs.get('image_id'), 'url': kwargs.get('image_location') }, 'location': kwargs.get('host_urn'), 'needCreateVolume': False }, True), 'copy_volume_to_image': ('POST', ('/volumes/volumetoimage', None, None, None), {}, { 'volumePara': { 'urn': kwargs.get('volume_urn'), 'quantityGB': kwargs.get('volume_size') }, 'imagePara': { 'id': kwargs.get('image_id'), 'url': kwargs.get('image_url') } }, True), 'import_vm_from_image': ('POST', ('/vms/action/import', None, None, None), {}, dict({ 'name': 'name', 'location': kwargs.get('host_urn'), 'autoBoot': 'false', 'url': kwargs.get('url'), 'protocol': 'nfs', 'vmConfig': { 'cpu': { 'quantity': 1 }, 'memory': { 'quantityMB': 1024 }, 'disks': [ { 'pciType': 'IDE', 'datastoreUrn': kwargs.get('ds_urn'), 'quantityGB': kwargs.get('vol_size'), 'volType': 0, 'sequenceNum': 1, }, ], }, 'osOptions': { 'osType': 'Windows', 'osVersion': 32 } }), True), 'detach_vol_from_vm': ('POST', ('/vms', None, kwargs.get('vm_id'), 'action/detachvol'), {}, { 'volUrn': kwargs.get('volUrn') }, True), 'stop_vm': ('POST', ('/vms', None, kwargs.get('vm_id'), 'action/stop'), {}, { 'mode': kwargs.get('mode') }, True), 'delete_vm': ('DELETE', ('/vms', None, kwargs.get('vm_id'), None), {}, {}, True), 'query_vm': ('GET', ('/vms', None, kwargs.get('vm_id'), None), {}, {}, False), 'list_templates': ('GET', ('/vms', None, None, None), { 'limit': kwargs.get('limit'), 'offset': kwargs.get('offset'), 'scope': kwargs.get('scope'), 'isTemplate': 'true' }, {}, False), 'clone_vm': ('POST', ('/vms', None, kwargs.get('template_id'), 'action/clone'), {}, dict({ "name": "cinder-plugin-temp-vm", "description": "cinder-plugin-temp-vm", "isLinkClone": kwargs.get('linked_clone'), 'location': kwargs.get('host_urn'), 'autoBoot': 'false', 'vmConfig': { 'cpu': { 'quantity': 2 }, 'memory': { 'quantityMB': 1024 }, 'disks': [ { 'pciType': 'IDE', 'datastoreUrn': kwargs.get('ds_urn'), 'quantityGB': kwargs.get('volume_size'), 'volType': 0, 'sequenceNum': 1, 'isThin': kwargs.get('is_thin') }, ], }, }), True), }, 'v2.0': {} } path = query = body = None LOG.info("[BRM-DRIVER] version is [%s]", self.version) if self.version not in COMMANDS.keys(): raise driver_exception.UnsupportedVersion() else: commands = COMMANDS[self.version] if cmd not in commands.keys(): raise driver_exception.UnsupportedCommand() else: (method, pathparams, queryparams, bodyparams, hastask) = commands[cmd] resource, resource_uri, tag1, tag2 = pathparams if resource_uri: path = resource_uri LOG.debug(_("[VRM-CINDER] [%s]"), path) else: path = self.site_uri + resource LOG.debug(_("[VRM-CINDER] [%s]"), path) if tag1: path += ('/' + str(tag1)) LOG.debug(_("[VRM-CINDER] [%s]"), path) if tag2: path += ('/' + str(tag2)) LOG.debug(_("[VRM-CINDER] [%s]"), path) if method == 'GET': query = self._joined_params(queryparams) elif method == 'DELETE': query = self._joined_params(queryparams) elif method == 'POST': query = self._joined_params(queryparams) LOG.info("[BRM-DRIVER] _generate_vrm_cmd bodyparams is [%s]", bodyparams) body = json.dumps(bodyparams) LOG.info("[BRM-DRIVER] _generate_vrm_cmd body is [%s]", body) else: raise cinder_exception.UnknownCmd(cmd=method) url = self._generate_url(path, query) LOG.info("[BRM-DRIVER] _generate_vrm_cmd url is [%s]", url) return (method, url, body, hastask)
def _generate_vpsa_cmd(self, cmd, **kwargs): """Generate command to be sent to VPSA.""" # Dictionary of applicable VPSA commands in the following format: # 'command': (method, API_URL, {optional parameters}) vpsa_commands = { 'login': ('POST', '/api/users/login.xml', {'user': self.conf.zadara_user, 'password': self.conf.zadara_password}), # Volume operations 'create_volume': ('POST', '/api/volumes.xml', {'name': kwargs.get('name'), 'capacity': kwargs.get('size'), 'pool': self.conf.zadara_vpsa_poolname, 'thin': 'YES', 'crypt': 'YES' if self.conf.zadara_vol_encrypt else 'NO', 'attachpolicies': 'NO' if not self.conf.zadara_default_snap_policy else 'YES'}), 'delete_volume': ('DELETE', '/api/volumes/%s.xml' % kwargs.get('vpsa_vol'), {'force': 'YES'}), 'expand_volume': ('POST', '/api/volumes/%s/expand.xml' % kwargs.get('vpsa_vol'), {'capacity': kwargs.get('size')}), # Snapshot operations # Snapshot request is triggered for a single volume though the # API call implies that snapshot is triggered for CG (legacy API). 'create_snapshot': ('POST', '/api/consistency_groups/%s/snapshots.xml' % kwargs.get('cg_name'), {'display_name': kwargs.get('snap_name')}), 'delete_snapshot': ('DELETE', '/api/snapshots/%s.xml' % kwargs.get('snap_id'), {}), 'create_clone_from_snap': ('POST', '/api/consistency_groups/%s/clone.xml' % kwargs.get('cg_name'), {'name': kwargs.get('name'), 'snapshot': kwargs.get('snap_id')}), 'create_clone': ('POST', '/api/consistency_groups/%s/clone.xml' % kwargs.get('cg_name'), {'name': kwargs.get('name')}), # Server operations 'create_server': ('POST', '/api/servers.xml', {'display_name': kwargs.get('initiator'), 'iqn': kwargs.get('initiator')}), # Attach/Detach operations 'attach_volume': ('POST', '/api/servers/%s/volumes.xml' % kwargs.get('vpsa_srv'), {'volume_name[]': kwargs.get('vpsa_vol'), 'force': 'NO'}), 'detach_volume': ('POST', '/api/volumes/%s/detach.xml' % kwargs.get('vpsa_vol'), {'server_name[]': kwargs.get('vpsa_srv'), 'force': 'YES'}), # Get operations 'list_volumes': ('GET', '/api/volumes.xml', {}), 'list_pools': ('GET', '/api/pools.xml', {}), 'list_controllers': ('GET', '/api/vcontrollers.xml', {}), 'list_servers': ('GET', '/api/servers.xml', {}), 'list_vol_attachments': ('GET', '/api/volumes/%s/servers.xml' % kwargs.get('vpsa_vol'), {}), 'list_vol_snapshots': ('GET', '/api/consistency_groups/%s/snapshots.xml' % kwargs.get('cg_name'), {})} try: method, url, params = vpsa_commands[cmd] except KeyError: raise exception.UnknownCmd(cmd=cmd) if method == 'GET': params = dict(page=1, start=0, limit=0) body = None elif method in ['DELETE', 'POST']: body = params params = None else: msg = (_('Method %(method)s is not defined') % {'method': method}) LOG.error(msg) raise AssertionError(msg) # 'access_key' was generated using username and password # or it was taken from the input file headers = {'X-Access-Key': self.access_key} return method, url, params, body, headers