def _do_request(self, method, action, body=None, headers=None, vsm_ip=None): """Perform the HTTP request. The response is in either JSON format or plain text. A GET method will invoke a JSON response while a PUT/POST/DELETE returns message from the VSM in plain text format. Exception is raised when VSM replies with an INTERNAL SERVER ERROR HTTP status code (500) i.e. an error has occurred on the VSM or SERVICE UNAVAILABLE (404) i.e. VSM is not reachable. :param method: type of the HTTP request. POST, GET, PUT or DELETE :param action: path to which the client makes request :param body: dict for arguments which are sent as part of the request :param headers: header for the HTTP request :param vsm_ip: vsm_ip for the HTTP request. If not provided then request will be sent to all VSMs. :returns: JSON or plain text in HTTP response """ action = self.action_prefix + action if body: body = jsonutils.dumps(body) LOG.debug("req: %s", body) hosts = [] if vsm_ip: hosts.append(vsm_ip) else: hosts = self.get_vsm_hosts() if not headers: headers = self._get_auth_header() headers['Content-Type'] = headers['Accept'] = "application/json" for vsm_ip in hosts: vsm_action = action % vsm_ip try: resp = self.pool.spawn(requests.request, method, url=vsm_action, data=body, headers=headers, timeout=self.timeout).wait() except Exception as e: raise n1kv_exc.VSMConnectionFailed(reason=e) if resp.status_code != requests.codes.OK: raise n1kv_exc.VSMError(reason=resp.text) if 'application/json' in resp.headers['content-type']: try: return resp.json() except ValueError: return {} elif 'text/plain' in resp.headers['content-type']: LOG.info(_LI("VSM: %s"), resp.text)
def test_network_profile_create_on_vsm_connection_failed(self): """Test a network profile creation when the operation fails on VSM.""" data = self.get_test_network_profile_dict(segment_type='vxlan', sub_type='enhanced') new_net_prof_name = data["network_profile"]["name"] mocked_ex = mock.MagicMock(side_effect=n1kv_exc.VSMConnectionFailed( reason='Connection to VSM lost')) with mock.patch(n1kv_client.__name__ + ".Client.create_network_segment_pool", mocked_ex): network_req = self.new_create_request('network_profiles', data) res = network_req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPServiceUnavailable.code, res.status_int) # list all network profiles list_req = self.new_list_request('network_profiles') list_res = list_req.get_response(self.ext_api) netprofs = self.deserialize(self.fmt, list_res) # assert that the network profile created is cleaned up on neutron # when creation on VSM fails self.assertNotIn( needle=new_net_prof_name, haystack=[d["name"] for d in netprofs["network_profiles"]])
def test_create_network_profile_on_vsm_connection_failed(self): """ Test a network profile creation when the connection to VSM is lost. """ data = self.get_test_network_profile_dict(segment_type='vxlan', sub_type='enhanced') new_net_prof_name = data["network_profile"]["name"] mocked_ex = mock.MagicMock(side_effect=n1kv_exc.VSMConnectionFailed( reason='Connection to VSM lost')) with mock.patch( n1kv_client.__name__ + ".Client.create_network_segment_pool", mocked_ex): self.create_assert_network_profile_failure( data=data, expected_status=webob.exc.HTTPServiceUnavailable.code) # list all network profiles netprofs = self.list_resource(resource='network_profiles', tenant_id=self.admin_tenant) # assert that the network profile created is cleaned up on neutron # when creation on VSM fails self.assertNotIn( needle=new_net_prof_name, haystack=[d["name"] for d in netprofs["network_profiles"]])
def _do_request(self, method, action, body=None, headers=None, vsm_ip=None): """Perform the HTTP request. The response is in either JSON format or plain text. A GET method will invoke a JSON response while a PUT/POST/DELETE returns message from the VSM in plain text format. Exception is raised when VSM replies with an INTERNAL SERVER ERROR HTTP status code (500) i.e. an error has occurred on the VSM or SERVICE UNAVAILABLE (404) i.e. VSM is not reachable. :param method: type of the HTTP request. POST, GET, PUT or DELETE :param action: path to which the client makes request :param body: dict for arguments which are sent as part of the request :param headers: header for the HTTP request :param vsm_ip: vsm_ip for the HTTP request. If not provided then request will be sent to all VSMs. :returns: JSON or plain text in HTTP response """ action = self.action_prefix + action if body: body = jsonutils.dumps(body) LOG.debug("req: %s", body) hosts = [] if vsm_ip: hosts.append(vsm_ip) else: hosts = self.vsm_ips if not headers: headers = self._get_auth_header() headers['Content-Type'] = headers['Accept'] = "application/json" for vsm_ip in hosts: if netutils.is_valid_ipv6(vsm_ip): # Enclose IPv6 address in [] in the URL vsm_action = action % ("[%s]" % vsm_ip) else: # IPv4 address vsm_action = action % vsm_ip for attempt in range(self.max_vsm_retries + 1): try: LOG.debug("[VSM %(vsm)s attempt %(id)s]: Connecting.." % { "vsm": vsm_ip, "id": attempt }) resp = self.pool.spawn(requests.request, method, url=vsm_action, data=body, headers=headers, timeout=self.timeout).wait() break except Exception as e: LOG.debug("[VSM %(vsm)s attempt %(id)s]: Conn timeout." % { "vsm": vsm_ip, "id": attempt }) if attempt == self.max_vsm_retries: LOG.error(_LE("VSM %s, Conn failed."), vsm_ip) raise n1kv_exc.VSMConnectionFailed(reason=e) if resp.status_code != requests.codes.OK: LOG.error(_LE("VSM %(vsm)s, Got error: %(err)s"), { "vsm": vsm_ip, "err": resp.text }) raise n1kv_exc.VSMError(reason=resp.text) if 'application/json' in resp.headers['content-type']: try: return resp.json() except ValueError: return {} elif 'text/plain' in resp.headers['content-type']: LOG.info(_LI("VSM: %s"), resp.text)