def test_terminate_connection(self, mock_host): vol_name = VOLUME["name"] + "-cinder" mock_host.return_value = {"name": "some-host"} # Branch with manually created host self.driver.terminate_connection(VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with("some-host", vol_name) self.assertFalse(self.array.list_host_connections.called) self.assertFalse(self.array.delete_host.called) # Branch with host added to host group self.array.reset_mock() self.array.list_host_connections.return_value = [] mock_host.return_value = PURE_HOST.copy() mock_host.return_value.update(hgroup="some-group") self.driver.terminate_connection(VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with(PURE_HOST_NAME, vol_name) self.assertTrue(self.array.list_host_connections.called) self.assertTrue(self.array.delete_host.called) # Branch with host still having connected volumes self.array.reset_mock() self.array.list_host_connections.return_value = [{ "lun": 2, "name": PURE_HOST_NAME, "vol": "some-vol" }] mock_host.return_value = PURE_HOST self.driver.terminate_connection(VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with(PURE_HOST_NAME, vol_name) self.array.list_host_connections.assert_called_with(PURE_HOST_NAME, private=True) self.assertFalse(self.array.delete_host.called) # Branch where host gets deleted self.array.reset_mock() self.array.list_host_connections.return_value = [] self.driver.terminate_connection(VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with(PURE_HOST_NAME, vol_name) self.array.list_host_connections.assert_called_with(PURE_HOST_NAME, private=True) self.array.delete_host.assert_called_with(PURE_HOST_NAME) # Branch where connection is missing and the host is still deleted self.array.reset_mock() self.array.disconnect_host.side_effect = exception.PureAPIException( code=400, reason="reason") self.driver.terminate_connection(VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with(PURE_HOST_NAME, vol_name) self.array.list_host_connections.assert_called_with(PURE_HOST_NAME, private=True) self.array.delete_host.assert_called_with(PURE_HOST_NAME) # Branch where an unexpected exception occurs self.array.reset_mock() self.array.disconnect_host.side_effect = exception.PureAPIException( code=500, reason="unexpected exception") self.assertRaises(exception.PureAPIException, self.driver.terminate_connection, VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with(PURE_HOST_NAME, vol_name) self.assertFalse(self.array.list_host_connections.called) self.assertFalse(self.array.delete_host.called)
def test_delete_volume_already_deleted(self): self.array.list_volume_hosts.side_effect = exception.PureAPIException( code=400, reason="Volume does not exist") self.driver.delete_volume(VOLUME) self.assertFalse(self.array.destroy_volume.called) self.array.list_volume_hosts.side_effect = None self.assert_error_propagates([self.array.destroy_volume], self.driver.delete_volume, VOLUME) # Testing case where array.destroy_volume returns an exception # because volume already deleted self.array.destroy_volume.side_effect = exception.PureAPIException( code=400, reason="Volume does not exist") self.driver.delete_volume(VOLUME) self.assertTrue(self.array.destroy_volume.called) self.array.destroy_volume.side_effect = None self.assert_error_propagates([self.array.destroy_volume], self.driver.delete_volume, VOLUME)
def _http_request(self, method, path, data=None, reestablish_session=True): """Perform HTTP request for REST API.""" req = urllib2.Request(self._root_url + path, headers={"Content-Type": "application/json"}) req.get_method = lambda: method body = json.dumps(data) try: # Raises urllib2.HTTPError if response code != 200 response = self._opener.open(req, body) except urllib2.HTTPError as err: if (reestablish_session and err.code == 401): self._start_session() return self._http_request(method, path, data, reestablish_session=False) elif err.code == 450: # Purity REST API version is bad new_version = self._choose_rest_version() if new_version == self._rest_version: raise exception.PureAPIException( code=err.code, reason=(_LE("Unable to find usable REST API version. " "Response from Pure Storage REST API: ") + err.read())) self._rest_version = new_version self._root_url = "https://%s/api/%s/" % (self._target, self._rest_version) return self._http_request(method, path, data) else: raise exception.PureAPIException(code=err.code, reason=err.read()) except urllib2.URLError as err: # Error outside scope of HTTP status codes, # e.g., unable to resolve domain name raise exception.PureDriverException( reason=_LE("Unable to connect to %r. Check san_ip.") % self._target) else: content = response.read() if "application/json" in response.info().get('Content-Type'): return json.loads(content) raise exception.PureAPIException( reason=(_LE("Response not in JSON: ") + content))
def test_delete_snapshot(self): snap_name = SNAPSHOT["volume_name"] + "-cinder." + SNAPSHOT["name"] self.driver.delete_snapshot(SNAPSHOT) expected = [mock.call.destroy_volume(snap_name)] self.array.assert_has_calls(expected) self.array.destroy_volume.side_effect = exception.PureAPIException( code=400, reason="reason") self.driver.delete_snapshot(SNAPSHOT) self.array.destroy_volume.side_effect = None self.assert_error_propagates([self.array.destroy_volume], self.driver.delete_snapshot, SNAPSHOT)
def assert_error_propagates(self, mocks, func, *args, **kwargs): """Assert that errors from mocks propogate to func. Fail if exceptions raised by mocks are not seen when calling func(*args, **kwargs). Ensure that we are really seeing exceptions from the mocks by failing if just running func(*args, **kargs) raises an exception itself. """ func(*args, **kwargs) for mock_func in mocks: mock_func.side_effect = exception.PureAPIException(reason="reason") self.assertRaises(exception.PureAPIException, func, *args, **kwargs) mock_func.side_effect = None
def test_terminate_connection(self, mock_host): vol_name = VOLUME["name"] + "-cinder" mock_host.return_value = HOST_NAME self.driver.terminate_connection(VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with(HOST_NAME, vol_name) self.array.disconnect_host.side_effect = exception.PureAPIException( code=400, reason="reason") self.driver.terminate_connection(VOLUME, CONNECTOR) self.array.disconnect_host.assert_called_with(HOST_NAME, vol_name) self.array.disconnect_host.side_effect = None self.array.disconnect_host.reset_mock() mock_host.side_effect = exception.PureDriverException(reason="reason") self.assertFalse(self.array.disconnect_host.called) mock_host.side_effect = None self.assert_error_propagates([self.array.disconnect_host], self.driver.terminate_connection, VOLUME, CONNECTOR)
def test_http_request_450_error(self, mock_choose): mock_choose.return_value = "1.1" error = urllib2.HTTPError(self.full_path, 450, self.error_msg, None, self.response) self.array._opener.open.side_effect = iter([error, self.response]) real_result = self.array._http_request(self.method, self.path, self.data) self.assertEqual(self.result, real_result) expected = [ self.make_call(), self.make_call( path=self.path_template.format(TARGET, "1.1", self.path)) ] self.assertEqual(self.array._opener.open.call_args_list, expected) mock_choose.assert_called_with(self.array) self.array._opener.open.side_effect = error self.assertRaises(exception.PureAPIException, self.array._http_request, self.method, self.path, self.data) self.array._opener.open.reset_mock() mock_choose.reset_mock() self.array._opener.open.side_effect = error mock_choose.side_effect = exception.PureAPIException(reason="reason") self.assertRaises(exception.PureAPIException, self.array._http_request, self.method, self.path, self.data)