Multiple regions with same keystone

Bug #1064822 reported by Moh
26
This bug affects 5 people
Affects Status Importance Assigned to Milestone
OpenStack Dashboard (Horizon)
Won't Fix
Undecided
Gabriel Hurley

Bug Description

When I have multiple regions with same identity service, Horizon stops working. For example, I have two independent regions that uses a central keystone. If I enter the following statement in local_settings.py, horizon stops working:

AVAILABLE_REGIONS =[
('cluster1','http://example.com:5000/v2.0'),
('cluster2','http://example.com:5000/v2.0'),
]
same identity manager for different regions.

First off, I can not login in cluster2. If I run an instance in cluster2, horizon initiates the instance on cluster1. It seems that Horizon is ignoring the cluster2 completely while the nams is listed in Regions. However, if they have different uri, it works fine.

Revision history for this message
Joe T (joe-topjian-v) wrote :

Hello,

Coincidentally I ran into the need to fix this problem around the same time you opened this bug report. I have modified Horizon (Essex) to fix this.

https://github.com/jtopjian/horizon-2012.1.3/commit/6690c14a6bb54fdf9df61dc2f9f3ea3522ce861b

The core issue is that the tuple format of AVAILABLE_REGIONS has to be (value, key) to accommodate for Django's drop-down box. This is why (region_endpoint, region_name) has to be entered in that order.

The fix I did is pretty hack-ish, but it gets the job done. There are also other commits in that repo that provide additional region functionality to Horizon.

If you do not want to modify the code, the other workaround is to have your Keystone server be accessible via multiple IPs or multiple hostnames.

Hope that helps,
Joe

Revision history for this message
Gabriel Hurley (gabriel-hurley) wrote :

If you're using the same keystone for both clusters, there's no practical differentiation to logging in to one versus the other... what do you expect it to do differently?

I do agree that because Django's ChoiceField will pick the first matching value it'll only ever let you into one, but I'm not convinced this is a bug yet. Having duplicate values in an HTML choices list is generally bad practice, and you're not making any real differentiation.

If there were some additional flag that was sent during authentication to indicate you were using one cluster versus the other I'd understand the need for this, but the difference right now is purely a matter of display.

Changed in horizon:
assignee: nobody → Gabriel Hurley (gabriel-hurley)
status: New → Incomplete
Revision history for this message
Joe T (joe-topjian-v) wrote :

Having a single Keystone service for multiple regions allows for a simple single sign-on type service for multiple regions. This, of course, is not the best way to do federated/single sign-on, but it's relatively easy to create at the moment.

The main use-case for this is if all users of your cloud use multiple regions. Rather than having to ensure all Keystone services in all regions are synchronized, everything just authenticates off of one.

Revision history for this message
Gabriel Hurley (gabriel-hurley) wrote :

I get the single-auth part, but Keystone is going to return the same service catalog regardless of which "cluster" you think you're authenticating against. There's nothing to differentiate between the two.

Can you enlighten me as to how the rest of the system is supposed to work once you've done that auth?

Revision history for this message
Svetlana Shturm (sshturm) wrote :

I had this problem in folsom branch. It happens because option creation in a select tag of the login form currently uses endpoint address as a value. In case if we have equal endpoints with different regions, we can't get a region by this value. In folsom branch authenticate using django-backend now. So, If you want fix this bug you must change this code:

/usr/local/lib/python2.7/dist-packages/openstack_auth/forms.py
@staticmethod
    def get_region_choices():
        default_region = (settings.OPENSTACK_KEYSTONE_URL, "Default Region")
  - return getattr(settings, 'AVAILABLE_REGIONS', [default_region])
  + regions = getattr(settings, 'AVAILABLE_REGIONS', [default_region])
  + options = []
  + return [('_'.join([region[0], region[1]]), region[1]) for region in regions]

@sensitive_variables()
    def clean(self):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')
 - region = self.cleaned_data.get('region')
 + region = self.cleaned_data.get('region').split('_')[0]

/usr/local/lib/python2.7/dist-packages/openstack_auth/views.py
@never_cache
def login(request):
    """ Logs a user in using the :class:`~openstack_auth.forms.Login` form. """
    # Get our initial region for the form.
    initial = {}
    current_region = request.session.get('region_endpoint', None)
- requested_region = request.GET.get('region', None)
+ requested_region = requested_region_name = None
+ requested_option = request.POST.get('region', None)
+ if requested_option is not None:
+ requested_region, requested_region_name = requested_option.split('_')

    if request.user.is_authenticated():
        set_session_from_user(request, request.user)
        region = request.user.endpoint
        region_name = dict(Login.get_region_choices()).get(region)
        request.session['region_endpoint'] = region
- request.session['region_name'] = region_name
+ request.session['region_name'] = requested_region_name

Revision history for this message
Svetlana Shturm (sshturm) wrote :

Also you must change /opt/stack/horizon/horizon/api/base.py
def url_for(request, service_type, admin=False, endpoint_type=None):
    endpoint_type = endpoint_type or getattr(settings,
                                             'OPENSTACK_ENDPOINT_TYPE',
                                             'publicURL')
+ region_name = request.session.get('region_name')
    catalog = request.user.service_catalog
    service = get_service_from_catalog(catalog, service_type)
    if service:
        try:
            if admin:
- return service['endpoints'][0]['adminURL']
+ for endpoint in service['endpoints']:
+ if endpoint['region'] == region_name:
+ return endpoint['adminURL']
            else:
- return service['endpoints'][0][endpoint_type]
+ for endpoint in service['endpoints']:
+ if endpoint['region'] == region_name:
+ return endpoint[endpoint_type]
            raise ValueError(region_name)
        except (IndexError, KeyError):
            raise exceptions.ServiceCatalogException(service_type)
    else:
        raise exceptions.ServiceCatalogException(service_type)

Revision history for this message
Gabriel Hurley (gabriel-hurley) wrote :

I'm closing this bug out because it's superseded by this blueprint: https://blueprints.launchpad.net/horizon/+spec/multiple-service-endpoints

It's definitely something worth working on, but there are larger design/implementation questions that need to be addressed and it's a moderately-sized feature to be added, not a bug.

Changed in horizon:
status: Incomplete → Won't Fix
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.