diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 3f3fc4491..19d005867 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from collections import defaultdict import copy from datetime import datetime + import itertools import re @@ -97,6 +98,9 @@ from .utils import ( ) +def utc_date_and_time(): + return datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') + def validate_resource_ids(resource_ids): for resource_id in resource_ids: if not is_valid_resource_id(resource_id): @@ -309,14 +313,17 @@ class Instance(BotoInstance, TaggedEC2Resource): in_ec2_classic = not bool(self.subnet_id) self.key_name = kwargs.get("key_name") self.source_dest_check = "true" - self.launch_time = datetime.utcnow().isoformat() + self.launch_time = utc_date_and_time() associate_public_ip = kwargs.get("associate_public_ip", False) if in_ec2_classic: # If we are in EC2-Classic, autoassign a public IP associate_public_ip = True self.block_device_mapping = BlockDeviceMapping() - self.block_device_mapping['/dev/sda1'] = BlockDeviceType(volume_id=random_volume_id()) + # Default have an instance with root volume should you not wish to override with attach volume cmd. + # However this is a ghost volume and wont show up in get_all_volumes or snapshot-able. + self.block_device_mapping['/dev/sda1'] = BlockDeviceType(volume_id=random_volume_id(), status='attached', + attach_time=utc_date_and_time()) amis = self.ec2_backend.describe_images(filters={'image-id': image_id}) ami = amis[0] if amis else None @@ -343,6 +350,10 @@ class Instance(BotoInstance, TaggedEC2Resource): private_ip=kwargs.get("private_ip"), associate_public_ip=associate_public_ip) + @property + def get_block_device_mapping(self): + return self.block_device_mapping.items() + @property def private_ip(self): return self.nics[0].private_ip_address @@ -1349,6 +1360,7 @@ class SecurityGroupIngress(object): class VolumeAttachment(object): def __init__(self, volume, instance, device): self.volume = volume + self.attach_time = utc_date_and_time() self.instance = instance self.device = device @@ -1373,6 +1385,7 @@ class Volume(TaggedEC2Resource): self.id = volume_id self.size = size self.zone = zone + self.create_time = utc_date_and_time() self.attachment = None self.ec2_backend = ec2_backend @@ -1404,6 +1417,7 @@ class Snapshot(TaggedEC2Resource): self.id = snapshot_id self.volume = volume self.description = description + self.start_time = utc_date_and_time() self.create_volume_permission_groups = set() self.ec2_backend = ec2_backend @@ -1444,6 +1458,13 @@ class EBSBackend(object): return False volume.attachment = VolumeAttachment(volume, instance, device_path) + # Modify instance to capture mount of block device. + bdt = BlockDeviceType(volume_id=volume_id, status=volume.status, size=volume.size, + attach_time=utc_date_and_time()) + try: + instance.block_device_mapping[device_path] = bdt + except: + instance.block_device_mapping.setdefault(device_path, bdt) return volume.attachment def detach_volume(self, volume_id, instance_id, device_path): diff --git a/moto/ec2/responses/elastic_block_store.py b/moto/ec2/responses/elastic_block_store.py index 96586a9bb..abb371260 100644 --- a/moto/ec2/responses/elastic_block_store.py +++ b/moto/ec2/responses/elastic_block_store.py @@ -42,12 +42,22 @@ class ElasticBlockStore(BaseResponse): return DELETE_VOLUME_RESPONSE def describe_snapshots(self): + # querystring for multiple snapshotids results in SnapshotId.1, SnapshotId.2 etc + snapshot_ids = ','.join([','.join(s[1]) for s in self.querystring.items() if 'SnapshotId' in s[0]]) snapshots = self.ec2_backend.describe_snapshots() + # Describe snapshots to handle filter on snapshot_ids + snapshots = [s for s in snapshots if s.id in snapshot_ids] if snapshot_ids else snapshots + # snapshots = self.ec2_backend.describe_snapshots() template = self.response_template(DESCRIBE_SNAPSHOTS_RESPONSE) return template.render(snapshots=snapshots) def describe_volumes(self): + # querystring for multiple volumeids results in VolumeId.1, VolumeId.2 etc + volume_ids = ','.join([','.join(v[1]) for v in self.querystring.items() if 'VolumeId' in v[0]]) volumes = self.ec2_backend.describe_volumes() + # Describe volumes to handle filter on volume_ids + volumes = [v for v in volumes if v.id in volume_ids] if volume_ids else volumes + # volumes = self.ec2_backend.describe_volumes() template = self.response_template(DESCRIBE_VOLUMES_RESPONSE) return template.render(volumes=volumes) @@ -103,7 +113,7 @@ CREATE_VOLUME_RESPONSE = """ @@ -174,7 +184,7 @@ CREATE_SNAPSHOT_RESPONSE = """ebs /dev/sda1 + {% for device_name,deviceobject in instance.get_block_device_mapping %} - /dev/sda1 + {{ device_name }} - {{ instance.block_device_mapping['/dev/sda1'].volume_id }} - attached - 2015-01-01T00:00:00.000Z - true + {{ deviceobject.volume_id }} + {{ deviceobject.status }} + {{ deviceobject.attach_time }} + {{ deviceobject.delete_on_termination }} + {{deviceobject.size}} + {% endfor %} {{ instance.virtualization_type }} ABCDE1234567890123 @@ -547,7 +550,7 @@ EC2_INSTANCE_STATUS = """ {% for instance in instances %} {{ instance.id }} - us-east-1d + {{ instance.placement }} {{ instance.state_code }} {{ instance.state }}