Пример #1
0
    def test_connect_fail(self, req):
        """Test Connect Failure handled correctly.

        If we get a connect failure, this is transient, and we expect
        that this will end up working correctly later. We don't want
        to disable the client.

        """
        req.side_effect = ks_exc.ConnectFailure()
        self.client._get_resource_provider("fake")
        self.assertFalse(self.client._disabled)

        # reset the call count to demonstrate that future calls do
        # work
        req.reset_mock()
        self.client._get_resource_provider("fake")
        self.assertTrue(req.called)
Пример #2
0
    def test_auth_ref_fails_debug_with_native_keystone_error(
            self, mock_is_debug, mock_log_exception):
        from keystoneauth1 import exceptions as ks_exc

        mock_is_debug.return_value = True
        keystone = osclients.Keystone(self.credential, {}, {})
        session = mock.Mock()
        auth_plugin = mock.Mock()
        auth_plugin.get_access.side_effect = ks_exc.ConnectFailure("foo")
        keystone.get_session = mock.Mock(return_value=(session, auth_plugin))

        self.assertRaises(osclients.AuthenticationFailed,
                          lambda: keystone.auth_ref)

        self.assertFalse(mock_log_exception.called)
        mock_is_debug.assert_called_once_with()
        auth_plugin.get_access.assert_called_once_with(session)
class ShellTest(utils.TestCase):

    FAKE_ENV = {
        'OS_USERNAME': '******',
        'OS_PASSWORD': '******',
        'OS_TENANT_NAME': 'tenant_name',
        'OS_AUTH_URL': 'http://no.where/v2.0',
    }

    # Patch os.environ to avoid required auth info.
    def make_env(self, exclude=None, include=None):
        env = dict((k, v) for k, v in self.FAKE_ENV.items() if k != exclude)
        env.update(include or {})
        self.useFixture(fixtures.MonkeyPatch('os.environ', env))

    def setUp(self):
        super(ShellTest, self).setUp()
        for var in self.FAKE_ENV:
            self.useFixture(
                fixtures.EnvironmentVariable(var, self.FAKE_ENV[var]))

    def shell(self, argstr):
        orig = sys.stdout
        try:
            sys.stdout = moves.StringIO()
            _shell = shell.OpenStackCinderShell()
            _shell.main(argstr.split())
        except SystemExit:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            self.assertEqual(0, exc_value.code)
        finally:
            out = sys.stdout.getvalue()
            sys.stdout.close()
            sys.stdout = orig

        return out

    def test_help_unknown_command(self):
        self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')

    def test_help(self):
        required = [
            '.*?^usage: ',
            '.*?(?m)^\s+create\s+Creates a volume.',
            '.*?(?m)^Run "cinder help SUBCOMMAND" for help on a subcommand.',
        ]
        help_text = self.shell('help')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_on_subcommand(self):
        required = [
            '.*?^usage: cinder list',
            '.*?(?m)^Lists all volumes.',
        ]
        help_text = self.shell('help list')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def register_keystone_auth_fixture(self, mocker, url):
        mocker.register_uri('GET',
                            url,
                            text=keystone_client.keystone_request_callback)

    @requests_mock.Mocker()
    def test_version_discovery(self, mocker):
        _shell = shell.OpenStackCinderShell()
        sess = session.Session()

        os_auth_url = "https://wrongdiscoveryresponse.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)

        self.assertRaises(DiscoveryFailure,
                          _shell._discover_auth_versions,
                          sess,
                          auth_url=os_auth_url)

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v2_url, "Expected v2 url")
        self.assertIsNone(v3_url, "Expected no v3 url")

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v3_url, "Expected v3 url")
        self.assertIsNone(v2_url, "Expected no v2 url")

    @requests_mock.Mocker()
    def list_volumes_on_service(self, count, mocker):
        os_auth_url = "http://multiple.service.names/v2.0"
        mocker.register_uri('POST',
                            os_auth_url + "/tokens",
                            text=keystone_client.keystone_request_callback)
        mocker.register_uri('GET',
                            "http://cinder%i.api.com/v2/volumes/detail" %
                            count,
                            text='{"volumes": []}')
        self.make_env(include={
            'OS_AUTH_URL': os_auth_url,
            'CINDER_SERVICE_NAME': 'cinder%i' % count
        })
        _shell = shell.OpenStackCinderShell()
        _shell.main(['list'])

    def test_cinder_service_name(self):
        # Failing with 'No mock address' means we are not
        # choosing the correct endpoint
        for count in range(1, 4):
            self.list_volumes_on_service(count)

    @mock.patch('keystoneauth1.identity.v2.Password')
    @mock.patch('keystoneauth1.adapter.Adapter.get_token',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('keystoneauth1.discover.Discover',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('sys.stdin', side_effect=mock.Mock)
    @mock.patch('getpass.getpass', return_value='password')
    def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover,
                               mock_token, mock_password):
        self.make_env(exclude='OS_PASSWORD')
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(ks_exc.ConnectFailure, _shell.main, ['list'])
        mock_getpass.assert_called_with('OS Password: '******'password'
        # equal to mock_getpass.return_value.
        mock_password.assert_called_with(
            self.FAKE_ENV['OS_AUTH_URL'],
            password=mock_getpass.return_value,
            tenant_id='',
            tenant_name=self.FAKE_ENV['OS_TENANT_NAME'],
            username=self.FAKE_ENV['OS_USERNAME'])

    @mock.patch.object(requests, "request")
    @mock.patch.object(pkg_resources, "iter_entry_points")
    def test_auth_system_not_keystone(self, mock_iter_entry_points,
                                      mock_request):
        """Test that we can authenticate using the auth plugin system."""
        non_keystone_auth_url = "http://non-keystone-url.com/v2.0"

        class MockEntrypoint(pkg_resources.EntryPoint):
            def load(self):
                return FakePlugin

        class FakePlugin(auth_plugin.BaseAuthPlugin):
            def authenticate(self, cls, auth_url):
                cls._authenticate(auth_url, {"fake": "me"})

            def get_auth_url(self):
                return non_keystone_auth_url

        mock_iter_entry_points.side_effect = lambda _t: [
            MockEntrypoint("fake", "fake", ["FakePlugin"])
        ]

        mock_request.side_effect = mock_http_request()

        # Tell the shell we wish to use our 'fake' auth instead of keystone
        # and the auth plugin will provide the auth url
        self.make_env(exclude="OS_AUTH_URL",
                      include={'OS_AUTH_SYSTEM': 'fake'})
        # This should fail as we have not setup a mock response for 'list',
        # however auth should have been called
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(KeyError, _shell.main, ['list'])

        headers = requested_headers(_shell.cs)
        token_url = _shell.cs.client.auth_url + "/tokens"
        self.assertEqual(non_keystone_auth_url + "/tokens", token_url)

        mock_request.assert_any_call("POST",
                                     token_url,
                                     headers=headers,
                                     data='{"fake": "me"}',
                                     allow_redirects=True,
                                     **self.TEST_REQUEST_BASE)

    @mock.patch.object(cinderclient.client.HTTPClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    # Easiest way to make cinderclient use httpclient is a None session
    @mock.patch.object(cinderclient.shell.OpenStackCinderShell,
                       '_get_keystone_session',
                       return_value=None)
    def test_http_client_insecure(self, mock_authenticate, mock_session):
        self.make_env(include={'CINDERCLIENT_INSECURE': True})

        _shell = shell.OpenStackCinderShell()

        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main, ['list'])

        self.assertEqual(False, _shell.cs.client.verify_cert)

    @mock.patch.object(cinderclient.client.SessionClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    def test_session_client_debug_logger(self, mock_session):
        _shell = shell.OpenStackCinderShell()
        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main,
                          ['--debug', 'list'])
        # In case of SessionClient when --debug switch is specified
        # 'keystoneauth' logger should be initialized.
        self.assertEqual('keystoneauth', _shell.cs.client.logger.name)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert_and_key(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', '--os-key', 'mickey', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert=('minnie', 'mickey'),
                                             verify=mock.ANY)
Пример #4
0
    def _send_request(self,
                      url,
                      method,
                      redirect,
                      log,
                      logger,
                      connect_retries,
                      connect_retry_delay=0.5,
                      **kwargs):
        # NOTE(jamielennox): We handle redirection manually because the
        # requests lib follows some browser patterns where it will redirect
        # POSTs as GETs for certain statuses which is not want we want for an
        # API. See: https://en.wikipedia.org/wiki/Post/Redirect/Get

        # NOTE(jamielennox): The interaction between retries and redirects are
        # handled naively. We will attempt only a maximum number of retries and
        # redirects rather than per request limits. Otherwise the extreme case
        # could be redirects * retries requests. This will be sufficient in
        # most cases and can be fixed properly if there's ever a need.

        try:
            try:
                resp = self.session.request(method, url, **kwargs)
            except requests.exceptions.SSLError as e:
                msg = 'SSL exception connecting to %(url)s: %(error)s' % {
                    'url': url,
                    'error': e
                }
                raise exceptions.SSLError(msg)
            except requests.exceptions.Timeout:
                msg = 'Request to %s timed out' % url
                raise exceptions.ConnectTimeout(msg)
            except requests.exceptions.ConnectionError:
                msg = 'Unable to establish connection to %s' % url
                raise exceptions.ConnectFailure(msg)
            except requests.exceptions.RequestException as e:
                msg = 'Unexpected exception for %(url)s: %(error)s' % {
                    'url': url,
                    'error': e
                }
                raise exceptions.UnknownConnectionError(msg, e)

        except exceptions.RetriableConnectionFailure as e:
            if connect_retries <= 0:
                raise

            logger.info('Failure: %(e)s. Retrying in %(delay).1fs.', {
                'e': e,
                'delay': connect_retry_delay
            })
            time.sleep(connect_retry_delay)

            return self._send_request(url,
                                      method,
                                      redirect,
                                      log,
                                      logger,
                                      connect_retries=connect_retries - 1,
                                      connect_retry_delay=connect_retry_delay *
                                      2,
                                      **kwargs)

        if log:
            self._http_log_response(response=resp, logger=logger)

        if resp.status_code in self._REDIRECT_STATUSES:
            # be careful here in python True == 1 and False == 0
            if isinstance(redirect, bool):
                redirect_allowed = redirect
            else:
                redirect -= 1
                redirect_allowed = redirect >= 0

            if not redirect_allowed:
                return resp

            try:
                location = resp.headers['location']
            except KeyError:
                logger.warning(
                    "Failed to redirect request to %s as new "
                    "location was not provided.", resp.url)
            else:
                # NOTE(jamielennox): We don't pass through connect_retry_delay.
                # This request actually worked so we can reset the delay count.
                new_resp = self._send_request(location,
                                              method,
                                              redirect,
                                              log,
                                              logger,
                                              connect_retries=connect_retries,
                                              **kwargs)

                if not isinstance(new_resp.history, list):
                    new_resp.history = list(new_resp.history)
                new_resp.history.insert(0, resp)
                resp = new_resp

        return resp
Пример #5
0
class ShellTest(utils.TestCase):

    FAKE_ENV = {
        'OS_USERNAME': '******',
        'OS_PASSWORD': '******',
        'OS_PROJECT_NAME': 'tenant_name',
        'OS_AUTH_URL': 'http://no.where/v2.0',
    }

    # Patch os.environ to avoid required auth info.
    def make_env(self, exclude=None, include=None):
        env = dict((k, v) for k, v in self.FAKE_ENV.items() if k != exclude)
        env.update(include or {})
        self.useFixture(fixtures.MonkeyPatch('os.environ', env))

    def setUp(self):
        super(ShellTest, self).setUp()
        for var in self.FAKE_ENV:
            self.useFixture(
                fixtures.EnvironmentVariable(var, self.FAKE_ENV[var]))

        self.mock_completion()

    def shell(self, argstr):
        orig = sys.stdout
        try:
            sys.stdout = moves.StringIO()
            _shell = shell.OpenStackCinderShell()
            _shell.main(argstr.split())
        except SystemExit:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            self.assertEqual(0, exc_value.code)
        finally:
            out = sys.stdout.getvalue()
            sys.stdout.close()
            sys.stdout = orig

        return out

    def test_default_auth_env(self):
        _shell = shell.OpenStackCinderShell()
        args, __ = _shell.get_base_parser().parse_known_args([])
        self.assertEqual('', args.os_auth_type)

    def test_auth_type_env(self):
        self.make_env(exclude='OS_PASSWORD',
                      include={
                          'OS_AUTH_SYSTEM': 'non existent auth',
                          'OS_AUTH_TYPE': 'noauth'
                      })
        _shell = shell.OpenStackCinderShell()
        args, __ = _shell.get_base_parser().parse_known_args([])
        self.assertEqual('noauth', args.os_auth_type)

    def test_auth_system_env(self):
        self.make_env(exclude='OS_PASSWORD',
                      include={'OS_AUTH_SYSTEM': 'noauth'})
        _shell = shell.OpenStackCinderShell()
        args, __ = _shell.get_base_parser().parse_known_args([])
        self.assertEqual('noauth', args.os_auth_type)

    @mock.patch.object(cinderclient.shell.OpenStackCinderShell,
                       '_get_keystone_session')
    @mock.patch.object(cinderclient.client.SessionClient,
                       'authenticate',
                       side_effect=RuntimeError())
    def test_password_auth_type(self, mock_authenticate, mock_get_session):
        self.make_env(include={'OS_AUTH_TYPE': 'password'})
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Client instantiation because this test
        # focuses only keystoneauth1 indentity cli opts parsing.
        self.assertRaises(RuntimeError, _shell.main, ['list'])
        self.assertIsInstance(_shell.cs.client.session.auth, ks_password)

    def test_help_unknown_command(self):
        self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')

    def test_help(self):
        # Some expected help output, including microversioned commands
        required = [
            '.*?^usage: ',
            '.*?(?m)^\s+create\s+Creates a volume.',
            '.*?(?m)^\s+summary\s+Get volumes summary.',
            '.*?(?m)^Run "cinder help SUBCOMMAND" for help on a subcommand.',
        ]
        help_text = self.shell('help')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_on_subcommand(self):
        required = [
            '.*?^usage: cinder list',
            '.*?(?m)^Lists all volumes.',
        ]
        help_text = self.shell('help list')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_on_subcommand_mv(self):
        required = [
            '.*?^usage: cinder summary',
            '.*?(?m)^Get volumes summary.',
        ]
        help_text = self.shell('help summary')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    @ddt.data('backup-create --help', '--help backup-create')
    def test_dash_dash_help_on_subcommand(self, cmd):
        required = ['.*?^Creates a volume backup.']
        help_text = self.shell(cmd)

        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def register_keystone_auth_fixture(self, mocker, url):
        mocker.register_uri('GET',
                            url,
                            text=keystone_client.keystone_request_callback)

    @requests_mock.Mocker()
    def test_version_discovery(self, mocker):
        _shell = shell.OpenStackCinderShell()
        sess = session.Session()

        os_auth_url = "https://wrongdiscoveryresponse.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)

        self.assertRaises(DiscoveryFailure,
                          _shell._discover_auth_versions,
                          sess,
                          auth_url=os_auth_url)

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v2_url, "Expected v2 url")
        self.assertIsNone(v3_url, "Expected no v3 url")

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v3_url, "Expected v3 url")
        self.assertIsNone(v2_url, "Expected no v2 url")

    @requests_mock.Mocker()
    def list_volumes_on_service(self, count, mocker):
        os_auth_url = "http://multiple.service.names/v2.0"
        mocker.register_uri('POST',
                            os_auth_url + "/tokens",
                            text=keystone_client.keystone_request_callback)
        mocker.register_uri('GET',
                            "http://cinder%i.api.com/v2/volumes/detail" %
                            count,
                            text='{"volumes": []}')
        self.make_env(include={
            'OS_AUTH_URL': os_auth_url,
            'CINDER_SERVICE_NAME': 'cinder%i' % count
        })
        _shell = shell.OpenStackCinderShell()
        _shell.main(['list'])

    def test_duplicate_filters(self):
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(exceptions.CommandError, _shell.main,
                          ['list', '--name', 'abc', '--filters', 'name=xyz'])

    @unittest.skip("Skip cuz I broke it")
    def test_cinder_service_name(self):
        # Failing with 'No mock address' means we are not
        # choosing the correct endpoint
        for count in range(1, 4):
            self.list_volumes_on_service(count)

    @mock.patch('keystoneauth1.identity.v2.Password')
    @mock.patch('keystoneauth1.adapter.Adapter.get_token',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('keystoneauth1.discover.Discover',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('sys.stdin', side_effect=mock.Mock)
    @mock.patch('getpass.getpass', return_value='password')
    def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover,
                               mock_token, mock_password):
        self.make_env(exclude='OS_PASSWORD')
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(ks_exc.ConnectFailure, _shell.main, ['list'])
        mock_getpass.assert_called_with('OS Password: '******'password'
        # equal to mock_getpass.return_value.
        mock_password.assert_called_with(
            self.FAKE_ENV['OS_AUTH_URL'],
            password=mock_getpass.return_value,
            tenant_id='',
            tenant_name=self.FAKE_ENV['OS_PROJECT_NAME'],
            username=self.FAKE_ENV['OS_USERNAME'])

    @requests_mock.Mocker()
    def test_noauth_plugin(self, mocker):
        os_auth_url = "http://example.com/v2"
        mocker.register_uri('GET',
                            "%s/admin/volumes/detail" % os_auth_url,
                            text='{"volumes": []}')
        _shell = shell.OpenStackCinderShell()
        args = [
            '--os-endpoint', os_auth_url, '--os-auth-type', 'noauth',
            '--os-user-id', 'admin', '--os-project-id', 'admin', 'list'
        ]
        _shell.main(args)
        self.assertIsInstance(_shell.cs.client.session.auth,
                              noauth.CinderNoAuthPlugin)

    @mock.patch.object(cinderclient.client.HTTPClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    # Easiest way to make cinderclient use httpclient is a None session
    @mock.patch.object(cinderclient.shell.OpenStackCinderShell,
                       '_get_keystone_session',
                       return_value=None)
    def test_http_client_insecure(self, mock_authenticate, mock_session):
        self.make_env(include={'CINDERCLIENT_INSECURE': True})

        _shell = shell.OpenStackCinderShell()

        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main, ['list'])

        self.assertEqual(False, _shell.cs.client.verify_cert)

    @mock.patch.object(cinderclient.client.SessionClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    def test_session_client_debug_logger(self, mock_session):
        _shell = shell.OpenStackCinderShell()
        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main,
                          ['--debug', 'list'])
        # In case of SessionClient when --debug switch is specified
        # 'keystoneauth' logger should be initialized.
        self.assertEqual('keystoneauth', _shell.cs.client.logger.name)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert_and_key(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', '--os-key', 'mickey', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert=('minnie', 'mickey'),
                                             verify=mock.ANY)