28
28
from nova.api.ec2 import ec2utils
29
29
from nova.api.ec2 import inst_state
30
30
from nova.api import validator
31
from nova import availability_zones
31
32
from nova import block_device
32
33
from nova import compute
33
34
from nova.compute import api as compute_api
41
42
from nova.openstack.common import log as logging
42
43
from nova.openstack.common import timeutils
43
44
from nova import quota
45
from nova import servicegroup
44
46
from nova import utils
45
47
from nova import volume
50
cfg.StrOpt('ec2_host',
52
help='the ip of the ec2 api server'),
53
cfg.StrOpt('ec2_dmz_host',
55
help='the internal ip of the ec2 api server'),
56
cfg.IntOpt('ec2_port',
58
help='the port of the ec2 api server'),
59
cfg.StrOpt('ec2_scheme',
61
help='the protocol to use when connecting to the ec2 api '
62
'server (http, https)'),
63
cfg.StrOpt('ec2_path',
64
default='/services/Cloud',
65
help='the path prefix used to call the ec2 api server'),
66
cfg.ListOpt('region_list',
68
help='list of region=fqdn pairs separated by commas'),
49
CONF.import_opt('ec2_host', 'nova.config')
50
CONF.import_opt('ec2_path', 'nova.config')
51
CONF.import_opt('ec2_port', 'nova.config')
52
CONF.import_opt('ec2_scheme', 'nova.config')
53
CONF.import_opt('region_list', 'nova.config')
72
CONF.register_opts(ec2_opts)
73
CONF.import_opt('my_ip', 'nova.config')
54
74
CONF.import_opt('vpn_image_id', 'nova.config')
55
75
CONF.import_opt('vpn_key_suffix', 'nova.config')
76
CONF.import_opt('internal_service_availability_zone',
77
'nova.availability_zones')
57
79
LOG = logging.getLogger(__name__)
62
84
def validate_ec2_id(val):
63
85
if not validator.validate_str()(val):
64
raise exception.InvalidInstanceIDMalformed(val)
86
raise exception.InvalidInstanceIDMalformed(val=val)
66
88
ec2utils.ec2_id_to_id(val)
67
89
except exception.InvalidEc2Id:
68
raise exception.InvalidInstanceIDMalformed(val)
90
raise exception.InvalidInstanceIDMalformed(val=val)
71
93
# EC2 API can return the following values as documented in the EC2 API
113
135
ec2_id = ebs.pop('snapshot_id', None)
115
id = ec2utils.ec2_vol_id_to_uuid(ec2_id)
116
137
if ec2_id.startswith('snap-'):
117
bdm['snapshot_id'] = id
138
bdm['snapshot_id'] = ec2utils.ec2_snap_id_to_uuid(ec2_id)
118
139
elif ec2_id.startswith('vol-'):
119
bdm['volume_id'] = id
140
bdm['volume_id'] = ec2utils.ec2_vol_id_to_uuid(ec2_id)
120
141
ebs.setdefault('delete_on_termination', True)
196
217
volume_api=self.volume_api,
197
218
security_group_api=self.security_group_api)
198
219
self.keypair_api = compute_api.KeypairAPI()
220
self.servicegroup_api = servicegroup.API()
200
222
def __str__(self):
201
223
return 'CloudController'
225
def _enforce_valid_instance_ids(self, context, instance_ids):
226
# NOTE(mikal): Amazon's implementation of the EC2 API requires that
227
# _all_ instance ids passed in be valid.
230
for ec2_id in instance_ids:
231
instance_uuid = ec2utils.ec2_inst_id_to_uuid(context, ec2_id)
232
instance = self.compute_api.get(context, instance_uuid)
233
instances[ec2_id] = instance
203
236
def _get_image_state(self, image):
204
237
# NOTE(vish): fallback status if image_state isn't set
205
238
state = image.get('status')
220
253
"""Return available and unavailable zones."""
221
254
enabled_services = db.service_get_all(context, False)
222
255
disabled_services = db.service_get_all(context, True)
256
enabled_services = availability_zones.set_availability_zones(context,
258
disabled_services = availability_zones.set_availability_zones(context,
224
261
available_zones = []
225
for zone in [service.availability_zone for service
262
for zone in [service['availability_zone'] for service
226
263
in enabled_services]:
227
264
if not zone in available_zones:
228
265
available_zones.append(zone)
230
267
not_available_zones = []
231
for zone in [service.availability_zone for service in disabled_services
232
if not service['availability_zone'] in available_zones]:
233
if not zone in not_available_zones:
268
zones = [service['available_zones'] for service in disabled_services
269
if service['available_zones'] not in available_zones]
271
if zone not in not_available_zones:
234
272
not_available_zones.append(zone)
236
273
return (available_zones, not_available_zones)
238
275
def _describe_availability_zones(self, context, **kwargs):
243
280
for zone in available_zones:
281
# Hide internal_service_availability_zone
282
if zone == CONF.internal_service_availability_zone:
244
284
result.append({'zoneName': zone,
245
285
'zoneState': "available"})
246
286
for zone in not_available_zones:
255
295
# Available services
256
296
enabled_services = db.service_get_all(context, False)
297
enabled_services = availability_zones.set_availability_zones(context,
258
300
host_services = {}
259
301
for service in enabled_services:
260
zone_hosts.setdefault(service.availability_zone, [])
261
if not service.host in zone_hosts[service.availability_zone]:
262
zone_hosts[service.availability_zone].append(service.host)
302
zone_hosts.setdefault(service['availability_zone'], [])
303
if not service['host'] in zone_hosts[service['availability_zone']]:
304
zone_hosts[service['availability_zone']].append(
264
host_services.setdefault(service.host, [])
265
host_services[service.host].append(service)
307
host_services.setdefault(service['availability_zone'] +
309
host_services[service['availability_zone'] + service['host']].\
268
313
for zone in available_zones:
272
317
result.append({'zoneName': '|- %s' % host,
273
318
'zoneState': ''})
275
for service in host_services[host]:
276
alive = utils.service_is_up(service)
320
for service in host_services[zone + host]:
321
alive = self.servicegroup_api.service_is_up(service)
277
322
art = (alive and ":-)") or "XXX"
278
323
active = 'enabled'
279
324
if service['disabled']:
324
369
snapshots.append(snapshot)
326
371
snapshots = self.volume_api.get_all_snapshots(context)
327
snapshots = [self._format_snapshot(context, s) for s in snapshots]
328
return {'snapshotSet': snapshots}
373
formatted_snapshots = []
375
formatted = self._format_snapshot(context, s)
377
formatted_snapshots.append(formatted)
378
return {'snapshotSet': formatted_snapshots}
330
380
def _format_snapshot(self, context, snapshot):
381
# NOTE(mikal): this is just a set of strings in cinder. If they
382
# implement an enum, then we should move this code to use it. The
383
# valid ec2 statuses are "pending", "completed", and "error".
384
status_map = {'new': 'pending',
385
'creating': 'pending',
386
'available': 'completed',
387
'active': 'completed',
388
'deleting': 'pending',
392
mapped_status = status_map.get(snapshot['status'], snapshot['status'])
393
if not mapped_status:
332
397
s['snapshotId'] = ec2utils.id_to_ec2_snap_id(snapshot['id'])
333
398
s['volumeId'] = ec2utils.id_to_ec2_vol_id(snapshot['volume_id'])
334
s['status'] = snapshot['status']
399
s['status'] = mapped_status
335
400
s['startTime'] = snapshot['created_at']
336
401
s['progress'] = snapshot['progress']
337
402
s['ownerId'] = snapshot['project_id']
456
521
def _format_security_group(self, context, group):
458
g['groupDescription'] = group.description
459
g['groupName'] = group.name
460
g['ownerId'] = group.project_id
523
g['groupDescription'] = group['description']
524
g['groupName'] = group['name']
525
g['ownerId'] = group['project_id']
461
526
g['ipPermissions'] = []
462
for rule in group.rules:
527
for rule in group['rules']:
465
530
r['ipRanges'] = []
467
source_group = rule.grantee_group
468
r['groups'] += [{'groupName': source_group.name,
469
'userId': source_group.project_id}]
471
r['ipProtocol'] = rule.protocol.lower()
472
r['fromPort'] = rule.from_port
473
r['toPort'] = rule.to_port
532
source_group = rule['grantee_group']
533
r['groups'] += [{'groupName': source_group['name'],
534
'userId': source_group['project_id']}]
536
r['ipProtocol'] = rule['protocol'].lower()
537
r['fromPort'] = rule['from_port']
538
r['toPort'] = rule['to_port']
474
539
g['ipPermissions'] += [dict(r)]
476
541
for protocol, min_port, max_port in (('icmp', -1, -1),
481
546
r['toPort'] = max_port
482
547
g['ipPermissions'] += [dict(r)]
484
r['ipProtocol'] = rule.protocol
485
r['fromPort'] = rule.from_port
486
r['toPort'] = rule.to_port
487
r['ipRanges'] += [{'cidrIp': rule.cidr}]
549
r['ipProtocol'] = rule['protocol']
550
r['fromPort'] = rule['from_port']
551
r['toPort'] = rule['to_port']
552
r['ipRanges'] += [{'cidrIp': rule['cidr']}]
488
553
g['ipPermissions'] += [r]
592
657
rulesvalues = self._rule_args_to_dict(context, values)
593
658
self._validate_rulevalues(rulesvalues)
594
659
for values_for_rule in rulesvalues:
595
values_for_rule['parent_group_id'] = security_group.id
660
values_for_rule['parent_group_id'] = security_group['id']
597
662
rule_ids.append(self.security_group_api.rule_exists(
598
663
security_group, values_for_rule))
625
690
rulesvalues = self._rule_args_to_dict(context, values)
626
691
self._validate_rulevalues(rulesvalues)
627
692
for values_for_rule in rulesvalues:
628
values_for_rule['parent_group_id'] = security_group.id
693
values_for_rule['parent_group_id'] = security_group['id']
629
694
if self.security_group_api.rule_exists(security_group,
630
695
values_for_rule):
631
696
err = _('%s - This rule already exists in group')
937
1002
def describe_instances(self, context, **kwargs):
938
1003
# Optional DescribeInstances argument
939
1004
instance_id = kwargs.get('instance_id', None)
1005
instances = self._enforce_valid_instance_ids(context, instance_id)
940
1006
return self._format_describe_instances(context,
941
instance_id=instance_id)
1007
instance_id=instance_id,
1008
instance_cache=instances)
943
1010
def describe_instances_v6(self, context, **kwargs):
944
1011
# Optional DescribeInstancesV6 argument
945
1012
instance_id = kwargs.get('instance_id', None)
1013
instances = self._enforce_valid_instance_ids(context, instance_id)
946
1014
return self._format_describe_instances(context,
947
instance_id=instance_id, use_v6=True)
1015
instance_id=instance_id,
1016
instance_cache=instances,
949
1019
def _format_describe_instances(self, context, **kwargs):
950
1020
return {'reservationSet': self._format_instances(context, **kwargs)}
1026
1096
security_group_names, 'groupId')
1028
1098
def _format_instances(self, context, instance_id=None, use_v6=False,
1099
instances_cache=None, **search_opts):
1030
1100
# TODO(termie): this method is poorly named as its name does not imply
1031
1101
# that it will be making a variety of database calls
1032
1102
# rather than simply formatting a bunch of instances that
1033
1103
# were handed to it
1034
1104
reservations = {}
1106
if not instances_cache:
1107
instances_cache = {}
1035
1109
# NOTE(vish): instance_id is an optional list of ids to filter by
1036
1110
if instance_id:
1038
1112
for ec2_id in instance_id:
1040
instance_uuid = ec2utils.ec2_inst_id_to_uuid(context,
1042
instance = self.compute_api.get(context, instance_uuid)
1043
except exception.NotFound:
1045
instances.append(instance)
1113
if ec2_id in instances_cache:
1114
instances.append(instances_cache[ec2_id])
1117
instance_uuid = ec2utils.ec2_inst_id_to_uuid(context,
1119
instance = self.compute_api.get(context, instance_uuid)
1120
except exception.NotFound:
1122
instances.append(instance)
1048
1125
# always filter out deleted instances