~ubuntu-branches/ubuntu/saucy/nova/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/CVE-2012-2101.patch/nova/api/ec2/cloud.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-05-24 13:12:53 UTC
  • mfrom: (1.1.55)
  • Revision ID: package-import@ubuntu.com-20120524131253-ommql08fg1en06ut
Tags: 2012.2~f1-0ubuntu1
* New upstream release.
* Prepare for quantal:
  - Dropped debian/patches/upstream/0006-Use-project_id-in-ec2.cloud._format_image.patch
  - Dropped debian/patches/upstream/0005-Populate-image-properties-with-project_id-again.patch
  - Dropped debian/patches/upstream/0004-Fixed-bug-962840-added-a-test-case.patch
  - Dropped debian/patches/upstream/0003-Allow-unprivileged-RADOS-users-to-access-rbd-volumes.patch
  - Dropped debian/patches/upstream/0002-Stop-libvirt-test-from-deleting-instances-dir.patch
  - Dropped debian/patches/upstream/0001-fix-bug-where-nova-ignores-glance-host-in-imageref.patch 
  - Dropped debian/patches/0001-fix-useexisting-deprecation-warnings.patch
* debian/control: Add python-keystone as a dependency. (LP: #907197)
* debian/patches/kombu_tests_timeout.patch: Refreshed.
* debian/nova.conf, debian/nova-common.postinst: Convert to new ini
  file configuration
* debian/patches/nova-manage_flagfile_location.patch: Refreshed

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
 
 
3
 
# Copyright 2010 United States Government as represented by the
4
 
# Administrator of the National Aeronautics and Space Administration.
5
 
# All Rights Reserved.
6
 
#
7
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
8
 
#    not use this file except in compliance with the License. You may obtain
9
 
#    a copy of the License at
10
 
#
11
 
#         http://www.apache.org/licenses/LICENSE-2.0
12
 
#
13
 
#    Unless required by applicable law or agreed to in writing, software
14
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
 
#    License for the specific language governing permissions and limitations
17
 
#    under the License.
18
 
 
19
 
"""
20
 
Cloud Controller: Implementation of EC2 REST API calls, which are
21
 
dispatched to other nodes via AMQP RPC. State is via distributed
22
 
datastore.
23
 
"""
24
 
 
25
 
import base64
26
 
import re
27
 
import time
28
 
import urllib
29
 
 
30
 
from nova.api.ec2 import ec2utils
31
 
from nova.api.ec2 import inst_state
32
 
from nova.api import validator
33
 
from nova import block_device
34
 
from nova import compute
35
 
from nova.compute import instance_types
36
 
from nova.compute import vm_states
37
 
from nova import crypto
38
 
from nova import db
39
 
from nova import exception
40
 
from nova import flags
41
 
from nova.image import s3
42
 
from nova import log as logging
43
 
from nova import network
44
 
from nova.rpc import common as rpc_common
45
 
from nova import utils
46
 
from nova import volume
47
 
 
48
 
 
49
 
FLAGS = flags.FLAGS
50
 
flags.DECLARE('dhcp_domain', 'nova.network.manager')
51
 
 
52
 
LOG = logging.getLogger(__name__)
53
 
 
54
 
 
55
 
def validate_ec2_id(val):
56
 
    if not validator.validate_str()(val):
57
 
        raise exception.InvalidInstanceIDMalformed(val)
58
 
    try:
59
 
        ec2utils.ec2_id_to_id(val)
60
 
    except exception.InvalidEc2Id:
61
 
        raise exception.InvalidInstanceIDMalformed(val)
62
 
 
63
 
 
64
 
def _gen_key(context, user_id, key_name):
65
 
    """Generate a key
66
 
 
67
 
    This is a module level method because it is slow and we need to defer
68
 
    it into a process pool."""
69
 
    # NOTE(vish): generating key pair is slow so check for legal
70
 
    #             creation before creating key_pair
71
 
    try:
72
 
        db.key_pair_get(context, user_id, key_name)
73
 
        raise exception.KeyPairExists(key_name=key_name)
74
 
    except exception.NotFound:
75
 
        pass
76
 
    private_key, public_key, fingerprint = crypto.generate_key_pair()
77
 
    key = {}
78
 
    key['user_id'] = user_id
79
 
    key['name'] = key_name
80
 
    key['public_key'] = public_key
81
 
    key['fingerprint'] = fingerprint
82
 
    db.key_pair_create(context, key)
83
 
    return {'private_key': private_key, 'fingerprint': fingerprint}
84
 
 
85
 
 
86
 
# EC2 API can return the following values as documented in the EC2 API
87
 
# http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/
88
 
#    ApiReference-ItemType-InstanceStateType.html
89
 
# pending 0 | running 16 | shutting-down 32 | terminated 48 | stopping 64 |
90
 
# stopped 80
91
 
_STATE_DESCRIPTION_MAP = {
92
 
    None: inst_state.PENDING,
93
 
    vm_states.ACTIVE: inst_state.RUNNING,
94
 
    vm_states.BUILDING: inst_state.PENDING,
95
 
    vm_states.REBUILDING: inst_state.PENDING,
96
 
    vm_states.DELETED: inst_state.TERMINATED,
97
 
    vm_states.SOFT_DELETE: inst_state.TERMINATED,
98
 
    vm_states.STOPPED: inst_state.STOPPED,
99
 
    vm_states.SHUTOFF: inst_state.SHUTOFF,
100
 
    vm_states.MIGRATING: inst_state.MIGRATE,
101
 
    vm_states.RESIZING: inst_state.RESIZE,
102
 
    vm_states.PAUSED: inst_state.PAUSE,
103
 
    vm_states.SUSPENDED: inst_state.SUSPEND,
104
 
    vm_states.RESCUED: inst_state.RESCUE,
105
 
}
106
 
 
107
 
 
108
 
def _state_description(vm_state, shutdown_terminate):
109
 
    """Map the vm state to the server status string"""
110
 
    if (vm_state == vm_states.SHUTOFF and
111
 
        not shutdown_terminate):
112
 
            name = inst_state.STOPPED
113
 
    else:
114
 
        name = _STATE_DESCRIPTION_MAP.get(vm_state, vm_state)
115
 
 
116
 
    return {'code': inst_state.name_to_code(name),
117
 
            'name': name}
118
 
 
119
 
 
120
 
def _parse_block_device_mapping(bdm):
121
 
    """Parse BlockDeviceMappingItemType into flat hash
122
 
    BlockDevicedMapping.<N>.DeviceName
123
 
    BlockDevicedMapping.<N>.Ebs.SnapshotId
124
 
    BlockDevicedMapping.<N>.Ebs.VolumeSize
125
 
    BlockDevicedMapping.<N>.Ebs.DeleteOnTermination
126
 
    BlockDevicedMapping.<N>.Ebs.NoDevice
127
 
    BlockDevicedMapping.<N>.VirtualName
128
 
    => remove .Ebs and allow volume id in SnapshotId
129
 
    """
130
 
    ebs = bdm.pop('ebs', None)
131
 
    if ebs:
132
 
        ec2_id = ebs.pop('snapshot_id', None)
133
 
        if ec2_id:
134
 
            id = ec2utils.ec2_id_to_id(ec2_id)
135
 
            if ec2_id.startswith('snap-'):
136
 
                bdm['snapshot_id'] = id
137
 
            elif ec2_id.startswith('vol-'):
138
 
                bdm['volume_id'] = id
139
 
            ebs.setdefault('delete_on_termination', True)
140
 
        bdm.update(ebs)
141
 
    return bdm
142
 
 
143
 
 
144
 
def _properties_get_mappings(properties):
145
 
    return block_device.mappings_prepend_dev(properties.get('mappings', []))
146
 
 
147
 
 
148
 
def _format_block_device_mapping(bdm):
149
 
    """Contruct BlockDeviceMappingItemType
150
 
    {'device_name': '...', 'snapshot_id': , ...}
151
 
    => BlockDeviceMappingItemType
152
 
    """
153
 
    keys = (('deviceName', 'device_name'),
154
 
             ('virtualName', 'virtual_name'))
155
 
    item = {}
156
 
    for name, k in keys:
157
 
        if k in bdm:
158
 
            item[name] = bdm[k]
159
 
    if bdm.get('no_device'):
160
 
        item['noDevice'] = True
161
 
    if ('snapshot_id' in bdm) or ('volume_id' in bdm):
162
 
        ebs_keys = (('snapshotId', 'snapshot_id'),
163
 
                    ('snapshotId', 'volume_id'),        # snapshotId is abused
164
 
                    ('volumeSize', 'volume_size'),
165
 
                    ('deleteOnTermination', 'delete_on_termination'))
166
 
        ebs = {}
167
 
        for name, k in ebs_keys:
168
 
            if k in bdm:
169
 
                if k == 'snapshot_id':
170
 
                    ebs[name] = ec2utils.id_to_ec2_snap_id(bdm[k])
171
 
                elif k == 'volume_id':
172
 
                    ebs[name] = ec2utils.id_to_ec2_vol_id(bdm[k])
173
 
                else:
174
 
                    ebs[name] = bdm[k]
175
 
        assert 'snapshotId' in ebs
176
 
        item['ebs'] = ebs
177
 
    return item
178
 
 
179
 
 
180
 
def _format_mappings(properties, result):
181
 
    """Format multiple BlockDeviceMappingItemType"""
182
 
    mappings = [{'virtualName': m['virtual'], 'deviceName': m['device']}
183
 
                for m in _properties_get_mappings(properties)
184
 
                if block_device.is_swap_or_ephemeral(m['virtual'])]
185
 
 
186
 
    block_device_mapping = [_format_block_device_mapping(bdm) for bdm in
187
 
                            properties.get('block_device_mapping', [])]
188
 
 
189
 
    # NOTE(yamahata): overwrite mappings with block_device_mapping
190
 
    for bdm in block_device_mapping:
191
 
        for i in range(len(mappings)):
192
 
            if bdm['deviceName'] == mappings[i]['deviceName']:
193
 
                del mappings[i]
194
 
                break
195
 
        mappings.append(bdm)
196
 
 
197
 
    # NOTE(yamahata): trim ebs.no_device == true. Is this necessary?
198
 
    mappings = [bdm for bdm in mappings if not (bdm.get('noDevice', False))]
199
 
 
200
 
    if mappings:
201
 
        result['blockDeviceMapping'] = mappings
202
 
 
203
 
 
204
 
class CloudController(object):
205
 
    """ CloudController provides the critical dispatch between
206
 
 inbound API calls through the endpoint and messages
207
 
 sent to the other nodes.
208
 
"""
209
 
    def __init__(self):
210
 
        self.image_service = s3.S3ImageService()
211
 
        self.network_api = network.API()
212
 
        self.volume_api = volume.API()
213
 
        self.compute_api = compute.API(network_api=self.network_api,
214
 
                                       volume_api=self.volume_api)
215
 
        self.sgh = utils.import_object(FLAGS.security_group_handler)
216
 
 
217
 
    def __str__(self):
218
 
        return 'CloudController'
219
 
 
220
 
    def _get_image_state(self, image):
221
 
        # NOTE(vish): fallback status if image_state isn't set
222
 
        state = image.get('status')
223
 
        if state == 'active':
224
 
            state = 'available'
225
 
        return image['properties'].get('image_state', state)
226
 
 
227
 
    def describe_availability_zones(self, context, **kwargs):
228
 
        if ('zone_name' in kwargs and
229
 
            'verbose' in kwargs['zone_name'] and
230
 
            context.is_admin):
231
 
            return self._describe_availability_zones_verbose(context,
232
 
                                                             **kwargs)
233
 
        else:
234
 
            return self._describe_availability_zones(context, **kwargs)
235
 
 
236
 
    def _describe_availability_zones(self, context, **kwargs):
237
 
        ctxt = context.elevated()
238
 
        enabled_services = db.service_get_all(ctxt, False)
239
 
        disabled_services = db.service_get_all(ctxt, True)
240
 
        available_zones = []
241
 
        for zone in [service.availability_zone for service
242
 
                     in enabled_services]:
243
 
            if not zone in available_zones:
244
 
                available_zones.append(zone)
245
 
        not_available_zones = []
246
 
        for zone in [service.availability_zone for service in disabled_services
247
 
                     if not service['availability_zone'] in available_zones]:
248
 
            if not zone in not_available_zones:
249
 
                not_available_zones.append(zone)
250
 
        result = []
251
 
        for zone in available_zones:
252
 
            result.append({'zoneName': zone,
253
 
                           'zoneState': "available"})
254
 
        for zone in not_available_zones:
255
 
            result.append({'zoneName': zone,
256
 
                           'zoneState': "not available"})
257
 
        return {'availabilityZoneInfo': result}
258
 
 
259
 
    def _describe_availability_zones_verbose(self, context, **kwargs):
260
 
        rv = {'availabilityZoneInfo': [{'zoneName': 'nova',
261
 
                                        'zoneState': 'available'}]}
262
 
 
263
 
        services = db.service_get_all(context, False)
264
 
        hosts = []
265
 
        for host in [service['host'] for service in services]:
266
 
            if not host in hosts:
267
 
                hosts.append(host)
268
 
        for host in hosts:
269
 
            rv['availabilityZoneInfo'].append({'zoneName': '|- %s' % host,
270
 
                                               'zoneState': ''})
271
 
            hsvcs = [service for service in services
272
 
                     if service['host'] == host]
273
 
            for svc in hsvcs:
274
 
                alive = utils.service_is_up(svc)
275
 
                art = (alive and ":-)") or "XXX"
276
 
                active = 'enabled'
277
 
                if svc['disabled']:
278
 
                    active = 'disabled'
279
 
                rv['availabilityZoneInfo'].append({
280
 
                        'zoneName': '| |- %s' % svc['binary'],
281
 
                        'zoneState': '%s %s %s' % (active, art,
282
 
                                                   svc['updated_at'])})
283
 
        return rv
284
 
 
285
 
    def describe_regions(self, context, region_name=None, **kwargs):
286
 
        if FLAGS.region_list:
287
 
            regions = []
288
 
            for region in FLAGS.region_list:
289
 
                name, _sep, host = region.partition('=')
290
 
                endpoint = '%s://%s:%s%s' % (FLAGS.ec2_scheme,
291
 
                                             host,
292
 
                                             FLAGS.ec2_port,
293
 
                                             FLAGS.ec2_path)
294
 
                regions.append({'regionName': name,
295
 
                                'regionEndpoint': endpoint})
296
 
        else:
297
 
            regions = [{'regionName': 'nova',
298
 
                        'regionEndpoint': '%s://%s:%s%s' % (FLAGS.ec2_scheme,
299
 
                                                            FLAGS.ec2_host,
300
 
                                                            FLAGS.ec2_port,
301
 
                                                            FLAGS.ec2_path)}]
302
 
        return {'regionInfo': regions}
303
 
 
304
 
    def describe_snapshots(self,
305
 
                           context,
306
 
                           snapshot_id=None,
307
 
                           owner=None,
308
 
                           restorable_by=None,
309
 
                           **kwargs):
310
 
        if snapshot_id:
311
 
            snapshots = []
312
 
            for ec2_id in snapshot_id:
313
 
                internal_id = ec2utils.ec2_id_to_id(ec2_id)
314
 
                snapshot = self.volume_api.get_snapshot(
315
 
                    context,
316
 
                    snapshot_id=internal_id)
317
 
                snapshots.append(snapshot)
318
 
        else:
319
 
            snapshots = self.volume_api.get_all_snapshots(context)
320
 
        snapshots = [self._format_snapshot(context, s) for s in snapshots]
321
 
        return {'snapshotSet': snapshots}
322
 
 
323
 
    def _format_snapshot(self, context, snapshot):
324
 
        s = {}
325
 
        s['snapshotId'] = ec2utils.id_to_ec2_snap_id(snapshot['id'])
326
 
        s['volumeId'] = ec2utils.id_to_ec2_vol_id(snapshot['volume_id'])
327
 
        s['status'] = snapshot['status']
328
 
        s['startTime'] = snapshot['created_at']
329
 
        s['progress'] = snapshot['progress']
330
 
        s['ownerId'] = snapshot['project_id']
331
 
        s['volumeSize'] = snapshot['volume_size']
332
 
        s['description'] = snapshot['display_description']
333
 
        return s
334
 
 
335
 
    def create_snapshot(self, context, volume_id, **kwargs):
336
 
        validate_ec2_id(volume_id)
337
 
        LOG.audit(_("Create snapshot of volume %s"), volume_id,
338
 
                  context=context)
339
 
        volume_id = ec2utils.ec2_id_to_id(volume_id)
340
 
        volume = self.volume_api.get(context, volume_id)
341
 
        snapshot = self.volume_api.create_snapshot(
342
 
                context,
343
 
                volume,
344
 
                None,
345
 
                kwargs.get('description'))
346
 
        return self._format_snapshot(context, snapshot)
347
 
 
348
 
    def delete_snapshot(self, context, snapshot_id, **kwargs):
349
 
        snapshot_id = ec2utils.ec2_id_to_id(snapshot_id)
350
 
        snapshot = self.volume_api.get_snapshot(context, snapshot_id)
351
 
        self.volume_api.delete_snapshot(context, snapshot)
352
 
        return True
353
 
 
354
 
    def describe_key_pairs(self, context, key_name=None, **kwargs):
355
 
        key_pairs = db.key_pair_get_all_by_user(context, context.user_id)
356
 
        if not key_name is None:
357
 
            key_pairs = [x for x in key_pairs if x['name'] in key_name]
358
 
 
359
 
        result = []
360
 
        for key_pair in key_pairs:
361
 
            # filter out the vpn keys
362
 
            suffix = FLAGS.vpn_key_suffix
363
 
            if context.is_admin or not key_pair['name'].endswith(suffix):
364
 
                result.append({
365
 
                    'keyName': key_pair['name'],
366
 
                    'keyFingerprint': key_pair['fingerprint'],
367
 
                })
368
 
 
369
 
        return {'keySet': result}
370
 
 
371
 
    def create_key_pair(self, context, key_name, **kwargs):
372
 
        if not re.match('^[a-zA-Z0-9_\- ]+$', str(key_name)):
373
 
            err = _("Value (%s) for KeyName is invalid."
374
 
                    " Content limited to Alphanumeric character, "
375
 
                    "spaces, dashes, and underscore.") % key_name
376
 
            raise exception.EC2APIError(err)
377
 
 
378
 
        if len(str(key_name)) > 255:
379
 
            err = _("Value (%s) for Keyname is invalid."
380
 
                    " Length exceeds maximum of 255.") % key_name
381
 
            raise exception.EC2APIError(err)
382
 
 
383
 
        LOG.audit(_("Create key pair %s"), key_name, context=context)
384
 
        data = _gen_key(context, context.user_id, key_name)
385
 
        return {'keyName': key_name,
386
 
                'keyFingerprint': data['fingerprint'],
387
 
                'keyMaterial': data['private_key']}
388
 
        # TODO(vish): when context is no longer an object, pass it here
389
 
 
390
 
    def import_key_pair(self, context, key_name, public_key_material,
391
 
                        **kwargs):
392
 
        LOG.audit(_("Import key %s"), key_name, context=context)
393
 
        try:
394
 
            db.key_pair_get(context, context.user_id, key_name)
395
 
            raise exception.KeyPairExists(key_name=key_name)
396
 
        except exception.NotFound:
397
 
            pass
398
 
        public_key = base64.b64decode(public_key_material)
399
 
        fingerprint = crypto.generate_fingerprint(public_key)
400
 
        key = {}
401
 
        key['user_id'] = context.user_id
402
 
        key['name'] = key_name
403
 
        key['public_key'] = public_key
404
 
        key['fingerprint'] = fingerprint
405
 
        db.key_pair_create(context, key)
406
 
        return {'keyName': key_name,
407
 
                'keyFingerprint': fingerprint}
408
 
 
409
 
    def delete_key_pair(self, context, key_name, **kwargs):
410
 
        LOG.audit(_("Delete key pair %s"), key_name, context=context)
411
 
        try:
412
 
            db.key_pair_destroy(context, context.user_id, key_name)
413
 
        except exception.NotFound:
414
 
            # aws returns true even if the key doesn't exist
415
 
            pass
416
 
        return True
417
 
 
418
 
    def describe_security_groups(self, context, group_name=None, group_id=None,
419
 
                                 **kwargs):
420
 
        self.compute_api.ensure_default_security_group(context)
421
 
        if group_name or group_id:
422
 
            groups = []
423
 
            if group_name:
424
 
                for name in group_name:
425
 
                    group = db.security_group_get_by_name(context,
426
 
                                                          context.project_id,
427
 
                                                          name)
428
 
                    groups.append(group)
429
 
            if group_id:
430
 
                for gid in group_id:
431
 
                    group = db.security_group_get(context, gid)
432
 
                    groups.append(group)
433
 
        elif context.is_admin:
434
 
            groups = db.security_group_get_all(context)
435
 
        else:
436
 
            groups = db.security_group_get_by_project(context,
437
 
                                                      context.project_id)
438
 
        groups = [self._format_security_group(context, g) for g in groups]
439
 
 
440
 
        return {'securityGroupInfo':
441
 
                list(sorted(groups,
442
 
                            key=lambda k: (k['ownerId'], k['groupName'])))}
443
 
 
444
 
    def _format_security_group(self, context, group):
445
 
        g = {}
446
 
        g['groupDescription'] = group.description
447
 
        g['groupName'] = group.name
448
 
        g['ownerId'] = group.project_id
449
 
        g['ipPermissions'] = []
450
 
        for rule in group.rules:
451
 
            r = {}
452
 
            r['groups'] = []
453
 
            r['ipRanges'] = []
454
 
            if rule.group_id:
455
 
                source_group = db.security_group_get(context, rule.group_id)
456
 
                r['groups'] += [{'groupName': source_group.name,
457
 
                                 'userId': source_group.project_id}]
458
 
                if rule.protocol:
459
 
                    r['ipProtocol'] = rule.protocol
460
 
                    r['fromPort'] = rule.from_port
461
 
                    r['toPort'] = rule.to_port
462
 
                    g['ipPermissions'] += [dict(r)]
463
 
                else:
464
 
                    for protocol, min_port, max_port in (('icmp', -1, -1),
465
 
                                                         ('tcp', 1, 65535),
466
 
                                                         ('udp', 1, 65535)):
467
 
                        r['ipProtocol'] = protocol
468
 
                        r['fromPort'] = min_port
469
 
                        r['toPort'] = max_port
470
 
                        g['ipPermissions'] += [dict(r)]
471
 
            else:
472
 
                r['ipProtocol'] = rule.protocol
473
 
                r['fromPort'] = rule.from_port
474
 
                r['toPort'] = rule.to_port
475
 
                r['ipRanges'] += [{'cidrIp': rule.cidr}]
476
 
                g['ipPermissions'] += [r]
477
 
        return g
478
 
 
479
 
    def _rule_args_to_dict(self, context, kwargs):
480
 
        rules = []
481
 
        if not 'groups' in kwargs and not 'ip_ranges' in kwargs:
482
 
            rule = self._rule_dict_last_step(context, **kwargs)
483
 
            if rule:
484
 
                rules.append(rule)
485
 
            return rules
486
 
        if 'ip_ranges' in kwargs:
487
 
            rules = self._cidr_args_split(kwargs)
488
 
        else:
489
 
            rules = [kwargs]
490
 
        finalset = []
491
 
        for rule in rules:
492
 
            if 'groups' in rule:
493
 
                groups_values = self._groups_args_split(rule)
494
 
                for groups_value in groups_values:
495
 
                    final = self._rule_dict_last_step(context, **groups_value)
496
 
                    finalset.append(final)
497
 
            else:
498
 
                final = self._rule_dict_last_step(context, **rule)
499
 
                finalset.append(final)
500
 
        return finalset
501
 
 
502
 
    def _cidr_args_split(self, kwargs):
503
 
        cidr_args_split = []
504
 
        cidrs = kwargs['ip_ranges']
505
 
        for key, cidr in cidrs.iteritems():
506
 
            mykwargs = kwargs.copy()
507
 
            del mykwargs['ip_ranges']
508
 
            mykwargs['cidr_ip'] = cidr['cidr_ip']
509
 
            cidr_args_split.append(mykwargs)
510
 
        return cidr_args_split
511
 
 
512
 
    def _groups_args_split(self, kwargs):
513
 
        groups_args_split = []
514
 
        groups = kwargs['groups']
515
 
        for key, group in groups.iteritems():
516
 
            mykwargs = kwargs.copy()
517
 
            del mykwargs['groups']
518
 
            if 'group_name' in group:
519
 
                mykwargs['source_security_group_name'] = group['group_name']
520
 
            if 'user_id' in group:
521
 
                mykwargs['source_security_group_owner_id'] = group['user_id']
522
 
            if 'group_id' in group:
523
 
                mykwargs['source_security_group_id'] = group['group_id']
524
 
            groups_args_split.append(mykwargs)
525
 
        return groups_args_split
526
 
 
527
 
    def _rule_dict_last_step(self, context, to_port=None, from_port=None,
528
 
                                  ip_protocol=None, cidr_ip=None, user_id=None,
529
 
                                  source_security_group_name=None,
530
 
                                  source_security_group_owner_id=None):
531
 
 
532
 
        values = {}
533
 
 
534
 
        if source_security_group_name:
535
 
            source_project_id = self._get_source_project_id(context,
536
 
                source_security_group_owner_id)
537
 
 
538
 
            source_security_group = db.security_group_get_by_name(
539
 
                    context.elevated(),
540
 
                    source_project_id,
541
 
                    source_security_group_name)
542
 
            notfound = exception.SecurityGroupNotFound
543
 
            if not source_security_group:
544
 
                raise notfound(security_group_id=source_security_group_name)
545
 
            values['group_id'] = source_security_group['id']
546
 
        elif cidr_ip:
547
 
            # If this fails, it throws an exception. This is what we want.
548
 
            cidr_ip = urllib.unquote(cidr_ip).decode()
549
 
 
550
 
            if not utils.is_valid_cidr(cidr_ip):
551
 
                # Raise exception for non-valid address
552
 
                raise exception.EC2APIError(_("Invalid CIDR"))
553
 
 
554
 
            values['cidr'] = cidr_ip
555
 
        else:
556
 
            values['cidr'] = '0.0.0.0/0'
557
 
 
558
 
        if source_security_group_name:
559
 
            # Open everything if an explicit port range or type/code are not
560
 
            # specified, but only if a source group was specified.
561
 
            ip_proto_upper = ip_protocol.upper() if ip_protocol else ''
562
 
            if (ip_proto_upper == 'ICMP' and
563
 
                from_port is None and to_port is None):
564
 
                from_port = -1
565
 
                to_port = -1
566
 
            elif (ip_proto_upper in ['TCP', 'UDP'] and from_port is None
567
 
                  and to_port is None):
568
 
                from_port = 1
569
 
                to_port = 65535
570
 
 
571
 
        if ip_protocol and from_port is not None and to_port is not None:
572
 
 
573
 
            ip_protocol = str(ip_protocol)
574
 
            try:
575
 
                # Verify integer conversions
576
 
                from_port = int(from_port)
577
 
                to_port = int(to_port)
578
 
            except ValueError:
579
 
                if ip_protocol.upper() == 'ICMP':
580
 
                    raise exception.InvalidInput(reason="Type and"
581
 
                         " Code must be integers for ICMP protocol type")
582
 
                else:
583
 
                    raise exception.InvalidInput(reason="To and From ports "
584
 
                          "must be integers")
585
 
 
586
 
            if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']:
587
 
                raise exception.InvalidIpProtocol(protocol=ip_protocol)
588
 
 
589
 
            # Verify that from_port must always be less than
590
 
            # or equal to to_port
591
 
            if (ip_protocol.upper() in ['TCP', 'UDP'] and
592
 
                (from_port > to_port)):
593
 
                raise exception.InvalidPortRange(from_port=from_port,
594
 
                      to_port=to_port, msg="Former value cannot"
595
 
                                            " be greater than the later")
596
 
 
597
 
            # Verify valid TCP, UDP port ranges
598
 
            if (ip_protocol.upper() in ['TCP', 'UDP'] and
599
 
                (from_port < 1 or to_port > 65535)):
600
 
                raise exception.InvalidPortRange(from_port=from_port,
601
 
                      to_port=to_port, msg="Valid TCP ports should"
602
 
                                           " be between 1-65535")
603
 
 
604
 
            # Verify ICMP type and code
605
 
            if (ip_protocol.upper() == "ICMP" and
606
 
                (from_port < -1 or from_port > 255 or
607
 
                to_port < -1 or to_port > 255)):
608
 
                raise exception.InvalidPortRange(from_port=from_port,
609
 
                      to_port=to_port, msg="For ICMP, the"
610
 
                                           " type:code must be valid")
611
 
 
612
 
            values['protocol'] = ip_protocol
613
 
            values['from_port'] = from_port
614
 
            values['to_port'] = to_port
615
 
        else:
616
 
            # If cidr based filtering, protocol and ports are mandatory
617
 
            if 'cidr' in values:
618
 
                return None
619
 
 
620
 
        return values
621
 
 
622
 
    def _security_group_rule_exists(self, security_group, values):
623
 
        """Indicates whether the specified rule values are already
624
 
           defined in the given security group.
625
 
        """
626
 
        for rule in security_group.rules:
627
 
            is_duplicate = True
628
 
            keys = ('group_id', 'cidr', 'from_port', 'to_port', 'protocol')
629
 
            for key in keys:
630
 
                if rule.get(key) != values.get(key):
631
 
                    is_duplicate = False
632
 
                    break
633
 
            if is_duplicate:
634
 
                return rule['id']
635
 
        return False
636
 
 
637
 
    def revoke_security_group_ingress(self, context, group_name=None,
638
 
                                      group_id=None, **kwargs):
639
 
        if not group_name and not group_id:
640
 
            err = _("Not enough parameters, need group_name or group_id")
641
 
            raise exception.EC2APIError(err)
642
 
        self.compute_api.ensure_default_security_group(context)
643
 
        notfound = exception.SecurityGroupNotFound
644
 
        if group_name:
645
 
            security_group = db.security_group_get_by_name(context,
646
 
                                                           context.project_id,
647
 
                                                           group_name)
648
 
            if not security_group:
649
 
                raise notfound(security_group_id=group_name)
650
 
        if group_id:
651
 
            security_group = db.security_group_get(context, group_id)
652
 
            if not security_group:
653
 
                raise notfound(security_group_id=group_id)
654
 
 
655
 
        msg = _("Revoke security group ingress %s")
656
 
        LOG.audit(msg, security_group['name'], context=context)
657
 
        prevalues = []
658
 
        try:
659
 
            prevalues = kwargs['ip_permissions']
660
 
        except KeyError:
661
 
            prevalues.append(kwargs)
662
 
        rule_id = None
663
 
        rule_ids = []
664
 
        for values in prevalues:
665
 
            rulesvalues = self._rule_args_to_dict(context, values)
666
 
            if not rulesvalues:
667
 
                err = _("%s Not enough parameters to build a valid rule")
668
 
                raise exception.EC2APIError(err % rulesvalues)
669
 
 
670
 
            for values_for_rule in rulesvalues:
671
 
                values_for_rule['parent_group_id'] = security_group.id
672
 
                rule_id = self._security_group_rule_exists(security_group,
673
 
                                                           values_for_rule)
674
 
                if rule_id:
675
 
                    db.security_group_rule_destroy(context, rule_id)
676
 
                    rule_ids.append(rule_id)
677
 
        if rule_id:
678
 
            # NOTE(vish): we removed a rule, so refresh
679
 
            self.compute_api.trigger_security_group_rules_refresh(
680
 
                    context,
681
 
                    security_group_id=security_group['id'])
682
 
            self.sgh.trigger_security_group_rule_destroy_refresh(
683
 
                    context, rule_ids)
684
 
            return True
685
 
        raise exception.EC2APIError(_("No rule for the specified parameters."))
686
 
 
687
 
    # TODO(soren): This has only been tested with Boto as the client.
688
 
    #              Unfortunately, it seems Boto is using an old API
689
 
    #              for these operations, so support for newer API versions
690
 
    #              is sketchy.
691
 
    def authorize_security_group_ingress(self, context, group_name=None,
692
 
                                         group_id=None, **kwargs):
693
 
        if not group_name and not group_id:
694
 
            err = _("Not enough parameters, need group_name or group_id")
695
 
            raise exception.EC2APIError(err)
696
 
        self.compute_api.ensure_default_security_group(context)
697
 
        notfound = exception.SecurityGroupNotFound
698
 
        if group_name:
699
 
            security_group = db.security_group_get_by_name(context,
700
 
                                                           context.project_id,
701
 
                                                           group_name)
702
 
            if not security_group:
703
 
                raise notfound(security_group_id=group_name)
704
 
        if group_id:
705
 
            security_group = db.security_group_get(context, group_id)
706
 
            if not security_group:
707
 
                raise notfound(security_group_id=group_id)
708
 
 
709
 
        msg = _("Authorize security group ingress %s")
710
 
        LOG.audit(msg, security_group['name'], context=context)
711
 
        prevalues = []
712
 
        try:
713
 
            prevalues = kwargs['ip_permissions']
714
 
        except KeyError:
715
 
            prevalues.append(kwargs)
716
 
        postvalues = []
717
 
        for values in prevalues:
718
 
            rulesvalues = self._rule_args_to_dict(context, values)
719
 
            if not rulesvalues:
720
 
                err = _("%s Not enough parameters to build a valid rule")
721
 
                raise exception.EC2APIError(err % rulesvalues)
722
 
            for values_for_rule in rulesvalues:
723
 
                values_for_rule['parent_group_id'] = security_group.id
724
 
                if self._security_group_rule_exists(security_group,
725
 
                                                    values_for_rule):
726
 
                    err = _('%s - This rule already exists in group')
727
 
                    raise exception.EC2APIError(err % values_for_rule)
728
 
                postvalues.append(values_for_rule)
729
 
 
730
 
        rule_ids = []
731
 
        for values_for_rule in postvalues:
732
 
            security_group_rule = db.security_group_rule_create(
733
 
                    context,
734
 
                    values_for_rule)
735
 
            rule_ids.append(security_group_rule['id'])
736
 
 
737
 
        if postvalues:
738
 
            self.compute_api.trigger_security_group_rules_refresh(
739
 
                    context,
740
 
                    security_group_id=security_group['id'])
741
 
            self.sgh.trigger_security_group_rule_create_refresh(
742
 
                    context, rule_ids)
743
 
            return True
744
 
 
745
 
        raise exception.EC2APIError(_("No rule for the specified parameters."))
746
 
 
747
 
    def _get_source_project_id(self, context, source_security_group_owner_id):
748
 
        if source_security_group_owner_id:
749
 
        # Parse user:project for source group.
750
 
            source_parts = source_security_group_owner_id.split(':')
751
 
 
752
 
            # If no project name specified, assume it's same as user name.
753
 
            # Since we're looking up by project name, the user name is not
754
 
            # used here.  It's only read for EC2 API compatibility.
755
 
            if len(source_parts) == 2:
756
 
                source_project_id = source_parts[1]
757
 
            else:
758
 
                source_project_id = source_parts[0]
759
 
        else:
760
 
            source_project_id = context.project_id
761
 
 
762
 
        return source_project_id
763
 
 
764
 
    def create_security_group(self, context, group_name, group_description):
765
 
        if not re.match('^[a-zA-Z0-9_\- ]+$', str(group_name)):
766
 
            # Some validation to ensure that values match API spec.
767
 
            # - Alphanumeric characters, spaces, dashes, and underscores.
768
 
            # TODO(Daviey): LP: #813685 extend beyond group_name checking, and
769
 
            #  probably create a param validator that can be used elsewhere.
770
 
            err = _("Value (%s) for parameter GroupName is invalid."
771
 
                    " Content limited to Alphanumeric characters, "
772
 
                    "spaces, dashes, and underscores.") % group_name
773
 
            # err not that of master ec2 implementation, as they fail to raise.
774
 
            raise exception.InvalidParameterValue(err=err)
775
 
 
776
 
        if len(str(group_name)) > 255:
777
 
            err = _("Value (%s) for parameter GroupName is invalid."
778
 
                    " Length exceeds maximum of 255.") % group_name
779
 
            raise exception.InvalidParameterValue(err=err)
780
 
 
781
 
        LOG.audit(_("Create Security Group %s"), group_name, context=context)
782
 
        self.compute_api.ensure_default_security_group(context)
783
 
        if db.security_group_exists(context, context.project_id, group_name):
784
 
            msg = _('group %s already exists')
785
 
            raise exception.EC2APIError(msg % group_name)
786
 
 
787
 
        group = {'user_id': context.user_id,
788
 
                 'project_id': context.project_id,
789
 
                 'name': group_name,
790
 
                 'description': group_description}
791
 
        group_ref = db.security_group_create(context, group)
792
 
 
793
 
        self.sgh.trigger_security_group_create_refresh(context, group)
794
 
 
795
 
        return {'securityGroupSet': [self._format_security_group(context,
796
 
                                                                 group_ref)]}
797
 
 
798
 
    def delete_security_group(self, context, group_name=None, group_id=None,
799
 
                              **kwargs):
800
 
        if not group_name and not group_id:
801
 
            err = _("Not enough parameters, need group_name or group_id")
802
 
            raise exception.EC2APIError(err)
803
 
        notfound = exception.SecurityGroupNotFound
804
 
        if group_name:
805
 
            security_group = db.security_group_get_by_name(context,
806
 
                                                           context.project_id,
807
 
                                                           group_name)
808
 
            if not security_group:
809
 
                raise notfound(security_group_id=group_name)
810
 
        elif group_id:
811
 
            security_group = db.security_group_get(context, group_id)
812
 
            if not security_group:
813
 
                raise notfound(security_group_id=group_id)
814
 
        if db.security_group_in_use(context, security_group.id):
815
 
            raise exception.InvalidGroup(reason="In Use")
816
 
        LOG.audit(_("Delete security group %s"), group_name, context=context)
817
 
        db.security_group_destroy(context, security_group.id)
818
 
 
819
 
        self.sgh.trigger_security_group_destroy_refresh(context,
820
 
                                                        security_group.id)
821
 
        return True
822
 
 
823
 
    def get_console_output(self, context, instance_id, **kwargs):
824
 
        LOG.audit(_("Get console output for instance %s"), instance_id,
825
 
                  context=context)
826
 
        # instance_id may be passed in as a list of instances
827
 
        if isinstance(instance_id, list):
828
 
            ec2_id = instance_id[0]
829
 
        else:
830
 
            ec2_id = instance_id
831
 
        validate_ec2_id(ec2_id)
832
 
        instance_id = ec2utils.ec2_id_to_id(ec2_id)
833
 
        instance = self.compute_api.get(context, instance_id)
834
 
        output = self.compute_api.get_console_output(context, instance)
835
 
        now = utils.utcnow()
836
 
        return {"InstanceId": ec2_id,
837
 
                "Timestamp": now,
838
 
                "output": base64.b64encode(output)}
839
 
 
840
 
    def describe_volumes(self, context, volume_id=None, **kwargs):
841
 
        if volume_id:
842
 
            volumes = []
843
 
            for ec2_id in volume_id:
844
 
                validate_ec2_id(ec2_id)
845
 
                internal_id = ec2utils.ec2_id_to_id(ec2_id)
846
 
                volume = self.volume_api.get(context, internal_id)
847
 
                volumes.append(volume)
848
 
        else:
849
 
            volumes = self.volume_api.get_all(context)
850
 
        volumes = [self._format_volume(context, v) for v in volumes]
851
 
        return {'volumeSet': volumes}
852
 
 
853
 
    def _format_volume(self, context, volume):
854
 
        instance_ec2_id = None
855
 
        instance_data = None
856
 
        if volume.get('instance', None):
857
 
            instance_id = volume['instance']['id']
858
 
            instance_ec2_id = ec2utils.id_to_ec2_id(instance_id)
859
 
            instance_data = '%s[%s]' % (instance_ec2_id,
860
 
                                        volume['instance']['host'])
861
 
        v = {}
862
 
        v['volumeId'] = ec2utils.id_to_ec2_vol_id(volume['id'])
863
 
        v['status'] = volume['status']
864
 
        v['size'] = volume['size']
865
 
        v['availabilityZone'] = volume['availability_zone']
866
 
        v['createTime'] = volume['created_at']
867
 
        if context.is_admin:
868
 
            v['status'] = '%s (%s, %s, %s, %s)' % (
869
 
                volume['status'],
870
 
                volume['project_id'],
871
 
                volume['host'],
872
 
                instance_data,
873
 
                volume['mountpoint'])
874
 
        if volume['attach_status'] == 'attached':
875
 
            v['attachmentSet'] = [{'attachTime': volume['attach_time'],
876
 
                                   'deleteOnTermination': False,
877
 
                                   'device': volume['mountpoint'],
878
 
                                   'instanceId': instance_ec2_id,
879
 
                                   'status': 'attached',
880
 
                                   'volumeId': v['volumeId']}]
881
 
        else:
882
 
            v['attachmentSet'] = [{}]
883
 
        if volume.get('snapshot_id') is not None:
884
 
            v['snapshotId'] = ec2utils.id_to_ec2_snap_id(volume['snapshot_id'])
885
 
        else:
886
 
            v['snapshotId'] = None
887
 
 
888
 
        return v
889
 
 
890
 
    def create_volume(self, context, **kwargs):
891
 
        size = kwargs.get('size')
892
 
        if kwargs.get('snapshot_id') is not None:
893
 
            snapshot_id = ec2utils.ec2_id_to_id(kwargs['snapshot_id'])
894
 
            snapshot = self.volume_api.get_snapshot(context, snapshot_id)
895
 
            LOG.audit(_("Create volume from snapshot %s"), snapshot_id,
896
 
                      context=context)
897
 
        else:
898
 
            snapshot = None
899
 
            LOG.audit(_("Create volume of %s GB"), size, context=context)
900
 
 
901
 
        availability_zone = kwargs.get('availability_zone', None)
902
 
 
903
 
        volume = self.volume_api.create(context,
904
 
                                        size,
905
 
                                        None,
906
 
                                        None,
907
 
                                        snapshot,
908
 
                                        availability_zone=availability_zone)
909
 
        # TODO(vish): Instance should be None at db layer instead of
910
 
        #             trying to lazy load, but for now we turn it into
911
 
        #             a dict to avoid an error.
912
 
        return self._format_volume(context, dict(volume))
913
 
 
914
 
    def delete_volume(self, context, volume_id, **kwargs):
915
 
        validate_ec2_id(volume_id)
916
 
        volume_id = ec2utils.ec2_id_to_id(volume_id)
917
 
 
918
 
        try:
919
 
            volume = self.volume_api.get(context, volume_id)
920
 
            self.volume_api.delete(context, volume)
921
 
        except exception.InvalidVolume:
922
 
            raise exception.EC2APIError(_('Delete Failed'))
923
 
 
924
 
        return True
925
 
 
926
 
    def attach_volume(self, context, volume_id, instance_id, device, **kwargs):
927
 
        validate_ec2_id(instance_id)
928
 
        validate_ec2_id(volume_id)
929
 
        volume_id = ec2utils.ec2_id_to_id(volume_id)
930
 
        instance_id = ec2utils.ec2_id_to_id(instance_id)
931
 
        instance = self.compute_api.get(context, instance_id)
932
 
        msg = _("Attach volume %(volume_id)s to instance %(instance_id)s"
933
 
                " at %(device)s") % locals()
934
 
        LOG.audit(msg, context=context)
935
 
 
936
 
        try:
937
 
            self.compute_api.attach_volume(context, instance,
938
 
                                           volume_id, device)
939
 
        except exception.InvalidVolume:
940
 
            raise exception.EC2APIError(_('Attach Failed.'))
941
 
 
942
 
        volume = self.volume_api.get(context, volume_id)
943
 
        return {'attachTime': volume['attach_time'],
944
 
                'device': volume['mountpoint'],
945
 
                'instanceId': ec2utils.id_to_ec2_id(instance_id),
946
 
                'requestId': context.request_id,
947
 
                'status': volume['attach_status'],
948
 
                'volumeId': ec2utils.id_to_ec2_vol_id(volume_id)}
949
 
 
950
 
    def detach_volume(self, context, volume_id, **kwargs):
951
 
        validate_ec2_id(volume_id)
952
 
        volume_id = ec2utils.ec2_id_to_id(volume_id)
953
 
        LOG.audit(_("Detach volume %s"), volume_id, context=context)
954
 
        volume = self.volume_api.get(context, volume_id)
955
 
 
956
 
        try:
957
 
            instance = self.compute_api.detach_volume(context,
958
 
                                                      volume_id=volume_id)
959
 
        except exception.InvalidVolume:
960
 
            raise exception.EC2APIError(_('Detach Volume Failed.'))
961
 
 
962
 
        return {'attachTime': volume['attach_time'],
963
 
                'device': volume['mountpoint'],
964
 
                'instanceId': ec2utils.id_to_ec2_id(instance['id']),
965
 
                'requestId': context.request_id,
966
 
                'status': volume['attach_status'],
967
 
                'volumeId': ec2utils.id_to_ec2_vol_id(volume_id)}
968
 
 
969
 
    def _format_kernel_id(self, context, instance_ref, result, key):
970
 
        kernel_uuid = instance_ref['kernel_id']
971
 
        if kernel_uuid is None or kernel_uuid == '':
972
 
            return
973
 
        result[key] = ec2utils.glance_id_to_ec2_id(context, kernel_uuid, 'aki')
974
 
 
975
 
    def _format_ramdisk_id(self, context, instance_ref, result, key):
976
 
        ramdisk_uuid = instance_ref['ramdisk_id']
977
 
        if ramdisk_uuid is None or ramdisk_uuid == '':
978
 
            return
979
 
        result[key] = ec2utils.glance_id_to_ec2_id(context, ramdisk_uuid,
980
 
                                                   'ari')
981
 
 
982
 
    def describe_instance_attribute(self, context, instance_id, attribute,
983
 
                                    **kwargs):
984
 
        def _unsupported_attribute(instance, result):
985
 
            raise exception.EC2APIError(_('attribute not supported: %s') %
986
 
                                     attribute)
987
 
 
988
 
        def _format_attr_block_device_mapping(instance, result):
989
 
            tmp = {}
990
 
            self._format_instance_root_device_name(instance, tmp)
991
 
            self._format_instance_bdm(context, instance_id,
992
 
                                      tmp['rootDeviceName'], result)
993
 
 
994
 
        def _format_attr_disable_api_termination(instance, result):
995
 
            result['disableApiTermination'] = instance['disable_terminate']
996
 
 
997
 
        def _format_attr_group_set(instance, result):
998
 
            CloudController._format_group_set(instance, result)
999
 
 
1000
 
        def _format_attr_instance_initiated_shutdown_behavior(instance,
1001
 
                                                               result):
1002
 
            if instance['shutdown_terminate']:
1003
 
                result['instanceInitiatedShutdownBehavior'] = 'terminate'
1004
 
            else:
1005
 
                result['instanceInitiatedShutdownBehavior'] = 'stop'
1006
 
 
1007
 
        def _format_attr_instance_type(instance, result):
1008
 
            self._format_instance_type(instance, result)
1009
 
 
1010
 
        def _format_attr_kernel(instance, result):
1011
 
            self._format_kernel_id(context, instance, result, 'kernel')
1012
 
 
1013
 
        def _format_attr_ramdisk(instance, result):
1014
 
            self._format_ramdisk_id(context, instance, result, 'ramdisk')
1015
 
 
1016
 
        def _format_attr_root_device_name(instance, result):
1017
 
            self._format_instance_root_device_name(instance, result)
1018
 
 
1019
 
        def _format_attr_source_dest_check(instance, result):
1020
 
            _unsupported_attribute(instance, result)
1021
 
 
1022
 
        def _format_attr_user_data(instance, result):
1023
 
            result['userData'] = base64.b64decode(instance['user_data'])
1024
 
 
1025
 
        attribute_formatter = {
1026
 
            'blockDeviceMapping': _format_attr_block_device_mapping,
1027
 
            'disableApiTermination': _format_attr_disable_api_termination,
1028
 
            'groupSet': _format_attr_group_set,
1029
 
            'instanceInitiatedShutdownBehavior':
1030
 
            _format_attr_instance_initiated_shutdown_behavior,
1031
 
            'instanceType': _format_attr_instance_type,
1032
 
            'kernel': _format_attr_kernel,
1033
 
            'ramdisk': _format_attr_ramdisk,
1034
 
            'rootDeviceName': _format_attr_root_device_name,
1035
 
            'sourceDestCheck': _format_attr_source_dest_check,
1036
 
            'userData': _format_attr_user_data,
1037
 
            }
1038
 
 
1039
 
        fn = attribute_formatter.get(attribute)
1040
 
        if fn is None:
1041
 
            raise exception.EC2APIError(
1042
 
                _('attribute not supported: %s') % attribute)
1043
 
 
1044
 
        ec2_instance_id = instance_id
1045
 
        validate_ec2_id(instance_id)
1046
 
        instance_id = ec2utils.ec2_id_to_id(ec2_instance_id)
1047
 
        instance = self.compute_api.get(context, instance_id)
1048
 
        result = {'instance_id': ec2_instance_id}
1049
 
        fn(instance, result)
1050
 
        return result
1051
 
 
1052
 
    def describe_instances(self, context, **kwargs):
1053
 
        # Optional DescribeInstances argument
1054
 
        instance_id = kwargs.get('instance_id', None)
1055
 
        return self._format_describe_instances(context,
1056
 
                instance_id=instance_id)
1057
 
 
1058
 
    def describe_instances_v6(self, context, **kwargs):
1059
 
        # Optional DescribeInstancesV6 argument
1060
 
        instance_id = kwargs.get('instance_id', None)
1061
 
        return self._format_describe_instances(context,
1062
 
                instance_id=instance_id, use_v6=True)
1063
 
 
1064
 
    def _format_describe_instances(self, context, **kwargs):
1065
 
        return {'reservationSet': self._format_instances(context, **kwargs)}
1066
 
 
1067
 
    def _format_run_instances(self, context, reservation_id):
1068
 
        i = self._format_instances(context, reservation_id=reservation_id)
1069
 
        assert len(i) == 1
1070
 
        return i[0]
1071
 
 
1072
 
    def _format_terminate_instances(self, context, instance_id,
1073
 
                                    previous_states):
1074
 
        instances_set = []
1075
 
        for (ec2_id, previous_state) in zip(instance_id, previous_states):
1076
 
            i = {}
1077
 
            i['instanceId'] = ec2_id
1078
 
            i['previousState'] = _state_description(previous_state['vm_state'],
1079
 
                                        previous_state['shutdown_terminate'])
1080
 
            try:
1081
 
                internal_id = ec2utils.ec2_id_to_id(ec2_id)
1082
 
                instance = self.compute_api.get(context, internal_id)
1083
 
                i['shutdownState'] = _state_description(instance['vm_state'],
1084
 
                                            instance['shutdown_terminate'])
1085
 
            except exception.NotFound:
1086
 
                i['shutdownState'] = _state_description(vm_states.DELETED,
1087
 
                                                        True)
1088
 
            instances_set.append(i)
1089
 
        return {'instancesSet': instances_set}
1090
 
 
1091
 
    def _format_instance_bdm(self, context, instance_id, root_device_name,
1092
 
                             result):
1093
 
        """Format InstanceBlockDeviceMappingResponseItemType"""
1094
 
        root_device_type = 'instance-store'
1095
 
        mapping = []
1096
 
        for bdm in db.block_device_mapping_get_all_by_instance(context,
1097
 
                                                               instance_id):
1098
 
            volume_id = bdm['volume_id']
1099
 
            if (volume_id is None or bdm['no_device']):
1100
 
                continue
1101
 
 
1102
 
            if (bdm['device_name'] == root_device_name and
1103
 
                (bdm['snapshot_id'] or bdm['volume_id'])):
1104
 
                assert not bdm['virtual_name']
1105
 
                root_device_type = 'ebs'
1106
 
 
1107
 
            vol = self.volume_api.get(context, volume_id)
1108
 
            LOG.debug(_("vol = %s\n"), vol)
1109
 
            # TODO(yamahata): volume attach time
1110
 
            ebs = {'volumeId': volume_id,
1111
 
                   'deleteOnTermination': bdm['delete_on_termination'],
1112
 
                   'attachTime': vol['attach_time'] or '-',
1113
 
                   'status': vol['status'], }
1114
 
            res = {'deviceName': bdm['device_name'],
1115
 
                   'ebs': ebs, }
1116
 
            mapping.append(res)
1117
 
 
1118
 
        if mapping:
1119
 
            result['blockDeviceMapping'] = mapping
1120
 
        result['rootDeviceType'] = root_device_type
1121
 
 
1122
 
    @staticmethod
1123
 
    def _format_instance_root_device_name(instance, result):
1124
 
        result['rootDeviceName'] = (instance.get('root_device_name') or
1125
 
                                    block_device.DEFAULT_ROOT_DEV_NAME)
1126
 
 
1127
 
    @staticmethod
1128
 
    def _format_instance_type(instance, result):
1129
 
        if instance['instance_type']:
1130
 
            result['instanceType'] = instance['instance_type'].get('name')
1131
 
        else:
1132
 
            result['instanceType'] = None
1133
 
 
1134
 
    @staticmethod
1135
 
    def _format_group_set(instance, result):
1136
 
        security_group_names = []
1137
 
        if instance.get('security_groups'):
1138
 
            for security_group in instance['security_groups']:
1139
 
                security_group_names.append(security_group['name'])
1140
 
        result['groupSet'] = utils.convert_to_list_dict(
1141
 
            security_group_names, 'groupId')
1142
 
 
1143
 
    def _format_instances(self, context, instance_id=None, use_v6=False,
1144
 
            **search_opts):
1145
 
        # TODO(termie): this method is poorly named as its name does not imply
1146
 
        #               that it will be making a variety of database calls
1147
 
        #               rather than simply formatting a bunch of instances that
1148
 
        #               were handed to it
1149
 
        reservations = {}
1150
 
        # NOTE(vish): instance_id is an optional list of ids to filter by
1151
 
        if instance_id:
1152
 
            instances = []
1153
 
            for ec2_id in instance_id:
1154
 
                internal_id = ec2utils.ec2_id_to_id(ec2_id)
1155
 
                try:
1156
 
                    instance = self.compute_api.get(context, internal_id)
1157
 
                except exception.NotFound:
1158
 
                    continue
1159
 
                instances.append(instance)
1160
 
        else:
1161
 
            try:
1162
 
                # always filter out deleted instances
1163
 
                search_opts['deleted'] = False
1164
 
                instances = self.compute_api.get_all(context,
1165
 
                                                     search_opts=search_opts,
1166
 
                                                     sort_dir='asc')
1167
 
            except exception.NotFound:
1168
 
                instances = []
1169
 
        for instance in instances:
1170
 
            if not context.is_admin:
1171
 
                if instance['image_ref'] == str(FLAGS.vpn_image_id):
1172
 
                    continue
1173
 
            i = {}
1174
 
            instance_id = instance['id']
1175
 
            ec2_id = ec2utils.id_to_ec2_id(instance_id)
1176
 
            i['instanceId'] = ec2_id
1177
 
            image_uuid = instance['image_ref']
1178
 
            i['imageId'] = ec2utils.glance_id_to_ec2_id(context, image_uuid)
1179
 
            self._format_kernel_id(context, instance, i, 'kernelId')
1180
 
            self._format_ramdisk_id(context, instance, i, 'ramdiskId')
1181
 
            i['instanceState'] = _state_description(
1182
 
                instance['vm_state'], instance['shutdown_terminate'])
1183
 
 
1184
 
            fixed_ip = None
1185
 
            floating_ip = None
1186
 
            ip_info = ec2utils.get_ip_info_for_instance(context, instance)
1187
 
            if ip_info['fixed_ips']:
1188
 
                fixed_ip = ip_info['fixed_ips'][0]
1189
 
            if ip_info['floating_ips']:
1190
 
                floating_ip = ip_info['floating_ips'][0]
1191
 
            if ip_info['fixed_ip6s']:
1192
 
                i['dnsNameV6'] = ip_info['fixed_ip6s'][0]
1193
 
            if FLAGS.ec2_private_dns_show_ip:
1194
 
                i['privateDnsName'] = fixed_ip
1195
 
            else:
1196
 
                i['privateDnsName'] = instance['hostname']
1197
 
            i['privateIpAddress'] = fixed_ip
1198
 
            i['publicDnsName'] = floating_ip
1199
 
            i['ipAddress'] = floating_ip or fixed_ip
1200
 
            i['dnsName'] = i['publicDnsName'] or i['privateDnsName']
1201
 
            i['keyName'] = instance['key_name']
1202
 
 
1203
 
            if context.is_admin:
1204
 
                i['keyName'] = '%s (%s, %s)' % (i['keyName'],
1205
 
                    instance['project_id'],
1206
 
                    instance['host'])
1207
 
            i['productCodesSet'] = utils.convert_to_list_dict([],
1208
 
                                                              'product_codes')
1209
 
            self._format_instance_type(instance, i)
1210
 
            i['launchTime'] = instance['created_at']
1211
 
            i['amiLaunchIndex'] = instance['launch_index']
1212
 
            self._format_instance_root_device_name(instance, i)
1213
 
            self._format_instance_bdm(context, instance_id,
1214
 
                                      i['rootDeviceName'], i)
1215
 
            host = instance['host']
1216
 
            services = db.service_get_all_by_host(context.elevated(), host)
1217
 
            zone = ec2utils.get_availability_zone_by_host(services, host)
1218
 
            i['placement'] = {'availabilityZone': zone}
1219
 
            if instance['reservation_id'] not in reservations:
1220
 
                r = {}
1221
 
                r['reservationId'] = instance['reservation_id']
1222
 
                r['ownerId'] = instance['project_id']
1223
 
                self._format_group_set(instance, r)
1224
 
                r['instancesSet'] = []
1225
 
                reservations[instance['reservation_id']] = r
1226
 
            reservations[instance['reservation_id']]['instancesSet'].append(i)
1227
 
 
1228
 
        return list(reservations.values())
1229
 
 
1230
 
    def describe_addresses(self, context, **kwargs):
1231
 
        return self.format_addresses(context)
1232
 
 
1233
 
    def format_addresses(self, context):
1234
 
        addresses = []
1235
 
        floaters = self.network_api.get_floating_ips_by_project(context)
1236
 
        for floating_ip_ref in floaters:
1237
 
            if floating_ip_ref['project_id'] is None:
1238
 
                continue
1239
 
            address = floating_ip_ref['address']
1240
 
            ec2_id = None
1241
 
            if floating_ip_ref['fixed_ip_id']:
1242
 
                fixed_id = floating_ip_ref['fixed_ip_id']
1243
 
                fixed = self.network_api.get_fixed_ip(context, fixed_id)
1244
 
                if fixed['instance_id'] is not None:
1245
 
                    ec2_id = ec2utils.id_to_ec2_id(fixed['instance_id'])
1246
 
            address_rv = {'public_ip': address,
1247
 
                          'instance_id': ec2_id}
1248
 
            if context.is_admin:
1249
 
                details = "%s (%s)" % (address_rv['instance_id'],
1250
 
                                       floating_ip_ref['project_id'])
1251
 
                address_rv['instance_id'] = details
1252
 
            addresses.append(address_rv)
1253
 
        return {'addressesSet': addresses}
1254
 
 
1255
 
    def allocate_address(self, context, **kwargs):
1256
 
        LOG.audit(_("Allocate address"), context=context)
1257
 
        try:
1258
 
            public_ip = self.network_api.allocate_floating_ip(context)
1259
 
            return {'publicIp': public_ip}
1260
 
        except rpc_common.RemoteError as ex:
1261
 
            # NOTE(tr3buchet) - why does this block exist?
1262
 
            if ex.exc_type == 'NoMoreFloatingIps':
1263
 
                raise exception.NoMoreFloatingIps()
1264
 
            else:
1265
 
                raise
1266
 
 
1267
 
    def release_address(self, context, public_ip, **kwargs):
1268
 
        LOG.audit(_("Release address %s"), public_ip, context=context)
1269
 
        self.network_api.release_floating_ip(context, address=public_ip)
1270
 
        return {'return': "true"}
1271
 
 
1272
 
    def associate_address(self, context, instance_id, public_ip, **kwargs):
1273
 
        LOG.audit(_("Associate address %(public_ip)s to"
1274
 
                " instance %(instance_id)s") % locals(), context=context)
1275
 
        instance_id = ec2utils.ec2_id_to_id(instance_id)
1276
 
        instance = self.compute_api.get(context, instance_id)
1277
 
        self.compute_api.associate_floating_ip(context,
1278
 
                                               instance,
1279
 
                                               address=public_ip)
1280
 
        return {'return': "true"}
1281
 
 
1282
 
    def disassociate_address(self, context, public_ip, **kwargs):
1283
 
        LOG.audit(_("Disassociate address %s"), public_ip, context=context)
1284
 
        self.network_api.disassociate_floating_ip(context, address=public_ip)
1285
 
        return {'return': "true"}
1286
 
 
1287
 
    def run_instances(self, context, **kwargs):
1288
 
        max_count = int(kwargs.get('max_count', 1))
1289
 
        if kwargs.get('kernel_id'):
1290
 
            kernel = self._get_image(context, kwargs['kernel_id'])
1291
 
            kwargs['kernel_id'] = ec2utils.id_to_glance_id(context,
1292
 
                                                           kernel['id'])
1293
 
        if kwargs.get('ramdisk_id'):
1294
 
            ramdisk = self._get_image(context, kwargs['ramdisk_id'])
1295
 
            kwargs['ramdisk_id'] = ec2utils.id_to_glance_id(context,
1296
 
                                                            ramdisk['id'])
1297
 
        for bdm in kwargs.get('block_device_mapping', []):
1298
 
            _parse_block_device_mapping(bdm)
1299
 
 
1300
 
        image = self._get_image(context, kwargs['image_id'])
1301
 
        image_uuid = ec2utils.id_to_glance_id(context, image['id'])
1302
 
 
1303
 
        if image:
1304
 
            image_state = self._get_image_state(image)
1305
 
        else:
1306
 
            raise exception.ImageNotFound(image_id=kwargs['image_id'])
1307
 
 
1308
 
        if image_state != 'available':
1309
 
            raise exception.EC2APIError(_('Image must be available'))
1310
 
 
1311
 
        (instances, resv_id) = self.compute_api.create(context,
1312
 
            instance_type=instance_types.get_instance_type_by_name(
1313
 
                kwargs.get('instance_type', None)),
1314
 
            image_href=image_uuid,
1315
 
            min_count=int(kwargs.get('min_count', max_count)),
1316
 
            max_count=max_count,
1317
 
            kernel_id=kwargs.get('kernel_id'),
1318
 
            ramdisk_id=kwargs.get('ramdisk_id'),
1319
 
            key_name=kwargs.get('key_name'),
1320
 
            user_data=kwargs.get('user_data'),
1321
 
            security_group=kwargs.get('security_group'),
1322
 
            availability_zone=kwargs.get('placement', {}).get(
1323
 
                                  'availability_zone'),
1324
 
            block_device_mapping=kwargs.get('block_device_mapping', {}))
1325
 
        return self._format_run_instances(context, resv_id)
1326
 
 
1327
 
    def terminate_instances(self, context, instance_id, **kwargs):
1328
 
        """Terminate each instance in instance_id, which is a list of ec2 ids.
1329
 
        instance_id is a kwarg so its name cannot be modified."""
1330
 
        LOG.debug(_("Going to start terminating instances"))
1331
 
        previous_states = []
1332
 
        for ec2_id in instance_id:
1333
 
            validate_ec2_id(ec2_id)
1334
 
            _instance_id = ec2utils.ec2_id_to_id(ec2_id)
1335
 
            instance = self.compute_api.get(context, _instance_id)
1336
 
            previous_states.append(instance)
1337
 
            self.compute_api.delete(context, instance)
1338
 
        return self._format_terminate_instances(context,
1339
 
                                                instance_id,
1340
 
                                                previous_states)
1341
 
 
1342
 
    def reboot_instances(self, context, instance_id, **kwargs):
1343
 
        """instance_id is a list of instance ids"""
1344
 
        LOG.audit(_("Reboot instance %r"), instance_id, context=context)
1345
 
        for ec2_id in instance_id:
1346
 
            validate_ec2_id(ec2_id)
1347
 
            _instance_id = ec2utils.ec2_id_to_id(ec2_id)
1348
 
            instance = self.compute_api.get(context, _instance_id)
1349
 
            self.compute_api.reboot(context, instance, 'HARD')
1350
 
        return True
1351
 
 
1352
 
    def stop_instances(self, context, instance_id, **kwargs):
1353
 
        """Stop each instances in instance_id.
1354
 
        Here instance_id is a list of instance ids"""
1355
 
        LOG.debug(_("Going to stop instances"))
1356
 
        for ec2_id in instance_id:
1357
 
            validate_ec2_id(ec2_id)
1358
 
            _instance_id = ec2utils.ec2_id_to_id(ec2_id)
1359
 
            instance = self.compute_api.get(context, _instance_id)
1360
 
            self.compute_api.stop(context, instance)
1361
 
        return True
1362
 
 
1363
 
    def start_instances(self, context, instance_id, **kwargs):
1364
 
        """Start each instances in instance_id.
1365
 
        Here instance_id is a list of instance ids"""
1366
 
        LOG.debug(_("Going to start instances"))
1367
 
        for ec2_id in instance_id:
1368
 
            validate_ec2_id(ec2_id)
1369
 
            _instance_id = ec2utils.ec2_id_to_id(ec2_id)
1370
 
            instance = self.compute_api.get(context, _instance_id)
1371
 
            self.compute_api.start(context, instance)
1372
 
        return True
1373
 
 
1374
 
    def _get_image(self, context, ec2_id):
1375
 
        try:
1376
 
            internal_id = ec2utils.ec2_id_to_id(ec2_id)
1377
 
            image = self.image_service.show(context, internal_id)
1378
 
        except (exception.InvalidEc2Id, exception.ImageNotFound):
1379
 
            try:
1380
 
                return self.image_service.show_by_name(context, ec2_id)
1381
 
            except exception.NotFound:
1382
 
                raise exception.ImageNotFound(image_id=ec2_id)
1383
 
        image_type = ec2_id.split('-')[0]
1384
 
        if ec2utils.image_type(image.get('container_format')) != image_type:
1385
 
            raise exception.ImageNotFound(image_id=ec2_id)
1386
 
        return image
1387
 
 
1388
 
    def _format_image(self, image):
1389
 
        """Convert from format defined by GlanceImageService to S3 format."""
1390
 
        i = {}
1391
 
        image_type = ec2utils.image_type(image.get('container_format'))
1392
 
        ec2_id = ec2utils.image_ec2_id(image.get('id'), image_type)
1393
 
        name = image.get('name')
1394
 
        i['imageId'] = ec2_id
1395
 
        kernel_id = image['properties'].get('kernel_id')
1396
 
        if kernel_id:
1397
 
            i['kernelId'] = ec2utils.image_ec2_id(kernel_id, 'aki')
1398
 
        ramdisk_id = image['properties'].get('ramdisk_id')
1399
 
        if ramdisk_id:
1400
 
            i['ramdiskId'] = ec2utils.image_ec2_id(ramdisk_id, 'ari')
1401
 
 
1402
 
        if FLAGS.auth_strategy == 'deprecated':
1403
 
            i['imageOwnerId'] = image['properties'].get('project_id')
1404
 
        else:
1405
 
            i['imageOwnerId'] = image.get('owner')
1406
 
 
1407
 
        img_loc = image['properties'].get('image_location')
1408
 
        if img_loc:
1409
 
            i['imageLocation'] = img_loc
1410
 
        else:
1411
 
            i['imageLocation'] = "%s (%s)" % (img_loc, name)
1412
 
 
1413
 
        i['name'] = name
1414
 
        if not name and img_loc:
1415
 
            # This should only occur for images registered with ec2 api
1416
 
            # prior to that api populating the glance name
1417
 
            i['name'] = img_loc
1418
 
 
1419
 
        i['imageState'] = self._get_image_state(image)
1420
 
        i['description'] = image.get('description')
1421
 
        display_mapping = {'aki': 'kernel',
1422
 
                           'ari': 'ramdisk',
1423
 
                           'ami': 'machine'}
1424
 
        i['imageType'] = display_mapping.get(image_type)
1425
 
        i['isPublic'] = not not image.get('is_public')
1426
 
        i['architecture'] = image['properties'].get('architecture')
1427
 
 
1428
 
        properties = image['properties']
1429
 
        root_device_name = block_device.properties_root_device_name(properties)
1430
 
        root_device_type = 'instance-store'
1431
 
        for bdm in properties.get('block_device_mapping', []):
1432
 
            if (bdm.get('device_name') == root_device_name and
1433
 
                ('snapshot_id' in bdm or 'volume_id' in bdm) and
1434
 
                not bdm.get('no_device')):
1435
 
                root_device_type = 'ebs'
1436
 
        i['rootDeviceName'] = (root_device_name or
1437
 
                               block_device.DEFAULT_ROOT_DEV_NAME)
1438
 
        i['rootDeviceType'] = root_device_type
1439
 
 
1440
 
        _format_mappings(properties, i)
1441
 
 
1442
 
        return i
1443
 
 
1444
 
    def describe_images(self, context, image_id=None, **kwargs):
1445
 
        # NOTE: image_id is a list!
1446
 
        if image_id:
1447
 
            images = []
1448
 
            for ec2_id in image_id:
1449
 
                try:
1450
 
                    image = self._get_image(context, ec2_id)
1451
 
                except exception.NotFound:
1452
 
                    raise exception.ImageNotFound(image_id=ec2_id)
1453
 
                images.append(image)
1454
 
        else:
1455
 
            images = self.image_service.detail(context)
1456
 
        images = [self._format_image(i) for i in images]
1457
 
        return {'imagesSet': images}
1458
 
 
1459
 
    def deregister_image(self, context, image_id, **kwargs):
1460
 
        LOG.audit(_("De-registering image %s"), image_id, context=context)
1461
 
        image = self._get_image(context, image_id)
1462
 
        internal_id = image['id']
1463
 
        self.image_service.delete(context, internal_id)
1464
 
        return {'imageId': image_id}
1465
 
 
1466
 
    def _register_image(self, context, metadata):
1467
 
        image = self.image_service.create(context, metadata)
1468
 
        image_type = ec2utils.image_type(image.get('container_format'))
1469
 
        image_id = ec2utils.image_ec2_id(image['id'], image_type)
1470
 
        return image_id
1471
 
 
1472
 
    def register_image(self, context, image_location=None, **kwargs):
1473
 
        if image_location is None and kwargs.get('name'):
1474
 
            image_location = kwargs['name']
1475
 
        if image_location is None:
1476
 
            raise exception.EC2APIError(_('imageLocation is required'))
1477
 
 
1478
 
        metadata = {'properties': {'image_location': image_location}}
1479
 
 
1480
 
        if kwargs.get('name'):
1481
 
            metadata['name'] = kwargs['name']
1482
 
        else:
1483
 
            metadata['name'] = image_location
1484
 
 
1485
 
        if 'root_device_name' in kwargs:
1486
 
            metadata['properties']['root_device_name'] = kwargs.get(
1487
 
                                                         'root_device_name')
1488
 
 
1489
 
        mappings = [_parse_block_device_mapping(bdm) for bdm in
1490
 
                    kwargs.get('block_device_mapping', [])]
1491
 
        if mappings:
1492
 
            metadata['properties']['block_device_mapping'] = mappings
1493
 
 
1494
 
        image_id = self._register_image(context, metadata)
1495
 
        msg = _("Registered image %(image_location)s with"
1496
 
                " id %(image_id)s") % locals()
1497
 
        LOG.audit(msg, context=context)
1498
 
        return {'imageId': image_id}
1499
 
 
1500
 
    def describe_image_attribute(self, context, image_id, attribute, **kwargs):
1501
 
        def _block_device_mapping_attribute(image, result):
1502
 
            _format_mappings(image['properties'], result)
1503
 
 
1504
 
        def _launch_permission_attribute(image, result):
1505
 
            result['launchPermission'] = []
1506
 
            if image['is_public']:
1507
 
                result['launchPermission'].append({'group': 'all'})
1508
 
 
1509
 
        def _root_device_name_attribute(image, result):
1510
 
            _prop_root_dev_name = block_device.properties_root_device_name
1511
 
            result['rootDeviceName'] = _prop_root_dev_name(image['properties'])
1512
 
            if result['rootDeviceName'] is None:
1513
 
                result['rootDeviceName'] = block_device.DEFAULT_ROOT_DEV_NAME
1514
 
 
1515
 
        supported_attributes = {
1516
 
            'blockDeviceMapping': _block_device_mapping_attribute,
1517
 
            'launchPermission': _launch_permission_attribute,
1518
 
            'rootDeviceName': _root_device_name_attribute,
1519
 
            }
1520
 
 
1521
 
        fn = supported_attributes.get(attribute)
1522
 
        if fn is None:
1523
 
            raise exception.EC2APIError(_('attribute not supported: %s')
1524
 
                                     % attribute)
1525
 
        try:
1526
 
            image = self._get_image(context, image_id)
1527
 
        except exception.NotFound:
1528
 
            raise exception.ImageNotFound(image_id=image_id)
1529
 
 
1530
 
        result = {'imageId': image_id}
1531
 
        fn(image, result)
1532
 
        return result
1533
 
 
1534
 
    def modify_image_attribute(self, context, image_id, attribute,
1535
 
                               operation_type, **kwargs):
1536
 
        # TODO(devcamcar): Support users and groups other than 'all'.
1537
 
        if attribute != 'launchPermission':
1538
 
            raise exception.EC2APIError(_('attribute not supported: %s')
1539
 
                                     % attribute)
1540
 
        if not 'user_group' in kwargs:
1541
 
            raise exception.EC2APIError(_('user or group not specified'))
1542
 
        if len(kwargs['user_group']) != 1 and kwargs['user_group'][0] != 'all':
1543
 
            raise exception.EC2APIError(_('only group "all" is supported'))
1544
 
        if not operation_type in ['add', 'remove']:
1545
 
            msg = _('operation_type must be add or remove')
1546
 
            raise exception.EC2APIError(msg)
1547
 
        LOG.audit(_("Updating image %s publicity"), image_id, context=context)
1548
 
 
1549
 
        try:
1550
 
            image = self._get_image(context, image_id)
1551
 
        except exception.NotFound:
1552
 
            raise exception.ImageNotFound(image_id=image_id)
1553
 
        internal_id = image['id']
1554
 
        del(image['id'])
1555
 
 
1556
 
        image['is_public'] = (operation_type == 'add')
1557
 
        try:
1558
 
            return self.image_service.update(context, internal_id, image)
1559
 
        except exception.ImageNotAuthorized:
1560
 
            msg = _('Not allowed to modify attributes for image %s')
1561
 
            raise exception.EC2APIError(msg % image_id)
1562
 
 
1563
 
    def update_image(self, context, image_id, **kwargs):
1564
 
        internal_id = ec2utils.ec2_id_to_id(image_id)
1565
 
        result = self.image_service.update(context, internal_id, dict(kwargs))
1566
 
        return result
1567
 
 
1568
 
    # TODO(yamahata): race condition
1569
 
    # At the moment there is no way to prevent others from
1570
 
    # manipulating instances/volumes/snapshots.
1571
 
    # As other code doesn't take it into consideration, here we don't
1572
 
    # care of it for now. Ostrich algorithm
1573
 
    def create_image(self, context, instance_id, **kwargs):
1574
 
        # NOTE(yamahata): name/description are ignored by register_image(),
1575
 
        #                 do so here
1576
 
        no_reboot = kwargs.get('no_reboot', False)
1577
 
        validate_ec2_id(instance_id)
1578
 
        ec2_instance_id = instance_id
1579
 
        instance_id = ec2utils.ec2_id_to_id(ec2_instance_id)
1580
 
        instance = self.compute_api.get(context, instance_id)
1581
 
 
1582
 
        # stop the instance if necessary
1583
 
        restart_instance = False
1584
 
        if not no_reboot:
1585
 
            vm_state = instance['vm_state']
1586
 
 
1587
 
            # if the instance is in subtle state, refuse to proceed.
1588
 
            if vm_state not in (vm_states.ACTIVE, vm_states.SHUTOFF,
1589
 
                                vm_states.STOPPED):
1590
 
                raise exception.InstanceNotRunning(instance_id=ec2_instance_id)
1591
 
 
1592
 
            if vm_state in (vm_states.ACTIVE, vm_states.SHUTOFF):
1593
 
                restart_instance = True
1594
 
                self.compute_api.stop(context, instance)
1595
 
 
1596
 
            # wait instance for really stopped
1597
 
            start_time = time.time()
1598
 
            while vm_state != vm_states.STOPPED:
1599
 
                time.sleep(1)
1600
 
                instance = self.compute_api.get(context, instance_id)
1601
 
                vm_state = instance['vm_state']
1602
 
                # NOTE(yamahata): timeout and error. 1 hour for now for safety.
1603
 
                #                 Is it too short/long?
1604
 
                #                 Or is there any better way?
1605
 
                timeout = 1 * 60 * 60 * 60
1606
 
                if time.time() > start_time + timeout:
1607
 
                    raise exception.EC2APIError(
1608
 
                        _('Couldn\'t stop instance with in %d sec') % timeout)
1609
 
 
1610
 
        src_image = self._get_image(context, instance['image_ref'])
1611
 
        properties = src_image['properties']
1612
 
        if instance['root_device_name']:
1613
 
            properties['root_device_name'] = instance['root_device_name']
1614
 
 
1615
 
        mapping = []
1616
 
        bdms = db.block_device_mapping_get_all_by_instance(context,
1617
 
                                                           instance_id)
1618
 
        for bdm in bdms:
1619
 
            if bdm.no_device:
1620
 
                continue
1621
 
            m = {}
1622
 
            for attr in ('device_name', 'snapshot_id', 'volume_id',
1623
 
                         'volume_size', 'delete_on_termination', 'no_device',
1624
 
                         'virtual_name'):
1625
 
                val = getattr(bdm, attr)
1626
 
                if val is not None:
1627
 
                    m[attr] = val
1628
 
 
1629
 
            volume_id = m.get('volume_id')
1630
 
            if m.get('snapshot_id') and volume_id:
1631
 
                # create snapshot based on volume_id
1632
 
                volume = self.volume_api.get(context, volume_id)
1633
 
                # NOTE(yamahata): Should we wait for snapshot creation?
1634
 
                #                 Linux LVM snapshot creation completes in
1635
 
                #                 short time, it doesn't matter for now.
1636
 
                snapshot = self.volume_api.create_snapshot_force(
1637
 
                        context, volume, volume['display_name'],
1638
 
                        volume['display_description'])
1639
 
                m['snapshot_id'] = snapshot['id']
1640
 
                del m['volume_id']
1641
 
 
1642
 
            if m:
1643
 
                mapping.append(m)
1644
 
 
1645
 
        for m in _properties_get_mappings(properties):
1646
 
            virtual_name = m['virtual']
1647
 
            if virtual_name in ('ami', 'root'):
1648
 
                continue
1649
 
 
1650
 
            assert block_device.is_swap_or_ephemeral(virtual_name)
1651
 
            device_name = m['device']
1652
 
            if device_name in [b['device_name'] for b in mapping
1653
 
                               if not b.get('no_device', False)]:
1654
 
                continue
1655
 
 
1656
 
            # NOTE(yamahata): swap and ephemeral devices are specified in
1657
 
            #                 AMI, but disabled for this instance by user.
1658
 
            #                 So disable those device by no_device.
1659
 
            mapping.append({'device_name': device_name, 'no_device': True})
1660
 
 
1661
 
        if mapping:
1662
 
            properties['block_device_mapping'] = mapping
1663
 
 
1664
 
        for attr in ('status', 'location', 'id'):
1665
 
            src_image.pop(attr, None)
1666
 
 
1667
 
        image_id = self._register_image(context, src_image)
1668
 
 
1669
 
        if restart_instance:
1670
 
            self.compute_api.start(context, instance_id=instance_id)
1671
 
 
1672
 
        return {'imageId': image_id}