Esempio n. 1
0
 def _check_version(self, version, headers=None):
     if headers is None:
         headers = {}
     # ensure that major version in the URL matches the header
     if version.major != BASE_VERSION:
         raise exc.HTTPNotAcceptable(
             _("Mutually exclusive versions requested. Version %(ver)s "
               "requested but not supported by this service. The supported "
               "version range is: [%(min)s, %(max)s].") % {
                   'ver': version,
                   'min': versions.min_version_string(),
                   'max': versions.max_version_string()
               },
             headers=headers)
     # ensure the minor version is within the supported range
     if version < min_version() or version > max_version():
         raise exc.HTTPNotAcceptable(
             _("Version %(ver)s was requested but the minor version is not "
               "supported by this service. The supported version range is: "
               "[%(min)s, %(max)s].") % {
                   'ver': version,
                   'min': versions.min_version_string(),
                   'max': versions.max_version_string()
               },
             headers=headers)
Esempio n. 2
0
    def _add_version_attributes(self):
        v = base.Version(api.request.headers, versions.min_version_string(),
                         versions.max_version_string())

        # Always set the min and max headers
        api.response.headers[base.Version.min_string] = (
            versions.min_version_string())
        api.response.headers[base.Version.max_string] = (
            versions.max_version_string())

        # assert that requested version is supported
        self._check_version(v, api.response.headers)
        api.response.headers[base.Version.string] = str(v)
        api.request.version = v
Esempio n. 3
0
    def _test_get_root(self, headers=None, additional_expected_resources=None):
        if headers is None:
            headers = {}
        if additional_expected_resources is None:
            additional_expected_resources = []
        data = self.get_json('/', headers=headers)
        self.assertEqual('v1', data['id'])
        # Check fields are not empty
        for f in data:
            self.assertNotIn(f, ['', []])
        # Check if all known resources are present and there are no extra ones.
        not_resources = ('id', 'links', 'media_types', 'version')
        actual_resources = tuple(set(data) - set(not_resources))
        expected_resources = (['chassis', 'drivers', 'nodes', 'ports']
                              + additional_expected_resources)
        self.assertEqual(sorted(expected_resources), sorted(actual_resources))
        self.assertIn({'type': 'application/vnd.openstack.ironic.v1+json',
                       'base': 'application/json'}, data['media_types'])

        version1 = data['version']
        self.assertEqual('v1', version1['id'])
        self.assertEqual('CURRENT', version1['status'])
        self.assertEqual(versions.min_version_string(),
                         version1['min_version'])
        self.assertEqual(versions.max_version_string(), version1['version'])
Esempio n. 4
0
    def _test_drivers(self, use_dynamic, detail=False, latest_if=False):
        self.register_fake_conductors()
        headers = {}
        expected = [
            {
                'name': self.hw1,
                'hosts': [self.h1, self.h2],
                'type': 'dynamic'
            },
            {
                'name': self.hw2,
                'hosts': [self.h1],
                'type': 'dynamic'
            },
        ]
        expected = sorted(expected, key=lambda d: d['name'])
        if use_dynamic:
            if latest_if:
                headers[api_base.Version.string] = \
                    api_versions.max_version_string()
            else:
                headers[api_base.Version.string] = '1.30'

        path = '/drivers'
        if detail:
            path += '?detail=True'
        data = self.get_json(path, headers=headers)

        self.assertEqual(len(expected), len(data['drivers']))
        drivers = sorted(data['drivers'], key=lambda d: d['name'])
        for i in range(len(expected)):
            d = drivers[i]
            e = expected[i]

            self.assertEqual(e['name'], d['name'])
            self.assertEqual(sorted(e['hosts']), sorted(d['hosts']))
            self.validate_link(d['links'][0]['href'])
            self.validate_link(d['links'][1]['href'])

            if use_dynamic:
                self.assertEqual(e['type'], d['type'])

            # NOTE(jroll) we don't test detail=True with use_dynamic=False
            # as this case can't actually happen.
            if detail:
                self.assertIn('default_deploy_interface', d)
                if latest_if:
                    self.assertIn('default_rescue_interface', d)
                    self.assertIn('enabled_rescue_interfaces', d)
                    self.assertIn('default_storage_interface', d)
                    self.assertIn('enabled_storage_interfaces', d)
                else:
                    self.assertNotIn('default_rescue_interface', d)
                    self.assertNotIn('enabled_rescue_interfaces', d)
                    self.assertNotIn('default_storage_interface', d)
                    self.assertNotIn('enabled_storage_interfaces', d)
            else:
                # ensure we don't spill these fields into driver listing
                # one should be enough
                self.assertNotIn('default_deploy_interface', d)
Esempio n. 5
0
def default_version():
    """Return a dict representing the current default version

    id: The ID of the (major) version, also acts as the release number
    links: A list containing one link that points to the current version
    of the API

    status: Status of the version, one of CURRENT, SUPPORTED, DEPRECATED

    min_version: The current, maximum supported (major.minor) version of API.

    version: Minimum supported (major.minor) version of API.
    """

    # NOTE(dtantsur): avoid circular imports
    from ironic.api.controllers.v1 import versions

    return {
        'id':
        ID_VERSION1,
        'links': [
            link.make_link('self',
                           api.request.public_url,
                           ID_VERSION1,
                           '',
                           bookmark=True)
        ],
        'status':
        'CURRENT',
        'min_version':
        versions.min_version_string(),
        'version':
        versions.max_version_string()
    }
Esempio n. 6
0
    def _test_get_root(self, headers=None, additional_expected_resources=None):
        if headers is None:
            headers = {}
        if additional_expected_resources is None:
            additional_expected_resources = []
        data = self.get_json('/', headers=headers)
        self.assertEqual('v1', data['id'])
        # Check fields are not empty
        for f in data:
            self.assertNotIn(f, ['', []])
        # Check if all known resources are present and there are no extra ones.
        not_resources = ('id', 'links', 'media_types', 'version')
        actual_resources = tuple(set(data) - set(not_resources))
        expected_resources = (['chassis', 'drivers', 'nodes', 'ports'] +
                              additional_expected_resources)
        self.assertEqual(sorted(expected_resources), sorted(actual_resources))
        self.assertEqual(
            {
                'type': 'application/vnd.openstack.ironic.v1+json',
                'base': 'application/json'
            }, data['media_types'])

        version1 = data['version']
        self.assertEqual('v1', version1['id'])
        self.assertEqual('CURRENT', version1['status'])
        self.assertEqual(versions.min_version_string(),
                         version1['min_version'])
        self.assertEqual(versions.max_version_string(), version1['version'])
Esempio n. 7
0
    def _route(self, args, request=None):
        v = base.Version(pecan.request.headers, versions.min_version_string(),
                         versions.max_version_string())

        # Always set the min and max headers
        pecan.response.headers[base.Version.min_string] = (
            versions.min_version_string())
        pecan.response.headers[base.Version.max_string] = (
            versions.max_version_string())

        # assert that requested version is supported
        self._check_version(v, pecan.response.headers)
        pecan.response.headers[base.Version.string] = str(v)
        pecan.request.version = v

        return super(Controller, self)._route(args, request)
Esempio n. 8
0
 def convert():
     root = Root()
     root.name = "OpenStack Ironic API"
     root.description = ("Ironic is an OpenStack project which aims to "
                         "provision baremetal machines.")
     root.default_version = Version(ID_VERSION1,
                                    versions.min_version_string(),
                                    versions.max_version_string())
     root.versions = [root.default_version]
     return root
Esempio n. 9
0
 def test_max_version_pinned(self, mock_release_mapping):
     CONF.set_override('pin_release_version',
                       release_mappings.RELEASE_VERSIONS[-1])
     mock_release_mapping.get.return_value = {
         'api': '1.5',
         'rpc': '1.4',
         'objects': {
             'MyObj': ['1.4'],
         }
     }
     self.assertEqual('1.5', versions.max_version_string())
Esempio n. 10
0
 def test_max_version_pinned(self, mock_release_mapping):
     CONF.set_override('pin_release_version',
                       release_mappings.RELEASE_VERSIONS[-1])
     mock_release_mapping.get.return_value = {
         'api': '1.5',
         'rpc': '1.4',
         'objects': {
             'MyObj': ['1.4'],
         }
     }
     self.assertEqual('1.5', versions.max_version_string())
Esempio n. 11
0
 def _check_version(self, version, headers=None):
     if headers is None:
         headers = {}
     # ensure that major version in the URL matches the header
     if version.major != BASE_VERSION:
         raise exc.HTTPNotAcceptable(_(
             "Mutually exclusive versions requested. Version %(ver)s "
             "requested but not supported by this service. The supported "
             "version range is: [%(min)s, %(max)s].") %
             {'ver': version, 'min': versions.min_version_string(),
              'max': versions.max_version_string()},
             headers=headers)
     # ensure the minor version is within the supported range
     if version < min_version() or version > max_version():
         raise exc.HTTPNotAcceptable(_(
             "Version %(ver)s was requested but the minor version is not "
             "supported by this service. The supported version range is: "
             "[%(min)s, %(max)s].") %
             {'ver': version, 'min': versions.min_version_string(),
              'max': versions.max_version_string()},
             headers=headers)
Esempio n. 12
0
    def _test_drivers(self, use_dynamic, detail=False, latest_if=False):
        self.register_fake_conductors()
        headers = {}
        expected = [
            {'name': self.hw1, 'hosts': [self.h1, self.h2], 'type': 'dynamic'},
            {'name': self.hw2, 'hosts': [self.h1], 'type': 'dynamic'},
        ]
        expected = sorted(expected, key=lambda d: d['name'])
        if use_dynamic:
            if latest_if:
                headers[api_base.Version.string] = \
                    api_versions.max_version_string()
            else:
                headers[api_base.Version.string] = '1.30'

        path = '/drivers'
        if detail:
            path += '?detail=True'
        data = self.get_json(path, headers=headers)

        self.assertEqual(len(expected), len(data['drivers']))
        drivers = sorted(data['drivers'], key=lambda d: d['name'])
        for i in range(len(expected)):
            d = drivers[i]
            e = expected[i]

            self.assertEqual(e['name'], d['name'])
            self.assertEqual(sorted(e['hosts']), sorted(d['hosts']))
            self.validate_link(d['links'][0]['href'])
            self.validate_link(d['links'][1]['href'])

            if use_dynamic:
                self.assertEqual(e['type'], d['type'])

            # NOTE(jroll) we don't test detail=True with use_dynamic=False
            # as this case can't actually happen.
            if detail:
                self.assertIn('default_deploy_interface', d)
                if latest_if:
                    self.assertIn('default_rescue_interface', d)
                    self.assertIn('enabled_rescue_interfaces', d)
                    self.assertIn('default_storage_interface', d)
                    self.assertIn('enabled_storage_interfaces', d)
                else:
                    self.assertNotIn('default_rescue_interface', d)
                    self.assertNotIn('enabled_rescue_interfaces', d)
                    self.assertNotIn('default_storage_interface', d)
                    self.assertNotIn('enabled_storage_interfaces', d)
            else:
                # ensure we don't spill these fields into driver listing
                # one should be enough
                self.assertNotIn('default_deploy_interface', d)
Esempio n. 13
0
    def test_get_root(self):
        response = self.get_json('/', path_prefix='')
        # Check fields are not empty
        [self.assertNotIn(f, ['', []]) for f in response]

        self.assertEqual('OpenStack Ironic API', response['name'])
        self.assertTrue(response['description'])
        self.assertEqual([response['default_version']], response['versions'])

        version1 = response['default_version']
        self.assertEqual('v1', version1['id'])
        self.assertEqual('CURRENT', version1['status'])
        self.assertEqual(versions.min_version_string(),
                         version1['min_version'])
        self.assertEqual(versions.max_version_string(), version1['version'])
Esempio n. 14
0
    def test_get_root(self):
        response = self.get_json('/', path_prefix='')
        # Check fields are not empty
        [self.assertNotIn(f, ['', []]) for f in response]

        self.assertEqual('OpenStack Ironic API', response['name'])
        self.assertTrue(response['description'])
        self.assertEqual([response['default_version']], response['versions'])

        version1 = response['default_version']
        self.assertEqual('v1', version1['id'])
        self.assertEqual('CURRENT', version1['status'])
        self.assertEqual(versions.min_version_string(),
                         version1['min_version'])
        self.assertEqual(versions.max_version_string(), version1['version'])
Esempio n. 15
0
    def setUp(self):
        super(TestPostRBAC, self).setUp()

        cfg.CONF.set_override('enforce_scope', True, group='oslo_policy')
        cfg.CONF.set_override('enforce_new_defaults',
                              True,
                              group='oslo_policy')
        cfg.CONF.set_override('auth_strategy', 'keystone')
        # Headers required for this to pass in system scope restricted
        # authentication, as our default for api tests is noauth.
        self.headers = {
            api_base.Version.string: str(versions.max_version_string()),
            'X-Auth-Token': 'test-auth-token',
            'X-Roles': 'admin',
            'OpenStack-System-Scope': 'all',
        }
Esempio n. 16
0
    def _test_drivers_get_one_ok(self,
                                 use_dynamic,
                                 mock_driver_properties,
                                 latest_if=False):
        # get_driver_properties mock is required by validate_link()
        self.register_fake_conductors()

        if use_dynamic:
            driver = self.d3
            driver_type = 'dynamic'
            hosts = [self.h1, self.h2]
        else:
            driver = self.d1
            driver_type = 'classic'
            hosts = [self.h1]

        headers = {}
        if latest_if:
            headers[api_base.Version.string] = \
                api_versions.max_version_string()
        else:
            headers[api_base.Version.string] = '1.30'

        data = self.get_json('/drivers/%s' % driver, headers=headers)

        self.assertEqual(driver, data['name'])
        self.assertEqual(sorted(hosts), sorted(data['hosts']))
        self.assertIn('properties', data)
        self.assertEqual(driver_type, data['type'])

        if use_dynamic:
            for iface in driver_base.ALL_INTERFACES:
                if iface != 'bios':
                    if latest_if or iface not in ['rescue', 'storage']:
                        self.assertIn('default_%s_interface' % iface, data)
                        self.assertIn('enabled_%s_interfaces' % iface, data)

            self.assertIsNotNone(data['default_deploy_interface'])
            self.assertIsNotNone(data['enabled_deploy_interfaces'])
        else:
            self.assertIsNone(data['default_deploy_interface'])
            self.assertIsNone(data['enabled_deploy_interfaces'])

        self.validate_link(data['links'][0]['href'])
        self.validate_link(data['links'][1]['href'])
        self.validate_link(data['properties'][0]['href'])
        self.validate_link(data['properties'][1]['href'])
Esempio n. 17
0
    def _test_drivers_get_one_ok(self, mock_driver_properties,
                                 latest_if=False):
        # get_driver_properties mock is required by validate_link()
        self.register_fake_conductors()

        driver = self.hw1
        driver_type = 'dynamic'
        hosts = [self.h1, self.h2]

        headers = {}
        if latest_if:
            headers[api_base.Version.string] = \
                api_versions.max_version_string()
        else:
            headers[api_base.Version.string] = '1.30'

        data = self.get_json('/drivers/%s' % driver,
                             headers=headers)

        self.assertEqual(driver, data['name'])
        self.assertEqual(sorted(hosts), sorted(data['hosts']))
        self.assertIn('properties', data)
        self.assertEqual(driver_type, data['type'])

        for iface in driver_base.ALL_INTERFACES:
            if iface != 'bios':
                if latest_if or iface not in ['rescue', 'storage']:
                    self.assertIn('default_%s_interface' % iface, data)
                    self.assertIn('enabled_%s_interfaces' % iface, data)

        self.assertIsNotNone(data['default_deploy_interface'])
        self.assertIsNotNone(data['enabled_deploy_interfaces'])

        self.validate_link(data['links'][0]['href'])
        self.validate_link(data['links'][1]['href'])
        self.validate_link(data['properties'][0]['href'])
        self.validate_link(data['properties'][1]['href'])
Esempio n. 18
0
    def _test_request(self, path, params=None, headers=None, method='get',
                      body=None, assert_status=None,
                      assert_dict_contains=None,
                      assert_list_length=None,
                      deprecated=None):
        path = path.format(**self.format_data)
        self.mock_auth.side_effect = self._fake_process_request

        # always request the latest api version
        version = api_versions.max_version_string()
        rheaders = {
            'X-OpenStack-Ironic-API-Version': version
        }
        # NOTE(TheJulia): Logging the test request to aid
        # in troubleshooting ACL testing. This is a pattern
        # followed in API unit testing in ironic, and
        # really does help.
        print('API ACL Testing Path %s %s' % (method, path))
        if headers:
            for k, v in headers.items():
                rheaders[k] = v.format(**self.format_data)
        if method == 'get':
            response = self.get_json(
                path,
                headers=rheaders,
                expect_errors=True,
                extra_environ=self.environ,
                path_prefix=''
            )
        elif method == 'put':
            response = self.put_json(
                path,
                headers=rheaders,
                expect_errors=True,
                extra_environ=self.environ,
                path_prefix='',
                params=body
            )
        elif method == 'post':
            response = self.post_json(
                path,
                headers=rheaders,
                expect_errors=True,
                extra_environ=self.environ,
                path_prefix='',
                params=body
            )
        elif method == 'patch':
            response = self.patch_json(
                path,
                params=body,
                headers=rheaders,
                expect_errors=True,
                extra_environ=self.environ,
                path_prefix=''
            )
        elif method == 'delete':
            response = self.delete(
                path,
                headers=rheaders,
                expect_errors=True,
                extra_environ=self.environ,
                path_prefix=''
            )
        else:
            assert False, 'Unimplemented test method: %s' % method
        # Once miggrated:
        # Items will return:
        # 403 - Trying to access something that is generally denied.
        #       Example: PATCH /v1/nodes/<uuid> as a reader.
        # 404 - Trying to access something where we don't have permissions
        #       in a project scope. This is particularly true where implied
        #       permissions or assocation exists. Ports are attempted to be
        #       accessed when the underlying node is inaccessible as owner
        #       nor node matches.
        #       Example: GET /v1/portgroups or /v1/nodes/<uuid>/ports
        # 500 - Attempting to access something such an system scoped endpoint
        #       with a project scoped request. Example: /v1/conductors.
        if not (bool(deprecated)
                and ('404' in response.status
                     or '500' in response.status
                     or '403' in response.status)
                and cfg.CONF.oslo_policy.enforce_scope
                and cfg.CONF.oslo_policy.enforce_new_defaults):
            self.assertEqual(assert_status, response.status_int)
        else:
            self.assertTrue(
                ('404' in response.status
                 or '500' in response.status
                 or '403' in response.status))
            # We can't check the contents of the response if there is no
            # response.
            return
        if not bool(deprecated):
            self.assertIsNotNone(assert_status,
                                 'Tests must include an assert_status')

        if assert_dict_contains:
            for k, v in assert_dict_contains.items():
                self.assertIn(k, response)
                print(k)
                print(v)
                if str(v) == "None":
                    # Compare since the variable loaded from the
                    # json ends up being null in json or None.
                    self.assertIsNone(response.json[k])
                elif str(v) == "{}":
                    # Special match for signifying a dictonary.
                    self.assertEqual({}, response.json[k])
                elif isinstance(v, dict):
                    # The value from the YAML can be a dictionary,
                    # which cannot be formatted, so we're likely doing
                    # direct matching.
                    self.assertEqual(str(v), str(response.json[k]))
                else:
                    self.assertEqual(v.format(**self.format_data),
                                     response.json[k])

        if assert_list_length:
            for root, length in assert_list_length.items():
                # root - object to look inside
                # length - number of expected elements which will be
                #          important for owner/lessee testing.
                items = response.json[root]
                self.assertIsInstance(items, list)
                if not (bool(deprecated)
                        and cfg.CONF.oslo_policy.enforce_scope):
                    self.assertEqual(length, len(items))
                else:
                    # If we have scope enforcement, we likely have different
                    # views, such as "other" admins being subjected to
                    # a filtered view in these cases.
                    self.assertEqual(0, len(items))

        # NOTE(TheJulia): API tests in Ironic tend to have a pattern
        # to print request and response data to aid in development
        # and troubleshooting. As such the prints should remain,
        # at least until we are through primary development of the
        # this test suite.
        print('ACL Test GOT %s' % response)
Esempio n. 19
0
 def test_max_version(self):
     response = self.get_json('/',
                              headers={
                                  'Accept':
                                  'application/json',
                                  'X-OpenStack-Ironic-API-Version':
                                  versions.max_version_string()
                              })
     self.assertEqual(
         {
             'id':
             'v1',
             'links': [{
                 'href': 'http://localhost/v1/',
                 'rel': 'self'
             }, {
                 'href': 'https://docs.openstack.org//ironic/latest'
                 '/contributor//webapi.html',
                 'rel': 'describedby',
                 'type': 'text/html'
             }],
             'media_types': {
                 'base': 'application/json',
                 'type': 'application/vnd.openstack.ironic.v1+json'
             },
             'version': {
                 'id': 'v1',
                 'links': [{
                     'href': 'http://localhost/v1/',
                     'rel': 'self'
                 }],
                 'status': 'CURRENT',
                 'min_version': versions.min_version_string(),
                 'version': versions.max_version_string()
             },
             'allocations': [{
                 'href': 'http://localhost/v1/allocations/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/allocations/',
                 'rel': 'bookmark'
             }],
             'chassis': [{
                 'href': 'http://localhost/v1/chassis/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/chassis/',
                 'rel': 'bookmark'
             }],
             'conductors': [{
                 'href': 'http://localhost/v1/conductors/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/conductors/',
                 'rel': 'bookmark'
             }],
             'deploy_templates': [{
                 'href': 'http://localhost/v1/deploy_templates/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/deploy_templates/',
                 'rel': 'bookmark'
             }],
             'drivers': [{
                 'href': 'http://localhost/v1/drivers/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/drivers/',
                 'rel': 'bookmark'
             }],
             'events': [{
                 'href': 'http://localhost/v1/events/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/events/',
                 'rel': 'bookmark'
             }],
             'heartbeat': [{
                 'href': 'http://localhost/v1/heartbeat/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/heartbeat/',
                 'rel': 'bookmark'
             }],
             'lookup': [{
                 'href': 'http://localhost/v1/lookup/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/lookup/',
                 'rel': 'bookmark'
             }],
             'nodes': [{
                 'href': 'http://localhost/v1/nodes/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/nodes/',
                 'rel': 'bookmark'
             }],
             'portgroups': [{
                 'href': 'http://localhost/v1/portgroups/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/portgroups/',
                 'rel': 'bookmark'
             }],
             'ports': [{
                 'href': 'http://localhost/v1/ports/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/ports/',
                 'rel': 'bookmark'
             }],
             'volume': [{
                 'href': 'http://localhost/v1/volume/',
                 'rel': 'self'
             }, {
                 'href': 'http://localhost/volume/',
                 'rel': 'bookmark'
             }]
         }, response)
Esempio n. 20
0
def default_version():
    # NOTE(dtantsur): avoid circular imports
    from ironic.api.controllers.v1 import versions

    return Version(ID_VERSION1, versions.min_version_string(),
                   versions.max_version_string())
Esempio n. 21
0
 def test_max_version_not_pinned_in_release_mappings(self):
     CONF.set_override('pin_release_version', None)
     self.assertEqual(release_mappings.RELEASE_MAPPING['master']['api'],
                      versions.max_version_string())
Esempio n. 22
0
 def test_max_version_not_pinned(self):
     CONF.set_override('pin_release_version', None)
     self.assertEqual(versions._MAX_VERSION_STRING,
                      versions.max_version_string())
Esempio n. 23
0
def max_version():
    return base.Version(
        {base.Version.string: versions.max_version_string()},
        versions.min_version_string(), versions.max_version_string())
Esempio n. 24
0
 def test_max_version_not_pinned_in_release_mappings(self):
     CONF.set_override('pin_release_version', None)
     self.assertEqual(release_mappings.RELEASE_MAPPING['master']['api'],
                      versions.max_version_string())
Esempio n. 25
0
 def setUp(self):
     super(TestPost, self).setUp()
     self.headers = {
         api_base.Version.string: str(versions.max_version_string())
     }
Esempio n. 26
0
 def test_max_version_not_pinned(self):
     CONF.set_override('pin_release_version', None)
     self.assertEqual(versions._MAX_VERSION_STRING,
                      versions.max_version_string())