def _issue_api_request(self, method_name, params): """All API requests to SolidFire device go through this method Simple json-rpc web based API calls. each call takes a set of paramaters (dict) and returns results in a dict as well. """ host = FLAGS.san_ip # For now 443 is the only port our server accepts requests on port = 443 # NOTE(john-griffith): Probably don't need this, but the idea is # we provide a request_id so we can correlate # responses with requests request_id = int(uuid.uuid4()) # just generate a random number cluster_admin = FLAGS.san_login cluster_password = FLAGS.san_password command = {'method': method_name, 'id': request_id} if params is not None: command['params'] = params payload = json.dumps(command, ensure_ascii=False) payload.encode('utf-8') # we use json-rpc, webserver needs to see json-rpc in header header = {'Content-Type': 'application/json-rpc; charset=utf-8'} if cluster_password is not None: # base64.encodestring includes a newline character # in the result, make sure we strip it off auth_key = base64.encodestring( '%s:%s' % (cluster_admin, cluster_password))[:-1] header['Authorization'] = 'Basic %s' % auth_key LOG.debug(_("Payload for SolidFire API call: %s"), payload) connection = httplib.HTTPSConnection(host, port) connection.request('POST', '/json-rpc/1.0', payload, header) response = connection.getresponse() data = {} if response.status != 200: connection.close() raise exception.SolidFireAPIException(status=response.status) else: data = response.read() try: data = json.loads(data) except (TypeError, ValueError), exc: connection.close() msg = _("Call to json.loads() raised an exception: %s") % exc raise exception.SfJsonEncodeFailure(msg) connection.close()
def _issue_api_request(self, method_name, params, version='1.0'): """All API requests to SolidFire device go through this method. Simple json-rpc web based API calls. each call takes a set of paramaters (dict) and returns results in a dict as well. """ max_simultaneous_clones = ['xMaxSnapshotsPerVolumeExceeded', 'xMaxClonesPerVolumeExceeded', 'xMaxSnapshotsPerNodeExceeded', 'xMaxClonesPerNodeExceeded'] host = self.configuration.san_ip port = self.configuration.sf_api_port cluster_admin = self.configuration.san_login cluster_password = self.configuration.san_password # NOTE(jdg): We're wrapping a retry loop for a know XDB issue # Shows up in very high request rates (ie create 1000 volumes) # we have to wrap the whole sequence because the request_id # can't be re-used retry_count = 5 while retry_count > 0: request_id = hash(uuid.uuid4()) # just generate a random number command = {'method': method_name, 'id': request_id} if params is not None: command['params'] = params payload = json.dumps(command, ensure_ascii=False) payload.encode('utf-8') header = {'Content-Type': 'application/json-rpc; charset=utf-8'} if cluster_password is not None: # base64.encodestring includes a newline character # in the result, make sure we strip it off auth_key = base64.encodestring('%s:%s' % (cluster_admin, cluster_password))[:-1] header['Authorization'] = 'Basic %s' % auth_key LOG.debug(_("Payload for SolidFire API call: %s"), payload) api_endpoint = '/json-rpc/%s' % version connection = httplib.HTTPSConnection(host, port) try: connection.request('POST', api_endpoint, payload, header) except Exception as ex: LOG.error(_('Failed to make httplib connection ' 'SolidFire Cluster: %s (verify san_ip ' 'settings)') % ex.message) msg = _("Failed to make httplib connection: %s") % ex.message raise exception.SolidFireAPIException(msg) response = connection.getresponse() data = {} if response.status != 200: connection.close() LOG.error(_('Request to SolidFire cluster returned ' 'bad status: %(status)s / %(reason)s (check ' 'san_login/san_password settings)') % {'status': response.status, 'reason': response.reason}) msg = (_("HTTP request failed, with status: %(status)s " "and reason: %(reason)s") % {'status': response.status, 'reason': response.reason}) raise exception.SolidFireAPIException(msg) else: data = response.read() try: data = json.loads(data) except (TypeError, ValueError) as exc: connection.close() msg = _("Call to json.loads() raised " "an exception: %s") % exc raise exception.SfJsonEncodeFailure(msg) connection.close() LOG.debug(_("Results of SolidFire API call: %s"), data) if 'error' in data: if data['error']['name'] in max_simultaneous_clones: LOG.warning(_('Clone operation ' 'encountered: %s') % data['error']['name']) LOG.warning(_( 'Waiting for outstanding operation ' 'before retrying snapshot: %s') % params['name']) time.sleep(5) # Don't decrement the retry count for this one elif 'xDBVersionMismatch' in data['error']['name']: LOG.warning(_('Detected xDBVersionMismatch, ' 'retry %s of 5') % (5 - retry_count)) time.sleep(1) retry_count -= 1 elif 'xUnknownAccount' in data['error']['name']: retry_count = 0 else: msg = _("API response: %s") % data raise exception.SolidFireAPIException(msg) else: retry_count = 0 return data