diff --git a/horizon/templates/horizon/common/_sidebar.html b/horizon/templates/horizon/common/_sidebar.html index 1f7a9a23e..b5367674f 100644 --- a/horizon/templates/horizon/common/_sidebar.html +++ b/horizon/templates/horizon/common/_sidebar.html @@ -32,5 +32,23 @@ {% endif %} + {% with num_of_regions=request.user.available_services_regions|length %} + {% if num_of_regions > 1 %} +
+ {% endif %} + {% endwith %} + {% horizon_dashboard_nav %} diff --git a/openstack_dashboard/api/base.py b/openstack_dashboard/api/base.py index ff227f6f1..ce16ba1e9 100644 --- a/openstack_dashboard/api/base.py +++ b/openstack_dashboard/api/base.py @@ -97,7 +97,7 @@ class APIDictWrapper(object): dictionary, in addition to attribute accesses. Attribute access is the preferred method of access, to be - consistent with api resource objects from novclient. + consistent with api resource objects from novaclient. """ def __init__(self, apidict): self._apidict = apidict @@ -198,18 +198,21 @@ ENDPOINT_TYPE_TO_INTERFACE = { } -def get_url_for_service(service, endpoint_type): +def get_url_for_service(service, region, endpoint_type): identity_version = get_version_from_service(service) for endpoint in service['endpoints']: - try: - if identity_version < 3: - return endpoint[endpoint_type] - else: - interface = ENDPOINT_TYPE_TO_INTERFACE.get(endpoint_type, '') - if endpoint['interface'] == interface: - return endpoint['url'] - except (IndexError, KeyError): - pass + # ignore region for identity + if service['type'] == 'identity' or region == endpoint['region']: + try: + if identity_version < 3: + return endpoint[endpoint_type] + else: + interface = \ + ENDPOINT_TYPE_TO_INTERFACE.get(endpoint_type, '') + if endpoint['interface'] == interface: + return endpoint['url'] + except (IndexError, KeyError): + return None return None @@ -222,9 +225,13 @@ def url_for(request, service_type, endpoint_type=None): catalog = request.user.service_catalog service = get_service_from_catalog(catalog, service_type) if service: - url = get_url_for_service(service, endpoint_type) + url = get_url_for_service(service, + request.user.services_region, + endpoint_type) if not url and fallback_endpoint_type: - url = get_url_for_service(service, fallback_endpoint_type) + url = get_url_for_service(service, + request.user.services_region, + fallback_endpoint_type) if url: return url raise exceptions.ServiceCatalogException(service_type) @@ -233,7 +240,14 @@ def url_for(request, service_type, endpoint_type=None): def is_service_enabled(request, service_type, service_name=None): service = get_service_from_catalog(request.user.service_catalog, service_type) - if service and service_name: - return service['name'] == service_name - else: - return service is not None + if service: + region = request.user.services_region + for endpoint in service['endpoints']: + # ignore region for identity + if service['type'] == 'identity' or \ + endpoint['region'] == region: + if service_name: + return service['name'] == service_name + else: + return True + return False diff --git a/openstack_dashboard/api/keystone.py b/openstack_dashboard/api/keystone.py index 458d1c38f..feae7ae02 100644 --- a/openstack_dashboard/api/keystone.py +++ b/openstack_dashboard/api/keystone.py @@ -77,12 +77,17 @@ class Service(base.APIDictWrapper): """ Wrapper for a dict based on the service data from keystone. """ _attrs = ['id', 'type', 'name'] - def __init__(self, service, *args, **kwargs): + def __init__(self, service, region, *args, **kwargs): super(Service, self).__init__(service, *args, **kwargs) - self.url = service['endpoints'][0]['internalURL'] - self.host = urlparse.urlparse(self.url).hostname - self.region = service['endpoints'][0]['region'] + self.public_url = base.get_url_for_service(service, region, + 'publicURL') + self.url = base.get_url_for_service(service, region, 'internalURL') + if self.url: + self.host = urlparse.urlparse(self.url).hostname + else: + self.host = None self.disabled = None + self.region = region def __unicode__(self): if(self.type == "identity"): diff --git a/openstack_dashboard/dashboards/admin/info/tables.py b/openstack_dashboard/dashboards/admin/info/tables.py index 7e4ed3217..bfe4f2dd5 100644 --- a/openstack_dashboard/dashboards/admin/info/tables.py +++ b/openstack_dashboard/dashboards/admin/info/tables.py @@ -63,7 +63,10 @@ def get_enabled(service, reverse=False): options = ["Enabled", "Disabled"] if reverse: options.reverse() - return options[0] if not service.disabled else options[1] + # if not configured in this region, neither option makes sense + if service.host: + return options[0] if not service.disabled else options[1] + return None class ServicesTable(tables.DataTable): diff --git a/openstack_dashboard/dashboards/admin/info/tabs.py b/openstack_dashboard/dashboards/admin/info/tabs.py index c99ea1853..28740419b 100644 --- a/openstack_dashboard/dashboards/admin/info/tabs.py +++ b/openstack_dashboard/dashboards/admin/info/tabs.py @@ -54,7 +54,8 @@ class ServicesTab(tabs.TableTab): services = [] for i, service in enumerate(request.user.service_catalog): service['id'] = i - services.append(keystone.Service(service)) + services.append( + keystone.Service(service, request.user.services_region)) return services diff --git a/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py b/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py index dc520cba2..88f171a7a 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py @@ -20,10 +20,6 @@ from django.utils.translation import ugettext_lazy as _ from horizon import tables -def get_endpoint(service): - return service.endpoints[0]['publicURL'] - - def pretty_service_names(name): name = name.replace('-', ' ') if name in ['ec2', 's3']: @@ -53,7 +49,7 @@ class EndpointsTable(tables.DataTable): api_name = tables.Column('type', verbose_name=_("Service"), filters=(pretty_service_names,)) - api_endpoint = tables.Column(get_endpoint, + api_endpoint = tables.Column('public_url', verbose_name=_("Service Endpoint")) class Meta: diff --git a/openstack_dashboard/dashboards/project/access_and_security/tabs.py b/openstack_dashboard/dashboards/project/access_and_security/tabs.py index 90420bac2..66dd6300e 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/tabs.py +++ b/openstack_dashboard/dashboards/project/access_and_security/tabs.py @@ -117,7 +117,9 @@ class APIAccessTab(tabs.TableTab): services = [] for i, service in enumerate(self.request.user.service_catalog): service['id'] = i - services.append(keystone.Service(service)) + services.append( + keystone.Service(service, self.request.user.services_region)) + return services diff --git a/openstack_dashboard/test/api_tests/base_tests.py b/openstack_dashboard/test/api_tests/base_tests.py index 13493f9c8..c6aa0460d 100644 --- a/openstack_dashboard/test/api_tests/base_tests.py +++ b/openstack_dashboard/test/api_tests/base_tests.py @@ -140,3 +140,25 @@ class ApiHelperTests(test.TestCase): 'Select a new nonexistent service catalog key') with self.assertRaises(exceptions.ServiceCatalogException): url = api_base.url_for(self.request, 'notAnApi') + + self.request.user.services_region = "RegionTwo" + url = api_base.url_for(self.request, 'compute') + self.assertEqual(url, 'http://public.nova2.example.com:8774/v2') + + self.request.user.services_region = "RegionTwo" + url = api_base.url_for(self.request, 'compute', + endpoint_type='adminURL') + self.assertEqual(url, 'http://admin.nova2.example.com:8774/v2') + + self.request.user.services_region = "RegionTwo" + with self.assertRaises(exceptions.ServiceCatalogException): + url = api_base.url_for(self.request, 'image') + + self.request.user.services_region = "bogus_value" + url = api_base.url_for(self.request, 'identity', + endpoint_type='adminURL') + self.assertEqual(url, 'http://admin.keystone.example.com:35357/v2.0') + + self.request.user.services_region = "bogus_value" + with self.assertRaises(exceptions.ServiceCatalogException): + url = api_base.url_for(self.request, 'image') diff --git a/openstack_dashboard/test/api_tests/keystone_tests.py b/openstack_dashboard/test/api_tests/keystone_tests.py index de8aafe93..02223d454 100644 --- a/openstack_dashboard/test/api_tests/keystone_tests.py +++ b/openstack_dashboard/test/api_tests/keystone_tests.py @@ -91,10 +91,28 @@ class ServiceAPITests(test.APITestCase): catalog = self.service_catalog identity_data = api.base.get_service_from_catalog(catalog, "identity") identity_data['id'] = 1 - service = api.keystone.Service(identity_data) + region = identity_data["endpoints"][0]["region"] + service = api.keystone.Service(identity_data, region) self.assertEqual(unicode(service), u"identity (native backend)") self.assertEqual(service.region, identity_data["endpoints"][0]["region"]) self.assertEqual(service.url, "http://int.keystone.example.com:5000/v2.0") + self.assertEqual(service.public_url, + "http://public.keystone.example.com:5000/v2.0") self.assertEqual(service.host, "int.keystone.example.com") + + def test_service_wrapper_service_in_region(self): + catalog = self.service_catalog + compute_data = api.base.get_service_from_catalog(catalog, "compute") + compute_data['id'] = 1 + region = compute_data["endpoints"][1]["region"] + service = api.keystone.Service(compute_data, region) + self.assertEqual(unicode(service), u"compute") + self.assertEqual(service.region, + compute_data["endpoints"][1]["region"]) + self.assertEqual(service.url, + "http://int.nova2.example.com:8774/v2") + self.assertEqual(service.public_url, + "http://public.nova2.example.com:8774/v2") + self.assertEqual(service.host, "int.nova2.example.com") diff --git a/openstack_dashboard/test/test_data/keystone_data.py b/openstack_dashboard/test/test_data/keystone_data.py index 38f3f67a8..d7340fce6 100644 --- a/openstack_dashboard/test/test_data/keystone_data.py +++ b/openstack_dashboard/test/test_data/keystone_data.py @@ -40,12 +40,20 @@ SERVICE_CATALOG = [ {"region": "RegionOne", "adminURL": "http://admin.nova.example.com:8774/v2", "internalURL": "http://int.nova.example.com:8774/v2", - "publicURL": "http://public.nova.example.com:8774/v2"}]}, + "publicURL": "http://public.nova.example.com:8774/v2"}, + {"region": "RegionTwo", + "adminURL": "http://admin.nova2.example.com:8774/v2", + "internalURL": "http://int.nova2.example.com:8774/v2", + "publicURL": "http://public.nova2.example.com:8774/v2"}]}, {"type": "volume", "name": "nova", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", + "adminURL": "http://admin.nova.example.com:8776/v1", + "internalURL": "http://int.nova.example.com:8776/v1", + "publicURL": "http://public.nova.example.com:8776/v1"}, + {"region": "RegionTwo", "adminURL": "http://admin.nova.example.com:8776/v1", "internalURL": "http://int.nova.example.com:8776/v1", "publicURL": "http://public.nova.example.com:8776/v1"}]},