summaryrefslogtreecommitdiffstats
path: root/keystone
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-12-18 18:01:40 +0000
committerGerrit Code Review <review@openstack.org>2012-12-18 18:01:40 +0000
commitac2d92ca2eea1070f765be320acb62fd5bef6dd3 (patch)
treef339e5c507efc5df31a33e7595e4401cc1475e8c /keystone
parent1a0d30bf0173f8e03abeac4bda2e807bd4f29412 (diff)
parent2f851340ee8969193b9dcc1913401aa9b33c5d97 (diff)
downloadkeystone-ac2d92ca2eea1070f765be320acb62fd5bef6dd3.tar.gz
keystone-ac2d92ca2eea1070f765be320acb62fd5bef6dd3.tar.xz
keystone-ac2d92ca2eea1070f765be320acb62fd5bef6dd3.zip
Merge "Split endpoint records in SQL by interface"
Diffstat (limited to 'keystone')
-rw-r--r--keystone/catalog/backends/sql.py54
-rw-r--r--keystone/catalog/controllers.py65
-rw-r--r--keystone/common/sql/migrate_repo/versions/010_endpoints_v3.py54
-rw-r--r--keystone/common/sql/migrate_repo/versions/011_populate_endpoint_type.py96
-rw-r--r--keystone/common/sql/migrate_repo/versions/012_drop_legacy_endpoints.py51
5 files changed, 288 insertions, 32 deletions
diff --git a/keystone/catalog/backends/sql.py b/keystone/catalog/backends/sql.py
index df128c7b..380d4660 100644
--- a/keystone/catalog/backends/sql.py
+++ b/keystone/catalog/backends/sql.py
@@ -36,12 +36,14 @@ class Service(sql.ModelBase, sql.DictBase):
class Endpoint(sql.ModelBase, sql.DictBase):
__tablename__ = 'endpoint'
- attributes = ['id', 'region', 'service_id']
+ attributes = ['id', 'interface', 'region', 'service_id', 'url']
id = sql.Column(sql.String(64), primary_key=True)
+ interface = sql.Column(sql.String(8), primary_key=True)
region = sql.Column('region', sql.String(255))
service_id = sql.Column(sql.String(64),
sql.ForeignKey('service.id'),
nullable=False)
+ url = sql.Column(sql.Text())
extra = sql.Column(sql.JsonBlob())
@@ -88,7 +90,9 @@ class Catalog(sql.Base, catalog.Driver):
old_dict = ref.to_dict()
old_dict.update(service_ref)
new_service = Service.from_dict(old_dict)
- ref.type = new_service.type
+ for attr in Service.attributes:
+ if attr != 'id':
+ setattr(ref, attr, getattr(new_service, attr))
ref.extra = new_service.extra
session.flush()
return ref.to_dict()
@@ -132,8 +136,9 @@ class Catalog(sql.Base, catalog.Driver):
old_dict = ref.to_dict()
old_dict.update(endpoint_ref)
new_endpoint = Endpoint.from_dict(old_dict)
- ref.service_id = new_endpoint.service_id
- ref.region = new_endpoint.region
+ for attr in Endpoint.attributes:
+ if attr != 'id':
+ setattr(ref, attr, getattr(new_endpoint, attr))
ref.extra = new_endpoint.extra
session.flush()
return ref.to_dict()
@@ -142,25 +147,28 @@ class Catalog(sql.Base, catalog.Driver):
d = dict(CONF.iteritems())
d.update({'tenant_id': tenant_id,
'user_id': user_id})
- catalog = {}
-
- endpoints = self.list_endpoints()
- for ep in endpoints:
- service = self.get_service(ep['service_id'])
- srv_type = service['type']
- srv_name = service['name']
- region = ep['region']
-
- if region not in catalog:
- catalog[region] = {}
- catalog[region][srv_type] = {}
-
- srv_type = catalog[region][srv_type]
- srv_type['id'] = ep['id']
- srv_type['name'] = srv_name
- srv_type['publicURL'] = core.format_url(ep.get('publicurl', ''), d)
- srv_type['internalURL'] = core.format_url(ep.get('internalurl'), d)
- srv_type['adminURL'] = core.format_url(ep.get('adminurl'), d)
+ catalog = {}
+ services = {}
+ for endpoint in self.list_endpoints():
+ # look up the service
+ services.setdefault(
+ endpoint['service_id'],
+ self.get_service(endpoint['service_id']))
+ service = services[endpoint['service_id']]
+
+ # add the endpoint to the catalog if it's not already there
+ catalog.setdefault(endpoint['region'], {})
+ catalog[endpoint['region']].setdefault(
+ service['type'], {
+ 'id': endpoint['id'],
+ 'name': service['name'],
+ 'publicURL': '', # this may be overridden, but must exist
+ })
+
+ # add the interface's url
+ url = core.format_url(endpoint.get('url'), d)
+ interface_url = '%sURL' % endpoint['interface']
+ catalog[endpoint['region']][service['type']][interface_url] = url
return catalog
diff --git a/keystone/catalog/controllers.py b/keystone/catalog/controllers.py
index b0460fc9..42ba4ed7 100644
--- a/keystone/catalog/controllers.py
+++ b/keystone/catalog/controllers.py
@@ -20,6 +20,13 @@ import uuid
from keystone.catalog import core
from keystone.common import controller
from keystone.common import wsgi
+from keystone import exception
+from keystone import identity
+from keystone import policy
+from keystone import token
+
+
+INTERFACES = ['public', 'internal', 'admin']
class Service(controller.V2Controller):
@@ -50,22 +57,62 @@ class Service(controller.V2Controller):
class Endpoint(controller.V2Controller):
def get_endpoints(self, context):
+ """Merge matching v3 endpoint refs into legacy refs."""
self.assert_admin(context)
- endpoint_list = self.catalog_api.list_endpoints(context)
- return {'endpoints': endpoint_list}
+ legacy_endpoints = {}
+ for endpoint in self.catalog_api.list_endpoints(context):
+ if not endpoint['legacy_endpoint_id']:
+ # endpoints created in v3 should not appear on the v2 API
+ continue
+
+ # is this is a legacy endpoint we haven't indexed yet?
+ if endpoint['legacy_endpoint_id'] not in legacy_endpoints:
+ legacy_ep = endpoint.copy()
+ legacy_ep['id'] = legacy_ep.pop('legacy_endpoint_id')
+ legacy_ep.pop('interface')
+ legacy_ep.pop('url')
+
+ legacy_endpoints[endpoint['legacy_endpoint_id']] = legacy_ep
+ else:
+ legacy_ep = legacy_endpoints[endpoint['legacy_endpoint_id']]
+
+ # add the legacy endpoint with an interface url
+ legacy_ep['%surl' % endpoint['interface']] = endpoint['url']
+ return {'endpoints': legacy_endpoints.values()}
def create_endpoint(self, context, endpoint):
+ """Create three v3 endpoint refs based on a legacy ref."""
self.assert_admin(context)
- endpoint_id = uuid.uuid4().hex
- endpoint_ref = endpoint.copy()
- endpoint_ref['id'] = endpoint_id
- new_endpoint_ref = self.catalog_api.create_endpoint(
- context, endpoint_id, endpoint_ref)
- return {'endpoint': new_endpoint_ref}
+
+ legacy_endpoint_ref = endpoint.copy()
+
+ urls = dict((i, endpoint.pop('%surl' % i)) for i in INTERFACES)
+ legacy_endpoint_id = uuid.uuid4().hex
+ for interface, url in urls.iteritems():
+ endpoint_ref = endpoint.copy()
+ endpoint_ref['id'] = uuid.uuid4().hex
+ endpoint_ref['legacy_endpoint_id'] = legacy_endpoint_id
+ endpoint_ref['interface'] = interface
+ endpoint_ref['url'] = url
+
+ self.catalog_api.create_endpoint(
+ context, endpoint_ref['id'], endpoint_ref)
+
+ legacy_endpoint_ref['id'] = legacy_endpoint_id
+ return {'endpoint': legacy_endpoint_ref}
def delete_endpoint(self, context, endpoint_id):
+ """Delete up to three v3 endpoint refs based on a legacy ref ID."""
self.assert_admin(context)
- self.catalog_api.delete_endpoint(context, endpoint_id)
+
+ deleted_at_least_one = False
+ for endpoint in self.catalog_api.list_endpoints(context):
+ if endpoint['legacy_endpoint_id'] == endpoint_id:
+ self.catalog_api.delete_endpoint(context, endpoint['id'])
+ deleted_at_least_one = True
+
+ if not deleted_at_least_one:
+ raise exception.EndpointNotFound(endpoint_id=endpoint_id)
class ServiceV3(controller.V3Controller):
diff --git a/keystone/common/sql/migrate_repo/versions/010_endpoints_v3.py b/keystone/common/sql/migrate_repo/versions/010_endpoints_v3.py
new file mode 100644
index 00000000..2d919aab
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/010_endpoints_v3.py
@@ -0,0 +1,54 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import migrate
+import sqlalchemy as sql
+
+
+def upgrade(migrate_engine):
+ """Create API-version specific endpoint tables."""
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ legacy_table = sql.Table('endpoint', meta, autoload=True)
+ legacy_table.rename('endpoint_v2')
+
+ new_table = sql.Table(
+ 'endpoint_v3',
+ meta,
+ sql.Column('id', sql.String(64), primary_key=True),
+ sql.Column('legacy_endpoint_id', sql.String(64)),
+ sql.Column('interface', sql.String(8), nullable=False),
+ sql.Column('region', sql.String(255)),
+ sql.Column('service_id',
+ sql.String(64),
+ sql.ForeignKey('service.id'),
+ nullable=False),
+ sql.Column('url', sql.Text(), nullable=False),
+ sql.Column('extra', sql.Text()))
+ new_table.create(migrate_engine, checkfirst=True)
+
+
+def downgrade(migrate_engine):
+ """Replace API-version specific endpoint tables with one based on v2."""
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ new_table = sql.Table('endpoint_v3', meta, autoload=True)
+ new_table.drop()
+
+ legacy_table = sql.Table('endpoint_v2', meta, autoload=True)
+ legacy_table.rename('endpoint')
diff --git a/keystone/common/sql/migrate_repo/versions/011_populate_endpoint_type.py b/keystone/common/sql/migrate_repo/versions/011_populate_endpoint_type.py
new file mode 100644
index 00000000..ae41d1f3
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/011_populate_endpoint_type.py
@@ -0,0 +1,96 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+import uuid
+
+import sqlalchemy as sql
+from sqlalchemy import orm
+
+
+ENDPOINT_TYPES = ['public', 'internal', 'admin']
+
+
+def upgrade(migrate_engine):
+ """Split each legacy endpoint into seperate records for each interface."""
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ legacy_table = sql.Table('endpoint_v2', meta, autoload=True)
+ new_table = sql.Table('endpoint_v3', meta, autoload=True)
+
+ session = orm.sessionmaker(bind=migrate_engine)()
+ for ref in session.query(legacy_table).all():
+ # pull urls out of extra
+ extra = json.loads(ref.extra)
+ urls = dict((i, extra.pop('%surl' % i)) for i in ENDPOINT_TYPES)
+
+ for interface in ENDPOINT_TYPES:
+ endpoint = {
+ 'id': uuid.uuid4().hex,
+ 'legacy_endpoint_id': ref.id,
+ 'interface': interface,
+ 'region': ref.region,
+ 'service_id': ref.service_id,
+ 'url': urls[interface],
+ 'extra': json.dumps(extra),
+ }
+ session.execute(
+ 'INSERT INTO `%s` (%s) VALUES (%s)' % (
+ new_table.name,
+ ', '.join('%s' % k for k in endpoint.keys()),
+ ', '.join("'%s'" % v for v in endpoint.values())))
+ session.commit()
+
+
+def downgrade(migrate_engine):
+ """Re-create the v2 endpoints table based on v3 endpoints."""
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ legacy_table = sql.Table('endpoint_v2', meta, autoload=True)
+ new_table = sql.Table('endpoint_v3', meta, autoload=True)
+
+ session = orm.sessionmaker(bind=migrate_engine)()
+ for ref in session.query(new_table).all():
+ extra = json.loads(ref.extra)
+ extra['%surl' % ref.interface] = ref.url
+ endpoint = {
+ 'id': ref.legacy_endpoint_id,
+ 'region': ref.region,
+ 'service_id': ref.service_id,
+ 'extra': json.dumps(extra),
+ }
+
+ try:
+ session.execute(
+ 'INSERT INTO `%s` (%s) VALUES (%s)' % (
+ legacy_table.name,
+ ', '.join('%s' % k for k in endpoint.keys()),
+ ', '.join("'%s'" % v for v in endpoint.values())))
+ except sql.exc.IntegrityError:
+ q = session.query(legacy_table)
+ q = q.filter_by(id=ref.legacy_endpoint_id)
+ legacy_ref = q.one()
+ extra = json.loads(legacy_ref.extra)
+ extra['%surl' % ref.interface] = ref.url
+
+ session.execute(
+ 'UPDATE `%s` SET extra=\'%s\' WHERE id="%s"' % (
+ legacy_table.name,
+ json.dumps(extra),
+ legacy_ref.id))
+ session.commit()
diff --git a/keystone/common/sql/migrate_repo/versions/012_drop_legacy_endpoints.py b/keystone/common/sql/migrate_repo/versions/012_drop_legacy_endpoints.py
new file mode 100644
index 00000000..e98fbb40
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/012_drop_legacy_endpoints.py
@@ -0,0 +1,51 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import migrate
+import sqlalchemy as sql
+
+
+def upgrade(migrate_engine):
+ """Replace API-version specific endpoint tables with one based on v3."""
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ legacy_table = sql.Table('endpoint_v2', meta, autoload=True)
+ legacy_table.drop()
+
+ new_table = sql.Table('endpoint_v3', meta, autoload=True)
+ new_table.rename('endpoint')
+
+
+def downgrade(migrate_engine):
+ """Create API-version specific endpoint tables."""
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ new_table = sql.Table('endpoint', meta, autoload=True)
+ new_table.rename('endpoint_v3')
+
+ legacy_table = sql.Table(
+ 'endpoint_v2',
+ meta,
+ sql.Column('id', sql.String(64), primary_key=True),
+ sql.Column('region', sql.String(255)),
+ sql.Column('service_id',
+ sql.String(64),
+ sql.ForeignKey('service.id'),
+ nullable=False),
+ sql.Column('extra', sql.Text()))
+ legacy_table.create(migrate_engine, checkfirst=True)