~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/compute/api.py

  • Committer: Package Import Robot
  • Author(s): Adam Gandelman, Adam Gandelman, Chuck Short
  • Date: 2012-08-27 15:37:18 UTC
  • mfrom: (1.1.60)
  • Revision ID: package-import@ubuntu.com-20120827153718-lj8er44eqqz1gsrj
Tags: 2012.2~rc1~20120827.15815-0ubuntu1
[ Adam Gandelman ]
* New upstream release.

[ Chuck Short ]
* debian/patches/0001-Update-tools-hacking-for-pep8-1.2-and-
  beyond.patch: Dropped we dont run pep8 tests anymore.
* debian/control: Drop pep8 build depends
* debian/*.upstart.in: Make sure we transition correctly from runlevel
  1 to 2. (LP: #820694)

Show diffs side-by-side

added added

removed removed

Lines of Context:
102
102
        if instance['locked'] and not context.is_admin:
103
103
            raise exception.InstanceIsLocked(instance_uuid=instance['uuid'])
104
104
        # NOTE(danms): at this point, we have verified that either
105
 
        # theinstance is not locked, or the user is suffiently endowed
 
105
        # the instance is not locked, or the user is sufficiently endowed
106
106
        # that it doesn't matter. While the following statement may be
107
107
        # interpreted as the "the instance is not locked", it actually
108
108
        # refers to the whole condition.
373
373
               access_ip_v4, access_ip_v6,
374
374
               requested_networks, config_drive,
375
375
               block_device_mapping, auto_disk_config,
376
 
               reservation_id=None, create_instance_here=False,
377
 
               scheduler_hints=None):
 
376
               reservation_id=None, scheduler_hints=None):
378
377
        """Verify all the input parameters regardless of the provisioning
379
378
        strategy being performed and schedule the instance(s) for
380
379
        creation."""
463
462
            'ephemeral_gb': instance_type['ephemeral_gb'],
464
463
            'display_name': display_name,
465
464
            'display_description': display_description or '',
466
 
            'user_data': user_data or '',
 
465
            'user_data': user_data,
467
466
            'key_name': key_name,
468
467
            'key_data': key_data,
469
468
            'locked': False,
495
494
 
496
495
        LOG.debug(_("Going to run %s instances...") % num_instances)
497
496
 
498
 
        if create_instance_here:
499
 
            instance = self.create_db_entry_for_new_instance(
500
 
                    context, instance_type, image, base_options,
501
 
                    security_group, block_device_mapping,
502
 
                    quota_reservations)
503
 
 
504
 
            # Reservations committed; don't double-commit
505
 
            quota_reservations = None
506
 
 
507
 
            # Tells scheduler we created the instance already.
508
 
            base_options['uuid'] = instance['uuid']
509
 
            use_call = False
510
 
        else:
511
 
            # We need to wait for the scheduler to create the instance
512
 
            # DB entries, because the instance *could* be # created in
513
 
            # a child zone.
514
 
            use_call = True
515
 
 
516
497
        filter_properties = dict(scheduler_hints=scheduler_hints)
517
498
        if context.is_admin and forced_host:
518
499
            filter_properties['force_hosts'] = [forced_host]
519
500
 
520
 
        # TODO(comstud): We should use rpc.multicall when we can
521
 
        # retrieve the full instance dictionary from the scheduler.
522
 
        # Otherwise, we could exceed the AMQP max message size limit.
523
 
        # This would require the schedulers' schedule_run_instances
524
 
        # methods to return an iterator vs a list.
525
 
        instances = self._schedule_run_instance(
526
 
                use_call,
527
 
                context, base_options,
528
 
                instance_type,
529
 
                availability_zone, injected_files,
530
 
                admin_password, image,
531
 
                num_instances, requested_networks,
532
 
                block_device_mapping, security_group,
533
 
                filter_properties, quota_reservations)
534
 
 
535
 
        if create_instance_here:
536
 
            return ([instance], reservation_id)
 
501
        instances = []
 
502
        instance_uuids = []
 
503
        try:
 
504
            for i in xrange(num_instances):
 
505
                options = base_options.copy()
 
506
                instance = self.create_db_entry_for_new_instance(
 
507
                        context, instance_type, image, options,
 
508
                        security_group, block_device_mapping)
 
509
                instances.append(instance)
 
510
                instance_uuids.append(instance['uuid'])
 
511
        except Exception:
 
512
            # Clean up as best we can.
 
513
            with excutils.save_and_reraise_exception():
 
514
                try:
 
515
                    for instance_uuid in instance_uuids:
 
516
                        self.db.instance_destroy(context,
 
517
                                instance_uuid)
 
518
                finally:
 
519
                    QUOTAS.rollback(context, quota_reservations)
 
520
 
 
521
        # Commit the reservations
 
522
        QUOTAS.commit(context, quota_reservations)
 
523
 
 
524
        request_spec = {
 
525
            'image': jsonutils.to_primitive(image),
 
526
            'instance_properties': base_options,
 
527
            'instance_type': instance_type,
 
528
            'instance_uuids': instance_uuids,
 
529
            'block_device_mapping': block_device_mapping,
 
530
            'security_group': security_group,
 
531
        }
 
532
 
 
533
        self.scheduler_rpcapi.run_instance(context,
 
534
                request_spec=request_spec,
 
535
                admin_password=admin_password, injected_files=injected_files,
 
536
                requested_networks=requested_networks, is_first_time=True,
 
537
                filter_properties=filter_properties)
 
538
 
537
539
        return (instances, reservation_id)
538
540
 
539
541
    @staticmethod
698
700
    #NOTE(bcwaldon): No policy check since this is only used by scheduler and
699
701
    # the compute api. That should probably be cleaned up, though.
700
702
    def create_db_entry_for_new_instance(self, context, instance_type, image,
701
 
            base_options, security_group, block_device_mapping, reservations):
 
703
            base_options, security_group, block_device_mapping):
702
704
        """Create an entry in the DB for this new instance,
703
705
        including any related table updates (such as security group,
704
706
        etc).
724
726
        notifications.send_update_with_states(context, instance, None,
725
727
                vm_states.BUILDING, None, None, service="api")
726
728
 
727
 
        # Commit the reservations
728
 
        if reservations:
729
 
            QUOTAS.commit(context, reservations)
730
 
 
731
729
        return instance
732
730
 
733
 
    def _schedule_run_instance(self,
734
 
            use_call,
735
 
            context, base_options,
736
 
            instance_type,
737
 
            availability_zone, injected_files,
738
 
            admin_password, image,
739
 
            num_instances,
740
 
            requested_networks,
741
 
            block_device_mapping,
742
 
            security_group,
743
 
            filter_properties,
744
 
            quota_reservations):
745
 
        """Send a run_instance request to the schedulers for processing."""
746
 
 
747
 
        pid = context.project_id
748
 
        uid = context.user_id
749
 
 
750
 
        LOG.debug(_("Sending create to scheduler for %(pid)s/%(uid)s's") %
751
 
                  locals())
752
 
 
753
 
        request_spec = {
754
 
            'image': jsonutils.to_primitive(image),
755
 
            'instance_properties': base_options,
756
 
            'instance_type': instance_type,
757
 
            'num_instances': num_instances,
758
 
            'block_device_mapping': block_device_mapping,
759
 
            'security_group': security_group,
760
 
        }
761
 
 
762
 
        return self.scheduler_rpcapi.run_instance(context,
763
 
                request_spec=request_spec,
764
 
                admin_password=admin_password, injected_files=injected_files,
765
 
                requested_networks=requested_networks, is_first_time=True,
766
 
                filter_properties=filter_properties,
767
 
                reservations=quota_reservations, call=use_call)
768
 
 
769
731
    def _check_create_policies(self, context, availability_zone,
770
732
            requested_networks, block_device_mapping):
771
733
        """Check policies for create()."""
795
757
        scheduler.  The scheduler will determine where the instance(s)
796
758
        go and will handle creating the DB entries.
797
759
 
798
 
        Returns a tuple of (instances, reservation_id) where instances
799
 
        could be 'None' or a list of instance dicts depending on if
800
 
        we waited for information from the scheduler or not.
 
760
        Returns a tuple of (instances, reservation_id)
801
761
        """
802
762
 
803
763
        self._check_create_policies(context, availability_zone,
804
764
                requested_networks, block_device_mapping)
805
765
 
806
 
        # We can create the DB entry for the instance here if we're
807
 
        # only going to create 1 instance.
808
 
        # This speeds up API responses for builds
809
 
        # as we don't need to wait for the scheduler.
810
 
        create_instance_here = max_count == 1 or max_count is None
811
 
 
812
 
        (instances, reservation_id) = self._create_instance(
 
766
        return self._create_instance(
813
767
                               context, instance_type,
814
768
                               image_href, kernel_id, ramdisk_id,
815
769
                               min_count, max_count,
820
774
                               access_ip_v4, access_ip_v6,
821
775
                               requested_networks, config_drive,
822
776
                               block_device_mapping, auto_disk_config,
823
 
                               create_instance_here=create_instance_here,
824
777
                               scheduler_hints=scheduler_hints)
825
778
 
826
 
        if create_instance_here or instances is None:
827
 
            return (instances, reservation_id)
828
 
 
829
 
        inst_ret_list = []
830
 
        for instance in instances:
831
 
            if instance.get('_is_precooked', False):
832
 
                inst_ret_list.append(instance)
833
 
            else:
834
 
                # Scheduler only gives us the 'id'.  We need to pull
835
 
                # in the created instances from the DB
836
 
                instance = self.db.instance_get(context, instance['id'])
837
 
                inst_ret_list.append(dict(instance.iteritems()))
838
 
 
839
 
        return (inst_ret_list, reservation_id)
840
 
 
841
779
    def trigger_provider_fw_rules_refresh(self, context):
842
780
        """Called when a rule is added/removed from a provider firewall"""
843
781
 
1561
1499
 
1562
1500
        request_spec = {
1563
1501
                'instance_type': new_instance_type,
1564
 
                'num_instances': 1,
 
1502
                'instance_uuids': [instance['uuid']],
1565
1503
                'instance_properties': instance}
1566
1504
 
1567
1505
        filter_properties = {'ignore_hosts': []}
1689
1627
    @wrap_check_policy
1690
1628
    def get_vnc_console(self, context, instance, console_type):
1691
1629
        """Get a url to an instance Console."""
 
1630
        if not instance['host']:
 
1631
            raise exception.InstanceNotReady(instance=instance)
 
1632
 
1692
1633
        connect_info = self.compute_rpcapi.get_vnc_console(context,
1693
1634
                instance=instance, console_type=console_type)
1694
1635
 
1783
1724
    def detach_volume(self, context, volume_id):
1784
1725
        """Detach a volume from an instance."""
1785
1726
        volume = self.volume_api.get(context, volume_id)
 
1727
        if volume['attach_status'] == 'detached':
 
1728
            msg = _("Volume must be attached in order to detach.")
 
1729
            raise exception.InvalidVolume(reason=msg)
 
1730
 
1786
1731
        instance_uuid = volume['instance_uuid']
1787
1732
        instance = self.db.instance_get_by_uuid(context.elevated(),
1788
1733
                                                instance_uuid)
1801
1746
    def delete_instance_metadata(self, context, instance, key):
1802
1747
        """Delete the given metadata item from an instance."""
1803
1748
        self.db.instance_metadata_delete(context, instance['uuid'], key)
 
1749
        instance['metadata'] = {}
 
1750
        notifications.send_update(context, instance, instance)
1804
1751
        self.compute_rpcapi.change_instance_metadata(context,
1805
1752
                                                     instance=instance,
1806
1753
                                                     diff={key: ['-']})
1823
1770
            _metadata.update(metadata)
1824
1771
 
1825
1772
        self._check_metadata_properties_quota(context, _metadata)
1826
 
        self.db.instance_metadata_update(context, instance['uuid'],
 
1773
        metadata = self.db.instance_metadata_update(context, instance['uuid'],
1827
1774
                                         _metadata, True)
 
1775
        instance['metadata'] = metadata
 
1776
        notifications.send_update(context, instance, instance)
1828
1777
        diff = utils.diff_dict(orig, _metadata)
1829
1778
        self.compute_rpcapi.change_instance_metadata(context,
1830
1779
                                                     instance=instance,