summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/api/ec2/__init__.py2
-rw-r--r--nova/api/ec2/cloud.py18
-rw-r--r--nova/api/ec2/ec2utils.py36
-rw-r--r--nova/api/metadata/base.py3
-rw-r--r--nova/db/api.py18
-rw-r--r--nova/db/sqlalchemy/api.py50
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py67
-rw-r--r--nova/db/sqlalchemy/models.py7
-rw-r--r--nova/tests/api/ec2/test_cloud.py19
9 files changed, 193 insertions, 27 deletions
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index edde3a9e4..6bb19e7b3 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -472,7 +472,7 @@ class Executor(wsgi.Application):
except exception.InstanceNotFound as ex:
LOG.info(_('InstanceNotFound raised: %s'), unicode(ex),
context=context)
- ec2_id = ec2utils.id_to_ec2_id(ex.kwargs['instance_id'])
+ ec2_id = ec2utils.id_to_ec2_inst_id(ex.kwargs['instance_id'])
message = ex.message % {'instance_id': ec2_id}
return ec2_error(req, request_id, type(ex).__name__, message)
except exception.VolumeNotFound as ex:
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 75f28d510..126df0f0a 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -690,8 +690,7 @@ class CloudController(object):
instance = db.instance_get_by_uuid(context.elevated(),
instance_uuid)
- instance_id = instance['id']
- instance_ec2_id = ec2utils.id_to_ec2_id(instance_id)
+ instance_ec2_id = ec2utils.id_to_ec2_inst_id(instance_uuid)
instance_data = '%s[%s]' % (instance_ec2_id,
instance['host'])
v = {}
@@ -778,7 +777,7 @@ class CloudController(object):
volume = self.volume_api.get(context, volume_id)
return {'attachTime': volume['attach_time'],
'device': volume['mountpoint'],
- 'instanceId': ec2utils.id_to_ec2_id(instance_id),
+ 'instanceId': ec2utils.id_to_ec2_inst_id(instance_id),
'requestId': context.request_id,
'status': volume['attach_status'],
'volumeId': ec2utils.id_to_ec2_vol_id(volume_id)}
@@ -797,7 +796,7 @@ class CloudController(object):
return {'attachTime': volume['attach_time'],
'device': volume['mountpoint'],
- 'instanceId': ec2utils.id_to_ec2_id(instance['id']),
+ 'instanceId': ec2utils.id_to_ec2_inst_id(instance['uuid']),
'requestId': context.request_id,
'status': volume['attach_status'],
'volumeId': ec2utils.id_to_ec2_vol_id(volume_id)}
@@ -987,9 +986,10 @@ class CloudController(object):
if instance_id:
instances = []
for ec2_id in instance_id:
- internal_id = ec2utils.ec2_id_to_id(ec2_id)
try:
- instance = self.compute_api.get(context, internal_id)
+ instance_uuid = ec2utils.ec2_inst_id_to_uuid(context,
+ ec2_id)
+ instance = self.compute_api.get(context, instance_uuid)
except exception.NotFound:
continue
instances.append(instance)
@@ -1007,8 +1007,8 @@ class CloudController(object):
if instance['image_ref'] == str(FLAGS.vpn_image_id):
continue
i = {}
- instance_id = instance['id']
- ec2_id = ec2utils.id_to_ec2_id(instance_id)
+ instance_uuid = instance['uuid']
+ ec2_id = ec2utils.id_to_ec2_inst_id(instance_uuid)
i['instanceId'] = ec2_id
image_uuid = instance['image_ref']
i['imageId'] = ec2utils.glance_id_to_ec2_id(context, image_uuid)
@@ -1081,7 +1081,7 @@ class CloudController(object):
fixed_id = floating_ip['fixed_ip_id']
fixed = self.network_api.get_fixed_ip(context, fixed_id)
if fixed['instance_id'] is not None:
- ec2_id = ec2utils.id_to_ec2_id(fixed['instance_id'])
+ ec2_id = ec2utils.id_to_ec2_inst_id(fixed['instance_id'])
address = {'public_ip': floating_ip['address'],
'instance_id': ec2_id}
if context.is_admin:
diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py
index 1735f8b3e..608628209 100644
--- a/nova/api/ec2/ec2utils.py
+++ b/nova/api/ec2/ec2utils.py
@@ -81,13 +81,6 @@ def ec2_id_to_id(ec2_id):
raise exception.InvalidEc2Id(ec2_id=ec2_id)
-def ec2_id_to_uuid(context, ec2_id):
- """Convert an ec2 ID into an instance UUID"""
- instance_id = ec2_id_to_id(ec2_id)
- instance = db.instance_get(context, instance_id)
- return instance['uuid']
-
-
def image_ec2_id(image_id, image_type='ami'):
"""Returns image ec2_id using id and three letter type."""
template = image_type + '-%08x'
@@ -134,6 +127,26 @@ def id_to_ec2_id(instance_id, template='i-%08x'):
return template % int(instance_id)
+def id_to_ec2_inst_id(instance_id):
+ """Get or create an ec2 instance ID (i-[base 16 number]) from uuid."""
+ if utils.is_uuid_like(instance_id):
+ ctxt = context.get_admin_context()
+ int_id = get_int_id_from_instance_uuid(ctxt, instance_id)
+ return id_to_ec2_id(int_id)
+ else:
+ return id_to_ec2_id(instance_id)
+
+
+def ec2_inst_id_to_uuid(context, ec2_id):
+ """"Convert an instance id to uuid."""
+ int_id = ec2_id_to_id(ec2_id)
+ return get_instance_uuid_from_int_id(context, int_id)
+
+
+def get_instance_uuid_from_int_id(context, int_id):
+ return db.get_instance_uuid_by_ec2_id(context, int_id)
+
+
def id_to_ec2_snap_id(snapshot_id):
"""Get or create an ec2 volume ID (vol-[base 16 number]) from uuid."""
if utils.is_uuid_like(snapshot_id):
@@ -163,6 +176,15 @@ def ec2_vol_id_to_uuid(ec2_id):
return get_volume_uuid_from_int_id(ctxt, int_id)
+def get_int_id_from_instance_uuid(context, instance_uuid):
+ if instance_uuid is None:
+ return
+ try:
+ return db.get_ec2_instance_id_by_uuid(context, instance_uuid)
+ except exception.NotFound:
+ return db.ec2_instance_create(context, instance_uuid)['id']
+
+
def get_int_id_from_volume_uuid(context, volume_uuid):
if volume_uuid is None:
return
diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py
index 1b0ac441b..06e290917 100644
--- a/nova/api/metadata/base.py
+++ b/nova/api/metadata/base.py
@@ -88,7 +88,8 @@ class InstanceMetadata():
self.ec2_ids = {}
- self.ec2_ids['instance-id'] = ec2utils.id_to_ec2_id(instance['id'])
+ self.ec2_ids['instance-id'] = ec2utils.id_to_ec2_inst_id(
+ instance['id'])
self.ec2_ids['ami-id'] = ec2utils.glance_id_to_ec2_id(ctxt,
instance['image_ref'])
diff --git a/nova/db/api.py b/nova/db/api.py
index 989864a2b..695c083c9 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -1930,3 +1930,21 @@ def instance_fault_create(context, values):
def instance_fault_get_by_instance_uuids(context, instance_uuids):
"""Get all instance faults for the provided instance_uuids."""
return IMPL.instance_fault_get_by_instance_uuids(context, instance_uuids)
+
+
+####################
+
+
+def get_ec2_instance_id_by_uuid(context, instance_id):
+ """Get ec2 id through uuid from instance_id_mappings table"""
+ return IMPL.get_ec2_instance_id_by_uuid(context, instance_id)
+
+
+def get_instance_uuid_by_ec2_id(context, instance_id):
+ """Get uuid through ec2 id from instance_id_mappings table"""
+ return IMPL.get_instance_uuid_by_ec2_id(context, instance_id)
+
+
+def ec2_instance_create(context, instance_ec2_id):
+ """Create the ec2 id to instance uuid mapping on demand"""
+ return IMPL.ec2_instance_create(context, instance_ec2_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 76578e061..bc0ba5307 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1367,6 +1367,9 @@ def instance_create(context, values):
# exists in the ref when we return. Fixes lazy loading issues.
instance_ref.instance_type
+ # create the instance uuid to ec2_id mapping entry for instance
+ ec2_instance_create(context, instance_ref['uuid'])
+
return instance_ref
@@ -5163,3 +5166,50 @@ def instance_fault_get_by_instance_uuids(context, instance_uuids):
output[row['instance_uuid']].append(data)
return output
+
+
+##################
+
+
+@require_context
+def ec2_instance_create(context, instance_uuid, id=None):
+ """Create ec2 compatable instance by provided uuid"""
+ ec2_instance_ref = models.InstanceIdMapping()
+ ec2_instance_ref.update({'uuid': instance_uuid})
+ if id is not None:
+ ec2_instance_ref.update({'id': id})
+
+ ec2_instance_ref.save()
+
+ return ec2_instance_ref
+
+
+@require_context
+def get_ec2_instance_id_by_uuid(context, instance_id, session=None):
+ result = _ec2_instance_get_query(context,
+ session=session).\
+ filter_by(uuid=instance_id).\
+ first()
+
+ if not result:
+ raise exception.InstanceNotFound(uuid=instance_id)
+
+ return result['id']
+
+
+@require_context
+def get_instance_uuid_by_ec2_id(context, instance_id, session=None):
+ result = _ec2_instance_get_query(context,
+ session=session).\
+ filter_by(id=instance_id).\
+ first()
+
+ if not result:
+ raise exception.InstanceNotFound(id=instance_id)
+
+ return result['uuid']
+
+
+@require_context
+def _ec2_instance_get_query(context, session=None):
+ return model_query(context, models.InstanceIdMapping, session=session)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py b/nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py
new file mode 100644
index 000000000..94adbd89b
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py
@@ -0,0 +1,67 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2012 SINA Corp.
+# All Rights Reserved.
+#
+# 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.
+
+from sqlalchemy import Boolean, Column, DateTime, Integer
+from sqlalchemy import Index, MetaData, String, Table
+from nova import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ # create new table
+ instance_id_mappings = Table('instance_id_mappings', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted',
+ Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(),
+ primary_key=True,
+ nullable=False,
+ autoincrement=True),
+ Column('uuid', String(36), index=True, nullable=False))
+ try:
+ instance_id_mappings.create()
+ except Exception:
+ LOG.exception("Exception while creating table 'instance_id_mappings'")
+ meta.drop_all(tables=[instance_id_mappings])
+ raise
+
+ if migrate_engine.name == "mysql":
+ migrate_engine.execute("ALTER TABLE instance_id_mappings "
+ "Engine=InnoDB")
+
+ instances = Table('instances', meta, autoload=True)
+ instance_id_mappings = Table('instance_id_mappings', meta, autoload=True)
+
+ instance_list = list(instances.select().execute())
+ for instance in instance_list:
+ instance_id = instance['id']
+ uuid = instance['uuid']
+ row = instance_id_mappings.insert()
+ row.execute({'id': instance_id, 'uuid': uuid})
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ instance_id_mappings = Table('instance_id_mappings', meta, autoload=True)
+ instance_id_mappings.drop()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index f63004386..335989135 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -1036,3 +1036,10 @@ class InstanceFault(BASE, NovaBase):
code = Column(Integer(), nullable=False)
message = Column(String(255))
details = Column(Text)
+
+
+class InstanceIdMapping(BASE, NovaBase):
+ """Compatability layer for the EC2 instance service"""
+ __tablename__ = 'instance_id_mappings'
+ id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
+ uuid = Column(String(36), nullable=False)
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index 90724977a..0603cac62 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -244,7 +244,7 @@ class CloudTestCase(test.TestCase):
project_id=project_id)
fixed_ips = nw_info.fixed_ips()
- ec2_id = ec2utils.id_to_ec2_id(inst['id'])
+ ec2_id = ec2utils.id_to_ec2_inst_id(inst['id'])
self.stubs.Set(ec2utils, 'get_ip_info_for_instance',
lambda *args: {'fixed_ips': ['10.0.0.1'],
@@ -760,7 +760,7 @@ class CloudTestCase(test.TestCase):
self.assertEqual(len(result['instancesSet']), 2)
# Now try filtering.
- instance_id = ec2utils.id_to_ec2_id(inst2['id'])
+ instance_id = ec2utils.id_to_ec2_inst_id(inst2['uuid'])
result = self.cloud.describe_instances(self.context,
instance_id=[instance_id])
result = result['reservationSet'][0]
@@ -848,7 +848,7 @@ class CloudTestCase(test.TestCase):
'power_state': power_state_, 'vm_state': vm_state_})
inst = db.instance_create(self.context, values)
- instance_id = ec2utils.id_to_ec2_id(inst['id'])
+ instance_id = ec2utils.id_to_ec2_inst_id(inst['uuid'])
result = self.cloud.describe_instances(self.context,
instance_id=[instance_id])
result = result['reservationSet'][0]
@@ -886,7 +886,7 @@ class CloudTestCase(test.TestCase):
result = result['reservationSet'][0]
self.assertEqual(len(result['instancesSet']), 1)
instance = result['instancesSet'][0]
- instance_id = ec2utils.id_to_ec2_id(inst1['id'])
+ instance_id = ec2utils.id_to_ec2_inst_id(inst1['uuid'])
self.assertEqual(instance['instanceId'], instance_id)
self.assertEqual(instance['publicDnsName'], '1.2.3.4')
self.assertEqual(instance['ipAddress'], '1.2.3.4')
@@ -916,7 +916,7 @@ class CloudTestCase(test.TestCase):
self.assertEqual(len(result['reservationSet']), 1)
result1 = result['reservationSet'][0]['instancesSet']
self.assertEqual(result1[0]['instanceId'],
- ec2utils.id_to_ec2_id(inst2.id))
+ ec2utils.id_to_ec2_inst_id(inst2['uuid']))
def test_describe_instances_with_image_deleted(self):
image_uuid = 'aebef54a-ed67-4d10-912f-14455edce176'
@@ -1083,7 +1083,7 @@ class CloudTestCase(test.TestCase):
self._tearDownBlockDeviceMapping(inst1, inst2, volumes)
def _assertInstance(self, instance_id):
- ec2_instance_id = ec2utils.id_to_ec2_id(instance_id)
+ ec2_instance_id = ec2utils.id_to_ec2_inst_id(instance_id)
result = self.cloud.describe_instances(self.context,
instance_id=[ec2_instance_id])
result = result['reservationSet'][0]
@@ -1109,12 +1109,12 @@ class CloudTestCase(test.TestCase):
"""
(inst1, inst2, volumes) = self._setUpBlockDeviceMapping()
- result = self._assertInstance(inst1['id'])
+ result = self._assertInstance(inst1['uuid'])
self.assertSubDictMatch(self._expected_instance_bdm1, result)
self._assertEqualBlockDeviceMapping(
self._expected_block_device_mapping0, result['blockDeviceMapping'])
- result = self._assertInstance(inst2['id'])
+ result = self._assertInstance(inst2['uuid'])
self.assertSubDictMatch(self._expected_instance_bdm2, result)
self._tearDownBlockDeviceMapping(inst1, inst2, volumes)
@@ -1875,7 +1875,8 @@ class CloudTestCase(test.TestCase):
'max_count': 1, }
instance_id = self._run_instance(**kwargs)
- internal_uuid = ec2utils.ec2_id_to_uuid(self.context, instance_id)
+ internal_uuid = db.get_instance_uuid_by_ec2_id(self.context,
+ ec2utils.ec2_id_to_id(instance_id))
instance = db.instance_update(self.context, internal_uuid,
{'disable_terminate': True})