summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorIsaku Yamahata <yamahata@valinux.co.jp>2011-06-17 23:52:22 +0000
committerTarmac <>2011-06-17 23:52:22 +0000
commitcf751516b6d7381fca5f0678c07baaa0f7fccece (patch)
treea30672ff2959eca20645d9d83f38e09819a9e602 /nova/api
parenta854d4e17214269b729777967e028600fdf4ee50 (diff)
parent74be8d2791a5579725fa07c8403545ede3354b2e (diff)
downloadnova-cf751516b6d7381fca5f0678c07baaa0f7fccece.tar.gz
nova-cf751516b6d7381fca5f0678c07baaa0f7fccece.tar.xz
nova-cf751516b6d7381fca5f0678c07baaa0f7fccece.zip
Implements a portion of ec2 ebs boot.
What's implemented - block_device_mapping option for run instance with volume (ephemeral device and no device isn't supported yet) - stop/start instance TODO: - ephemeral device/no device - machine image
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/ec2/apirequest.py78
-rw-r--r--nova/api/ec2/cloud.py58
-rw-r--r--nova/api/ec2/ec2utils.py94
3 files changed, 144 insertions, 86 deletions
diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py
index 6672e60bb..7d78c5cfa 100644
--- a/nova/api/ec2/apirequest.py
+++ b/nova/api/ec2/apirequest.py
@@ -21,22 +21,15 @@ APIRequest class
"""
import datetime
-import re
# TODO(termie): replace minidom with etree
from xml.dom import minidom
from nova import log as logging
+from nova.api.ec2 import ec2utils
LOG = logging.getLogger("nova.api.request")
-_c2u = re.compile('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))')
-
-
-def _camelcase_to_underscore(str):
- return _c2u.sub(r'_\1', str).lower().strip('_')
-
-
def _underscore_to_camelcase(str):
return ''.join([x[:1].upper() + x[1:] for x in str.split('_')])
@@ -51,59 +44,6 @@ def _database_to_isoformat(datetimeobj):
return datetimeobj.strftime("%Y-%m-%dT%H:%M:%SZ")
-def _try_convert(value):
- """Return a non-string from a string or unicode, if possible.
-
- ============= =====================================================
- When value is returns
- ============= =====================================================
- zero-length ''
- 'None' None
- 'True' True
- 'False' False
- '0', '-0' 0
- 0xN, -0xN int from hex (postitive) (N is any number)
- 0bN, -0bN int from binary (positive) (N is any number)
- * try conversion to int, float, complex, fallback value
-
- """
- if len(value) == 0:
- return ''
- if value == 'None':
- return None
- if value == 'True':
- return True
- if value == 'False':
- return False
- valueneg = value[1:] if value[0] == '-' else value
- if valueneg == '0':
- return 0
- if valueneg == '':
- return value
- if valueneg[0] == '0':
- if valueneg[1] in 'xX':
- return int(value, 16)
- elif valueneg[1] in 'bB':
- return int(value, 2)
- else:
- try:
- return int(value, 8)
- except ValueError:
- pass
- try:
- return int(value)
- except ValueError:
- pass
- try:
- return float(value)
- except ValueError:
- pass
- try:
- return complex(value)
- except ValueError:
- return value
-
-
class APIRequest(object):
def __init__(self, controller, action, version, args):
self.controller = controller
@@ -114,7 +54,7 @@ class APIRequest(object):
def invoke(self, context):
try:
method = getattr(self.controller,
- _camelcase_to_underscore(self.action))
+ ec2utils.camelcase_to_underscore(self.action))
except AttributeError:
controller = self.controller
action = self.action
@@ -125,19 +65,7 @@ class APIRequest(object):
# and reraise as 400 error.
raise Exception(_error)
- args = {}
- for key, value in self.args.items():
- parts = key.split(".")
- key = _camelcase_to_underscore(parts[0])
- if isinstance(value, str) or isinstance(value, unicode):
- # NOTE(vish): Automatically convert strings back
- # into their respective values
- value = _try_convert(value)
- if len(parts) > 1:
- d = args.get(key, {})
- d[parts[1]] = value
- value = d
- args[key] = value
+ args = ec2utils.dict_from_dotted_str(self.args.items())
for key in args.keys():
# NOTE(vish): Turn numeric dict keys into lists
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index c8a7e69be..97875f1f5 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -909,6 +909,25 @@ class CloudController(object):
if kwargs.get('ramdisk_id'):
ramdisk = self._get_image(context, kwargs['ramdisk_id'])
kwargs['ramdisk_id'] = ramdisk['id']
+ for bdm in kwargs.get('block_device_mapping', []):
+ # NOTE(yamahata)
+ # BlockDevicedMapping.<N>.DeviceName
+ # BlockDevicedMapping.<N>.Ebs.SnapshotId
+ # BlockDevicedMapping.<N>.Ebs.VolumeSize
+ # BlockDevicedMapping.<N>.Ebs.DeleteOnTermination
+ # BlockDevicedMapping.<N>.VirtualName
+ # => remove .Ebs and allow volume id in SnapshotId
+ ebs = bdm.pop('ebs', None)
+ if ebs:
+ ec2_id = ebs.pop('snapshot_id')
+ id = ec2utils.ec2_id_to_id(ec2_id)
+ if ec2_id.startswith('snap-'):
+ bdm['snapshot_id'] = id
+ elif ec2_id.startswith('vol-'):
+ bdm['volume_id'] = id
+ ebs.setdefault('delete_on_termination', True)
+ bdm.update(ebs)
+
image = self._get_image(context, kwargs['image_id'])
if image:
@@ -933,37 +952,54 @@ class CloudController(object):
user_data=kwargs.get('user_data'),
security_group=kwargs.get('security_group'),
availability_zone=kwargs.get('placement', {}).get(
- 'AvailabilityZone'))
+ 'AvailabilityZone'),
+ block_device_mapping=kwargs.get('block_device_mapping', {}))
return self._format_run_instances(context,
instances[0]['reservation_id'])
+ def _do_instance(self, action, context, ec2_id):
+ instance_id = ec2utils.ec2_id_to_id(ec2_id)
+ action(context, instance_id=instance_id)
+
+ def _do_instances(self, action, context, instance_id):
+ for ec2_id in instance_id:
+ self._do_instance(action, context, ec2_id)
+
def terminate_instances(self, context, instance_id, **kwargs):
"""Terminate each instance in instance_id, which is a list of ec2 ids.
instance_id is a kwarg so its name cannot be modified."""
LOG.debug(_("Going to start terminating instances"))
- for ec2_id in instance_id:
- instance_id = ec2utils.ec2_id_to_id(ec2_id)
- self.compute_api.delete(context, instance_id=instance_id)
+ self._do_instances(self.compute_api.delete, context, instance_id)
return True
def reboot_instances(self, context, instance_id, **kwargs):
"""instance_id is a list of instance ids"""
LOG.audit(_("Reboot instance %r"), instance_id, context=context)
- for ec2_id in instance_id:
- instance_id = ec2utils.ec2_id_to_id(ec2_id)
- self.compute_api.reboot(context, instance_id=instance_id)
+ self._do_instances(self.compute_api.reboot, context, instance_id)
+ return True
+
+ def stop_instances(self, context, instance_id, **kwargs):
+ """Stop each instances in instance_id.
+ Here instance_id is a list of instance ids"""
+ LOG.debug(_("Going to stop instances"))
+ self._do_instances(self.compute_api.stop, context, instance_id)
+ return True
+
+ def start_instances(self, context, instance_id, **kwargs):
+ """Start each instances in instance_id.
+ Here instance_id is a list of instance ids"""
+ LOG.debug(_("Going to start instances"))
+ self._do_instances(self.compute_api.start, context, instance_id)
return True
def rescue_instance(self, context, instance_id, **kwargs):
"""This is an extension to the normal ec2_api"""
- instance_id = ec2utils.ec2_id_to_id(instance_id)
- self.compute_api.rescue(context, instance_id=instance_id)
+ self._do_instance(self.compute_api.rescue, contect, instnace_id)
return True
def unrescue_instance(self, context, instance_id, **kwargs):
"""This is an extension to the normal ec2_api"""
- instance_id = ec2utils.ec2_id_to_id(instance_id)
- self.compute_api.unrescue(context, instance_id=instance_id)
+ self._do_instance(self.compute_api.unrescue, context, instance_id)
return True
def update_instance(self, context, instance_id, **kwargs):
diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py
index 163aa4ed2..222e1de1e 100644
--- a/nova/api/ec2/ec2utils.py
+++ b/nova/api/ec2/ec2utils.py
@@ -16,6 +16,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import re
+
from nova import exception
@@ -30,3 +32,95 @@ def ec2_id_to_id(ec2_id):
def id_to_ec2_id(instance_id, template='i-%08x'):
"""Convert an instance ID (int) to an ec2 ID (i-[base 16 number])"""
return template % instance_id
+
+
+_c2u = re.compile('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))')
+
+
+def camelcase_to_underscore(str):
+ return _c2u.sub(r'_\1', str).lower().strip('_')
+
+
+def _try_convert(value):
+ """Return a non-string from a string or unicode, if possible.
+
+ ============= =====================================================
+ When value is returns
+ ============= =====================================================
+ zero-length ''
+ 'None' None
+ 'True' True case insensitive
+ 'False' False case insensitive
+ '0', '-0' 0
+ 0xN, -0xN int from hex (postitive) (N is any number)
+ 0bN, -0bN int from binary (positive) (N is any number)
+ * try conversion to int, float, complex, fallback value
+
+ """
+ if len(value) == 0:
+ return ''
+ if value == 'None':
+ return None
+ lowered_value = value.lower()
+ if lowered_value == 'true':
+ return True
+ if lowered_value == 'false':
+ return False
+ valueneg = value[1:] if value[0] == '-' else value
+ if valueneg == '0':
+ return 0
+ if valueneg == '':
+ return value
+ if valueneg[0] == '0':
+ if valueneg[1] in 'xX':
+ return int(value, 16)
+ elif valueneg[1] in 'bB':
+ return int(value, 2)
+ else:
+ try:
+ return int(value, 8)
+ except ValueError:
+ pass
+ try:
+ return int(value)
+ except ValueError:
+ pass
+ try:
+ return float(value)
+ except ValueError:
+ pass
+ try:
+ return complex(value)
+ except ValueError:
+ return value
+
+
+def dict_from_dotted_str(items):
+ """parse multi dot-separated argument into dict.
+ EBS boot uses multi dot-separeted arguments like
+ BlockDeviceMapping.1.DeviceName=snap-id
+ Convert the above into
+ {'block_device_mapping': {'1': {'device_name': snap-id}}}
+ """
+ args = {}
+ for key, value in items:
+ parts = key.split(".")
+ key = camelcase_to_underscore(parts[0])
+ if isinstance(value, str) or isinstance(value, unicode):
+ # NOTE(vish): Automatically convert strings back
+ # into their respective values
+ value = _try_convert(value)
+
+ if len(parts) > 1:
+ d = args.get(key, {})
+ args[key] = d
+ for k in parts[1:-1]:
+ k = camelcase_to_underscore(k)
+ v = d.get(k, {})
+ d[k] = v
+ d = v
+ d[camelcase_to_underscore(parts[-1])] = value
+ else:
+ args[key] = value
+
+ return args