~ubuntu-branches/ubuntu/saucy/heat/saucy-updates

« back to all changes in this revision

Viewing changes to heat/engine/resources/instance.py

  • Committer: Package Import Robot
  • Author(s): James Page, Chuck Short, James Page
  • Date: 2013-08-08 15:23:59 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20130808152359-9jgqjp23kssvc3x9
Tags: 2013.2~b2.a186.g2b4b248-0ubuntu1
[ Chuck Short ]
* debian/patches/rename-quantumclient.patch: Dropped no longer needed. 
* debian/control: Add python-oslo.sphinx

[ James Page ]
* New upstream snapshot.
* d/watch: Updated to track releases on launchpad.
* d/control: Drop BD in pep8, no longer required.
* d/control,rules: Drop use of openstack-pkg-tools, revert use of xz
  compression for debs.
* d/control,*.config,*.templates,po: Drop use of debconf/dbconfig-common
  to configure heat.
* d/*.upstart: Add upstart configurations for Ubuntu.
* d/p/default-kombu.patch: Switch default messaging from qpid to
  kombu.
* d/p/default-sqlite.patch: Use sqlite as default database option.
* d/control: Add python-ceilometerclient to BD's.
* d/rules: Fail package build for unit test failures.
* d/*.install: Directly install configuration files to /etc/heat.
* d/control: Update VCS locations to ubuntu-server-dev branches.
* d/heat-common.{install,manpages}: Include new binaries and associated
  manpages.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
from oslo.config import cfg
24
24
 
 
25
from heat.engine import signal_responder
25
26
from heat.engine import clients
26
27
from heat.engine import resource
27
28
from heat.engine import scheduler
30
31
from heat.common import exception
31
32
from heat.engine.resources.network_interface import NetworkInterface
32
33
 
 
34
from heat.openstack.common.gettextutils import _
33
35
from heat.openstack.common import log as logging
34
36
from heat.openstack.common import uuidutils
35
37
 
36
38
logger = logging.getLogger(__name__)
37
39
 
38
40
 
39
 
class Restarter(resource.Resource):
 
41
class Restarter(signal_responder.SignalResponder):
40
42
    properties_schema = {'InstanceId': {'Type': 'String',
41
43
                                        'Required': True}}
 
44
    attributes_schema = {
 
45
        "AlarmUrl": ("A signed url to handle the alarm. "
 
46
                     "(Heat extension)")
 
47
    }
42
48
 
43
49
    def _find_resource(self, resource_id):
44
50
        '''
50
56
                return resource
51
57
        return None
52
58
 
53
 
    def alarm(self):
 
59
    def handle_signal(self, details=None):
 
60
        if details is None:
 
61
            alarm_state = 'alarm'
 
62
        else:
 
63
            alarm_state = details.get('state', 'alarm').lower()
 
64
 
 
65
        logger.info('%s Alarm, new state %s' % (self.name, alarm_state))
 
66
 
 
67
        if alarm_state != 'alarm':
 
68
            return
 
69
 
54
70
        victim = self._find_resource(self.properties['InstanceId'])
55
 
 
56
71
        if victim is None:
57
72
            logger.info('%s Alarm, can not find instance %s' %
58
73
                       (self.name, self.properties['InstanceId']))
62
77
                    (self.name, victim.name))
63
78
        self.stack.restart_resource(victim.name)
64
79
 
 
80
    def _resolve_attribute(self, name):
 
81
        '''
 
82
        heat extension: "AlarmUrl" returns the url to post to the policy
 
83
        when there is an alarm.
 
84
        '''
 
85
        if name == 'AlarmUrl' and self.resource_id is not None:
 
86
            return unicode(self._get_signed_url())
 
87
 
65
88
 
66
89
class Instance(resource.Resource):
67
90
    # AWS does not require InstanceType but Heat does because the nova
121
144
                         'PublicIp': ('Public IP address of the specified '
122
145
                                      'instance.')}
123
146
 
124
 
    # template keys supported for handle_update, note trailing comma
125
 
    # is required for a single item to get a tuple not a string
126
 
    update_allowed_keys = ('Metadata',)
 
147
    update_allowed_keys = ('Metadata', 'Properties')
 
148
    update_allowed_properties = ('InstanceType',)
127
149
 
128
150
    _deferred_server_statuses = ['BUILD',
129
151
                                 'HARD_REBOOT',
286
308
            security_groups = None
287
309
        return security_groups
288
310
 
289
 
    def handle_create(self):
290
 
        security_groups = self._get_security_groups()
291
 
 
292
 
        userdata = self.properties['UserData'] or ''
293
 
        flavor = self.properties['InstanceType']
294
 
        key_name = self.properties['KeyName']
295
 
        availability_zone = self.properties['AvailabilityZone']
296
 
 
297
 
        keypairs = [k.name for k in self.nova().keypairs.list()]
298
 
        if key_name not in keypairs and key_name is not None:
299
 
            raise exception.UserKeyPairMissing(key_name=key_name)
300
 
 
301
 
        image_name = self.properties['ImageId']
302
 
 
303
 
        image_id = self._get_image_id(image_name)
304
 
 
 
311
    def _get_flavor_id(self, flavor):
305
312
        flavor_id = None
306
313
        flavor_list = self.nova().flavors.list()
307
314
        for o in flavor_list:
310
317
                break
311
318
        if flavor_id is None:
312
319
            raise exception.FlavorMissing(flavor_id=flavor)
 
320
        return flavor_id
 
321
 
 
322
    def _get_keypair(self, key_name):
 
323
        for keypair in self.nova().keypairs.list():
 
324
            if keypair.name == key_name:
 
325
                return keypair
 
326
        raise exception.UserKeyPairMissing(key_name=key_name)
 
327
 
 
328
    def handle_create(self):
 
329
        security_groups = self._get_security_groups()
 
330
 
 
331
        userdata = self.properties['UserData'] or ''
 
332
        flavor = self.properties['InstanceType']
 
333
        availability_zone = self.properties['AvailabilityZone']
 
334
 
 
335
        key_name = self.properties['KeyName']
 
336
        if key_name:
 
337
            # confirm keypair exists
 
338
            self._get_keypair(key_name)
 
339
 
 
340
        image_name = self.properties['ImageId']
 
341
 
 
342
        image_id = self._get_image_id(image_name)
 
343
 
 
344
        flavor_id = self._get_flavor_id(flavor)
313
345
 
314
346
        tags = {}
315
347
        if self.properties['Tags']:
376
408
                self._set_ipaddress(server.networks)
377
409
                volume_attach.start()
378
410
                return volume_attach.done()
 
411
            elif server.status == 'ERROR':
 
412
                delete = scheduler.TaskRunner(self._delete_server, server)
 
413
                delete(wait_time=0.2)
 
414
                exc = exception.Error("Build of server %s failed." %
 
415
                                      server.name)
 
416
                raise exception.ResourceFailure(exc)
379
417
            else:
380
 
                raise exception.Error('%s instance[%s] status[%s]' %
 
418
                exc = exception.Error('%s instance[%s] status[%s]' %
381
419
                                      ('nova reported unexpected',
382
420
                                       self.name, server.status))
 
421
                raise exception.ResourceFailure(exc)
383
422
        else:
384
423
            return volume_attach.step()
385
424
 
396
435
 
397
436
    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
398
437
        if 'Metadata' in tmpl_diff:
399
 
            self.metadata = tmpl_diff.get('Metadata', {})
 
438
            self.metadata = tmpl_diff['Metadata']
 
439
        if 'InstanceType' in prop_diff:
 
440
            flavor = prop_diff['InstanceType']
 
441
            flavor_id = self._get_flavor_id(flavor)
 
442
            server = self.nova().servers.get(self.resource_id)
 
443
            server.resize(flavor_id)
 
444
            scheduler.TaskRunner(self._check_resize, server, flavor)()
 
445
 
 
446
    def _check_resize(self, server, flavor):
 
447
        """
 
448
        Verify that the server is properly resized. If that's the case, confirm
 
449
        the resize, if not raise an error.
 
450
        """
 
451
        yield
 
452
        server.get()
 
453
        while server.status == 'RESIZE':
 
454
            yield
 
455
            server.get()
 
456
        if server.status == 'VERIFY_RESIZE':
 
457
            server.confirm_resize()
 
458
        else:
 
459
            raise exception.Error(
 
460
                "Resizing to '%s' failed, status '%s'" % (
 
461
                    flavor, server.status))
400
462
 
401
463
    def metadata_update(self, new_metadata=None):
402
464
        '''
418
480
        if key_name:
419
481
            keypairs = self.nova().keypairs.list()
420
482
            if not any(k.name == key_name for k in keypairs):
421
 
                return {'Error':
422
 
                        'Provided KeyName is not registered with nova'}
 
483
                raise exception.UserKeyPairMissing(key_name=key_name)
423
484
 
424
485
        # check validity of security groups vs. network interfaces
425
486
        security_groups = self._get_security_groups()
426
487
        if security_groups and self.properties.get('NetworkInterfaces'):
427
 
            return {'Error':
428
 
                    'Cannot define both SecurityGroups/SecurityGroupIds and '
429
 
                    'NetworkInterfaces properties.'}
 
488
            raise exception.ResourcePropertyConflict(
 
489
                'SecurityGroups/SecurityGroupIds',
 
490
                'NetworkInterfaces')
430
491
 
431
492
        # make sure the image exists.
432
493
        image_identifier = self.properties['ImageId']
433
 
        try:
434
 
            self._get_image_id(image_identifier)
435
 
        except exception.ImageNotFound:
436
 
            return {'Error': 'Image %s was not found in glance' %
437
 
                    image_identifier}
438
 
        except exception.NoUniqueImageFound:
439
 
            return {'Error': 'Multiple images were found with name %s' %
440
 
                    image_identifier}
 
494
        self._get_image_id(image_identifier)
441
495
 
442
496
        return
443
497