2013-02-21 04:19:43 +00:00
|
|
|
from collections import defaultdict
|
|
|
|
|
2013-02-28 05:08:35 +00:00
|
|
|
from boto.ec2.instance import Instance as BotoInstance, Reservation
|
2013-02-18 21:09:40 +00:00
|
|
|
|
|
|
|
from moto.core import BaseBackend
|
2013-02-23 23:01:41 +00:00
|
|
|
from .utils import (
|
|
|
|
random_ami_id,
|
|
|
|
random_instance_id,
|
|
|
|
random_reservation_id,
|
|
|
|
random_security_group_id,
|
|
|
|
random_snapshot_id,
|
2013-03-06 03:53:53 +00:00
|
|
|
random_subnet_id,
|
2013-02-23 23:01:41 +00:00
|
|
|
random_volume_id,
|
2013-03-06 03:33:41 +00:00
|
|
|
random_vpc_id,
|
2013-02-23 23:01:41 +00:00
|
|
|
)
|
2013-02-18 21:09:40 +00:00
|
|
|
|
|
|
|
|
2013-02-28 05:08:35 +00:00
|
|
|
class Instance(BotoInstance):
|
|
|
|
def __init__(self):
|
|
|
|
self._state_name = None
|
|
|
|
self._state_code = None
|
|
|
|
super(Instance, self).__init__()
|
|
|
|
|
|
|
|
|
2013-02-21 04:19:43 +00:00
|
|
|
class InstanceBackend(object):
|
2013-02-18 21:09:40 +00:00
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.reservations = {}
|
2013-02-21 04:19:43 +00:00
|
|
|
super(InstanceBackend, self).__init__()
|
2013-02-18 21:09:40 +00:00
|
|
|
|
2013-02-19 04:06:23 +00:00
|
|
|
def get_instance(self, instance_id):
|
|
|
|
for instance in self.all_instances():
|
|
|
|
if instance.id == instance_id:
|
|
|
|
return instance
|
2013-02-18 21:09:40 +00:00
|
|
|
|
2013-03-05 13:14:43 +00:00
|
|
|
def add_instances(self, image_id, count):
|
2013-02-18 21:09:40 +00:00
|
|
|
new_reservation = Reservation()
|
|
|
|
new_reservation.id = random_reservation_id()
|
2013-02-19 04:06:23 +00:00
|
|
|
for index in range(count):
|
|
|
|
new_instance = Instance()
|
|
|
|
new_instance.id = random_instance_id()
|
2013-03-05 13:14:43 +00:00
|
|
|
new_instance.image_id = image_id
|
2013-02-28 05:08:35 +00:00
|
|
|
new_instance._state_name = "pending"
|
|
|
|
new_instance._state_code = 0
|
2013-02-19 04:06:23 +00:00
|
|
|
new_reservation.instances.append(new_instance)
|
2013-02-18 21:09:40 +00:00
|
|
|
self.reservations[new_reservation.id] = new_reservation
|
|
|
|
return new_reservation
|
|
|
|
|
2013-02-19 02:56:22 +00:00
|
|
|
def start_instances(self, instance_ids):
|
|
|
|
started_instances = []
|
|
|
|
for instance in self.all_instances():
|
|
|
|
if instance.id in instance_ids:
|
2013-02-28 05:08:35 +00:00
|
|
|
instance._state_name = "pending"
|
|
|
|
instance._state_code = 0
|
2013-02-19 02:56:22 +00:00
|
|
|
started_instances.append(instance)
|
|
|
|
|
|
|
|
return started_instances
|
|
|
|
|
|
|
|
def stop_instances(self, instance_ids):
|
|
|
|
stopped_instances = []
|
|
|
|
for instance in self.all_instances():
|
|
|
|
if instance.id in instance_ids:
|
2013-02-28 05:08:35 +00:00
|
|
|
instance._state_name = "stopping"
|
|
|
|
instance._state_code = 64
|
2013-02-19 02:56:22 +00:00
|
|
|
stopped_instances.append(instance)
|
|
|
|
|
|
|
|
return stopped_instances
|
|
|
|
|
2013-02-18 21:09:40 +00:00
|
|
|
def terminate_instances(self, instance_ids):
|
|
|
|
terminated_instances = []
|
|
|
|
for instance in self.all_instances():
|
|
|
|
if instance.id in instance_ids:
|
2013-02-28 05:08:35 +00:00
|
|
|
instance._state_name = "shutting-down"
|
|
|
|
instance._state_code = 32
|
2013-02-18 21:09:40 +00:00
|
|
|
terminated_instances.append(instance)
|
|
|
|
|
|
|
|
return terminated_instances
|
|
|
|
|
2013-02-20 04:55:01 +00:00
|
|
|
def reboot_instances(self, instance_ids):
|
|
|
|
rebooted_instances = []
|
|
|
|
for instance in self.all_instances():
|
|
|
|
if instance.id in instance_ids:
|
|
|
|
# TODO double check instances go to pending when reboot
|
2013-02-28 05:08:35 +00:00
|
|
|
instance._state_name = "pending"
|
|
|
|
instance._state_code = 0
|
2013-02-20 04:55:01 +00:00
|
|
|
rebooted_instances.append(instance)
|
|
|
|
|
|
|
|
return rebooted_instances
|
|
|
|
|
2013-02-23 19:22:09 +00:00
|
|
|
def modify_instance_attribute(self, instance_id, key, value):
|
|
|
|
instance = self.get_instance(instance_id)
|
|
|
|
setattr(instance, key, value)
|
|
|
|
return instance
|
|
|
|
|
|
|
|
def describe_instance_attribute(self, instance_id, key):
|
|
|
|
instance = self.get_instance(instance_id)
|
|
|
|
value = getattr(instance, key)
|
|
|
|
return instance, value
|
|
|
|
|
2013-02-18 21:09:40 +00:00
|
|
|
def all_instances(self):
|
|
|
|
instances = []
|
|
|
|
for reservation in self.all_reservations():
|
|
|
|
for instance in reservation.instances:
|
|
|
|
instances.append(instance)
|
|
|
|
return instances
|
|
|
|
|
|
|
|
def all_reservations(self):
|
|
|
|
return self.reservations.values()
|
|
|
|
|
|
|
|
|
2013-02-21 04:19:43 +00:00
|
|
|
class TagBackend(object):
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.tags = defaultdict(dict)
|
|
|
|
super(TagBackend, self).__init__()
|
|
|
|
|
|
|
|
def create_tag(self, resource_id, key, value):
|
|
|
|
self.tags[resource_id][key] = value
|
|
|
|
return value
|
|
|
|
|
|
|
|
def delete_tag(self, resource_id, key):
|
|
|
|
return self.tags[resource_id].pop(key)
|
|
|
|
|
|
|
|
def describe_tags(self):
|
|
|
|
results = []
|
|
|
|
for resource_id, tags in self.tags.iteritems():
|
|
|
|
ami = 'ami' in resource_id
|
|
|
|
for key, value in tags.iteritems():
|
|
|
|
result = {
|
|
|
|
'resource_id': resource_id,
|
|
|
|
'key': key,
|
|
|
|
'value': value,
|
|
|
|
'resource_type': 'image' if ami else 'instance',
|
|
|
|
}
|
|
|
|
results.append(result)
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
2013-02-23 19:22:09 +00:00
|
|
|
class Ami(object):
|
|
|
|
def __init__(self, ami_id, instance, name, description):
|
|
|
|
self.id = ami_id
|
|
|
|
self.instance = instance
|
|
|
|
self.instance_id = instance.id
|
|
|
|
self.name = name
|
|
|
|
self.description = description
|
|
|
|
|
|
|
|
self.virtualization_type = instance.virtualization_type
|
|
|
|
self.kernel_id = instance.kernel
|
|
|
|
|
2013-02-26 05:31:01 +00:00
|
|
|
|
2013-02-23 19:22:09 +00:00
|
|
|
class AmiBackend(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.amis = {}
|
|
|
|
super(AmiBackend, self).__init__()
|
|
|
|
|
|
|
|
def create_image(self, instance_id, name, description):
|
|
|
|
# TODO: check that instance exists and pull info from it.
|
|
|
|
ami_id = random_ami_id()
|
2013-02-23 20:26:54 +00:00
|
|
|
instance = self.get_instance(instance_id)
|
2013-02-23 19:22:09 +00:00
|
|
|
if not instance:
|
|
|
|
return None
|
2013-02-26 05:31:01 +00:00
|
|
|
ami = Ami(ami_id, instance, name, description)
|
2013-02-23 19:22:09 +00:00
|
|
|
self.amis[ami_id] = ami
|
|
|
|
return ami
|
|
|
|
|
|
|
|
def describe_images(self):
|
|
|
|
return self.amis.values()
|
|
|
|
|
|
|
|
def deregister_image(self, ami_id):
|
|
|
|
if ami_id in self.amis:
|
|
|
|
self.amis.pop(ami_id)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2013-02-23 19:51:19 +00:00
|
|
|
|
|
|
|
class Region(object):
|
|
|
|
def __init__(self, name, endpoint):
|
|
|
|
self.name = name
|
|
|
|
self.endpoint = endpoint
|
|
|
|
|
|
|
|
|
|
|
|
class Zone(object):
|
|
|
|
def __init__(self, name, region_name):
|
|
|
|
self.name = name
|
|
|
|
self.region_name = region_name
|
|
|
|
|
|
|
|
|
|
|
|
class RegionsAndZonesBackend(object):
|
|
|
|
regions = [
|
|
|
|
Region("eu-west-1", "ec2.eu-west-1.amazonaws.com"),
|
|
|
|
Region("sa-east-1", "ec2.sa-east-1.amazonaws.com"),
|
|
|
|
Region("us-east-1", "ec2.us-east-1.amazonaws.com"),
|
|
|
|
Region("ap-northeast-1", "ec2.ap-northeast-1.amazonaws.com"),
|
|
|
|
Region("us-west-2", "ec2.us-west-2.amazonaws.com"),
|
|
|
|
Region("us-west-1", "ec2.us-west-1.amazonaws.com"),
|
|
|
|
Region("ap-southeast-1", "ec2.ap-southeast-1.amazonaws.com"),
|
|
|
|
Region("ap-southeast-2", "ec2.ap-southeast-2.amazonaws.com"),
|
|
|
|
]
|
|
|
|
|
|
|
|
# TODO: cleanup. For now, pretend everything is us-east-1. 'merica.
|
|
|
|
zones = [
|
|
|
|
Zone("us-east-1a", "us-east-1"),
|
|
|
|
Zone("us-east-1b", "us-east-1"),
|
|
|
|
Zone("us-east-1c", "us-east-1"),
|
|
|
|
Zone("us-east-1d", "us-east-1"),
|
|
|
|
Zone("us-east-1e", "us-east-1"),
|
|
|
|
]
|
|
|
|
|
|
|
|
def describe_regions(self):
|
|
|
|
return self.regions
|
|
|
|
|
|
|
|
def describe_availability_zones(self):
|
|
|
|
return self.zones
|
|
|
|
|
2013-02-23 22:37:55 +00:00
|
|
|
def get_zone_by_name(self, name):
|
|
|
|
for zone in self.zones:
|
|
|
|
if zone.name == name:
|
|
|
|
return zone
|
|
|
|
|
2013-02-23 19:51:19 +00:00
|
|
|
|
2013-02-23 21:27:43 +00:00
|
|
|
class SecurityRule(object):
|
|
|
|
def __init__(self, ip_protocol, from_port, to_port, ip_ranges, source_groups):
|
|
|
|
self.ip_protocol = ip_protocol
|
|
|
|
self.from_port = from_port
|
|
|
|
self.to_port = to_port
|
|
|
|
self.ip_ranges = ip_ranges or []
|
|
|
|
self.source_groups = source_groups
|
|
|
|
|
|
|
|
@property
|
|
|
|
def unique_representation(self):
|
|
|
|
return "{}-{}-{}-{}-{}".format(
|
2013-03-05 13:14:43 +00:00
|
|
|
self.ip_protocol,
|
|
|
|
self.from_port,
|
|
|
|
self.to_port,
|
|
|
|
self.ip_ranges,
|
|
|
|
self.source_groups
|
2013-02-26 05:31:01 +00:00
|
|
|
)
|
2013-02-23 21:27:43 +00:00
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return self.unique_representation == other.unique_representation
|
|
|
|
|
|
|
|
|
2013-02-23 20:26:54 +00:00
|
|
|
class SecurityGroup(object):
|
|
|
|
def __init__(self, group_id, name, description):
|
|
|
|
self.id = group_id
|
|
|
|
self.name = name
|
|
|
|
self.description = description
|
2013-02-23 21:27:43 +00:00
|
|
|
self.ingress_rules = []
|
|
|
|
self.egress_rules = []
|
2013-02-23 20:26:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
class SecurityGroupBackend(object):
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.groups = {}
|
2013-02-23 22:37:55 +00:00
|
|
|
super(SecurityGroupBackend, self).__init__()
|
2013-02-23 20:26:54 +00:00
|
|
|
|
|
|
|
def create_security_group(self, name, description):
|
|
|
|
group_id = random_security_group_id()
|
|
|
|
existing_group = self.get_security_group_from_name(name)
|
|
|
|
if existing_group:
|
|
|
|
return None
|
2013-02-26 05:31:01 +00:00
|
|
|
group = SecurityGroup(group_id, name, description)
|
2013-02-23 20:26:54 +00:00
|
|
|
self.groups[group_id] = group
|
|
|
|
return group
|
|
|
|
|
|
|
|
def describe_security_groups(self):
|
|
|
|
return self.groups.values()
|
|
|
|
|
|
|
|
def delete_security_group(self, name_or_group_id):
|
|
|
|
if name_or_group_id in self.groups:
|
|
|
|
# Group Id
|
|
|
|
return self.groups.pop(name_or_group_id)
|
|
|
|
else:
|
|
|
|
# Group Name
|
|
|
|
group = self.get_security_group_from_name(name_or_group_id)
|
|
|
|
if group:
|
|
|
|
return self.groups.pop(group.id)
|
|
|
|
|
|
|
|
def get_security_group_from_name(self, name):
|
|
|
|
for group_id, group in self.groups.iteritems():
|
|
|
|
if group.name == name:
|
|
|
|
return group
|
|
|
|
|
2013-02-23 21:27:43 +00:00
|
|
|
def authorize_security_group_ingress(self, group_name, ip_protocol, from_port, to_port, ip_ranges=None, source_group_names=None):
|
|
|
|
group = self.get_security_group_from_name(group_name)
|
|
|
|
source_groups = []
|
|
|
|
for source_group_name in source_group_names:
|
|
|
|
source_groups.append(self.get_security_group_from_name(source_group_name))
|
|
|
|
|
|
|
|
security_rule = SecurityRule(ip_protocol, from_port, to_port, ip_ranges, source_groups)
|
|
|
|
group.ingress_rules.append(security_rule)
|
|
|
|
|
|
|
|
def revoke_security_group_ingress(self, group_name, ip_protocol, from_port, to_port, ip_ranges=None, source_group_names=None):
|
|
|
|
group = self.get_security_group_from_name(group_name)
|
|
|
|
source_groups = []
|
|
|
|
for source_group_name in source_group_names:
|
|
|
|
source_groups.append(self.get_security_group_from_name(source_group_name))
|
|
|
|
|
|
|
|
security_rule = SecurityRule(ip_protocol, from_port, to_port, ip_ranges, source_groups)
|
|
|
|
if security_rule in group.ingress_rules:
|
|
|
|
group.ingress_rules.remove(security_rule)
|
|
|
|
return security_rule
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2013-02-23 22:37:55 +00:00
|
|
|
class VolumeAttachment(object):
|
|
|
|
def __init__(self, volume, instance, device):
|
|
|
|
self.volume = volume
|
|
|
|
self.instance = instance
|
|
|
|
self.device = device
|
|
|
|
|
|
|
|
|
|
|
|
class Volume(object):
|
|
|
|
def __init__(self, volume_id, size, zone):
|
|
|
|
self.id = volume_id
|
|
|
|
self.size = size
|
|
|
|
self.zone = zone
|
|
|
|
self.attachment = None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def status(self):
|
|
|
|
if self.attachment:
|
|
|
|
return 'in-use'
|
|
|
|
else:
|
|
|
|
return 'available'
|
|
|
|
|
|
|
|
|
2013-02-23 23:01:41 +00:00
|
|
|
class Snapshot(object):
|
|
|
|
def __init__(self, snapshot_id, volume, description):
|
|
|
|
self.id = snapshot_id
|
|
|
|
self.volume = volume
|
|
|
|
self.description = description
|
|
|
|
|
|
|
|
|
2013-02-23 22:37:55 +00:00
|
|
|
class EBSBackend(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.volumes = {}
|
|
|
|
self.attachments = {}
|
2013-02-23 23:01:41 +00:00
|
|
|
self.snapshots = {}
|
2013-02-23 22:37:55 +00:00
|
|
|
super(EBSBackend, self).__init__()
|
|
|
|
|
|
|
|
def create_volume(self, size, zone_name):
|
|
|
|
volume_id = random_volume_id()
|
|
|
|
zone = self.get_zone_by_name(zone_name)
|
|
|
|
volume = Volume(volume_id, size, zone)
|
|
|
|
self.volumes[volume_id] = volume
|
|
|
|
return volume
|
|
|
|
|
|
|
|
def describe_volumes(self):
|
|
|
|
return self.volumes.values()
|
|
|
|
|
|
|
|
def delete_volume(self, volume_id):
|
|
|
|
if volume_id in self.volumes:
|
|
|
|
return self.volumes.pop(volume_id)
|
|
|
|
return False
|
|
|
|
|
|
|
|
def attach_volume(self, volume_id, instance_id, device_path):
|
|
|
|
volume = self.volumes.get(volume_id)
|
|
|
|
instance = self.get_instance(instance_id)
|
|
|
|
|
|
|
|
if not volume or not instance:
|
|
|
|
return False
|
|
|
|
|
|
|
|
volume.attachment = VolumeAttachment(volume, instance, device_path)
|
|
|
|
return volume.attachment
|
|
|
|
|
|
|
|
def detach_volume(self, volume_id, instance_id, device_path):
|
|
|
|
volume = self.volumes.get(volume_id)
|
|
|
|
instance = self.get_instance(instance_id)
|
|
|
|
|
|
|
|
if not volume or not instance:
|
|
|
|
return False
|
|
|
|
|
|
|
|
old_attachment = volume.attachment
|
|
|
|
volume.attachment = None
|
|
|
|
return old_attachment
|
|
|
|
|
2013-02-23 23:01:41 +00:00
|
|
|
def create_snapshot(self, volume_id, description):
|
|
|
|
snapshot_id = random_snapshot_id()
|
|
|
|
volume = self.volumes.get(volume_id)
|
|
|
|
snapshot = Snapshot(snapshot_id, volume, description)
|
|
|
|
self.snapshots[snapshot_id] = snapshot
|
|
|
|
return snapshot
|
|
|
|
|
|
|
|
def describe_snapshots(self):
|
|
|
|
return self.snapshots.values()
|
|
|
|
|
|
|
|
def delete_snapshot(self, snapshot_id):
|
|
|
|
if snapshot_id in self.snapshots:
|
|
|
|
return self.snapshots.pop(snapshot_id)
|
|
|
|
return False
|
|
|
|
|
2013-02-23 22:37:55 +00:00
|
|
|
|
2013-03-06 03:33:41 +00:00
|
|
|
class VPC(object):
|
|
|
|
def __init__(self, vpc_id, cidr_block):
|
|
|
|
self.id = vpc_id
|
|
|
|
self.cidr_block = cidr_block
|
|
|
|
|
|
|
|
|
|
|
|
class VPCBackend(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.vpcs = {}
|
|
|
|
super(VPCBackend, self).__init__()
|
|
|
|
|
|
|
|
def create_vpc(self, cidr_block):
|
|
|
|
vpc_id = random_vpc_id()
|
|
|
|
vpc = VPC(vpc_id, cidr_block)
|
|
|
|
self.vpcs[vpc_id] = vpc
|
|
|
|
return vpc
|
|
|
|
|
2013-03-06 03:53:53 +00:00
|
|
|
def get_vpc(self, vpc_id):
|
|
|
|
return self.vpcs.get(vpc_id)
|
|
|
|
|
2013-03-06 03:33:41 +00:00
|
|
|
def get_all_vpcs(self):
|
|
|
|
return self.vpcs.values()
|
|
|
|
|
|
|
|
def delete_vpc(self, vpc_id):
|
|
|
|
return self.vpcs.pop(vpc_id, None)
|
|
|
|
|
|
|
|
|
2013-03-06 03:53:53 +00:00
|
|
|
class Subnet(object):
|
|
|
|
def __init__(self, subnet_id, vpc, cidr_block):
|
|
|
|
self.id = subnet_id
|
|
|
|
self.vpc = vpc
|
|
|
|
self.cidr_block = cidr_block
|
|
|
|
|
|
|
|
|
|
|
|
class SubnetBackend(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.subnets = {}
|
|
|
|
super(SubnetBackend, self).__init__()
|
|
|
|
|
|
|
|
def create_subnet(self, vpc_id, cidr_block):
|
|
|
|
subnet_id = random_subnet_id()
|
|
|
|
vpc = self.get_vpc(vpc_id)
|
|
|
|
subnet = Subnet(subnet_id, vpc, cidr_block)
|
|
|
|
self.subnets[subnet_id] = subnet
|
|
|
|
return subnet
|
|
|
|
|
|
|
|
def get_all_subnets(self):
|
|
|
|
return self.subnets.values()
|
|
|
|
|
|
|
|
def delete_subnet(self, subnet_id):
|
|
|
|
return self.subnets.pop(subnet_id, None)
|
|
|
|
|
|
|
|
|
2013-03-06 03:33:41 +00:00
|
|
|
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
|
|
|
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
2013-03-06 03:53:53 +00:00
|
|
|
VPCBackend, SubnetBackend):
|
2013-02-21 04:19:43 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
ec2_backend = EC2Backend()
|