~ubuntu-branches/ubuntu/quantal/nova/quantal-security

« back to all changes in this revision

Viewing changes to nova/compute/api.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Chuck Short, Adam Gandelman
  • Date: 2012-06-22 12:39:57 UTC
  • mfrom: (1.1.57)
  • Revision ID: package-import@ubuntu.com-20120622123957-hbzwg84nt9rqwg8r
Tags: 2012.2~f2~20120621.14517-0ubuntu1
[ Chuck Short ]
* New upstream version.

[ Adam Gandelman ]
* debian/rules: Temporarily disable test suite while blocking
  tests are investigated. 
* debian/patches/kombu_tests_timeout.patch: Dropped.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
import re
26
26
import string
27
27
import time
 
28
import urllib
28
29
 
29
30
from nova import block_device
30
31
from nova.compute import aggregate_states
42
43
from nova import log as logging
43
44
from nova import network
44
45
from nova import notifications
45
 
from nova.openstack.common import cfg
46
46
from nova.openstack.common import excutils
 
47
from nova.openstack.common import importutils
47
48
from nova.openstack.common import jsonutils
 
49
from nova.openstack.common import timeutils
48
50
import nova.policy
49
51
from nova import quota
50
 
from nova import rpc
51
52
from nova.scheduler import rpcapi as scheduler_rpcapi
52
53
from nova import utils
53
54
from nova import volume
94
95
    return outer
95
96
 
96
97
 
97
 
def wrap_check_policy(func):
 
98
def policy_decorator(scope):
98
99
    """Check corresponding policy prior of wrapped method to execution"""
99
 
    @functools.wraps(func)
100
 
    def wrapped(self, context, target, *args, **kwargs):
101
 
        check_policy(context, func.__name__, target)
102
 
        return func(self, context, target, *args, **kwargs)
103
 
    return wrapped
104
 
 
105
 
 
106
 
def check_policy(context, action, target):
107
 
    _action = 'compute:%s' % action
 
100
    def outer(func):
 
101
        @functools.wraps(func)
 
102
        def wrapped(self, context, target, *args, **kwargs):
 
103
            check_policy(context, func.__name__, target, scope)
 
104
            return func(self, context, target, *args, **kwargs)
 
105
        return wrapped
 
106
    return outer
 
107
 
 
108
wrap_check_policy = policy_decorator(scope='compute')
 
109
wrap_check_security_groups_policy = policy_decorator(
 
110
                                     scope='compute:security_groups')
 
111
 
 
112
 
 
113
def check_policy(context, action, target, scope='compute'):
 
114
    _action = '%s:%s' % (scope, action)
108
115
    nova.policy.enforce(context, _action, target)
109
116
 
110
117
 
112
119
    """API for interacting with the compute manager."""
113
120
 
114
121
    def __init__(self, image_service=None, network_api=None, volume_api=None,
115
 
                 **kwargs):
 
122
                 security_group_api=None, **kwargs):
116
123
        self.image_service = (image_service or
117
124
                              nova.image.get_default_image_service())
118
125
 
119
126
        self.network_api = network_api or network.API()
120
127
        self.volume_api = volume_api or volume.API()
 
128
        self.security_group_api = security_group_api or SecurityGroupAPI()
121
129
        self.consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
122
130
        self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
123
131
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()
172
180
            # OK, we exceeded quota; let's figure out why...
173
181
            quotas = exc.kwargs['quotas']
174
182
            usages = exc.kwargs['usages']
 
183
            overs = exc.kwargs['overs']
 
184
 
175
185
            headroom = dict((res, quotas[res] -
176
186
                             (usages[res]['in_use'] + usages[res]['reserved']))
177
187
                            for res in quotas.keys())
 
188
 
 
189
            # Reduce 'allowed' to the minimum supported
178
190
            allowed = headroom['instances']
179
191
            if instance_type['vcpus']:
180
192
                allowed = min(allowed,
187
199
            pid = context.project_id
188
200
            if allowed <= 0:
189
201
                msg = _("Cannot run any more instances of this type.")
190
 
                used = max_count
 
202
                allowed = 0
191
203
            elif min_count <= allowed <= max_count:
192
204
                # We're actually OK, but still need reservations
193
205
                return self._check_num_instances_quota(context, instance_type,
195
207
            else:
196
208
                msg = (_("Can only run %s more instances of this type.") %
197
209
                       allowed)
198
 
                used = max_count - allowed
199
 
            LOG.warn(_("Quota exceeded for %(pid)s,"
 
210
 
 
211
            used = quotas['instances'] - headroom['instances']
 
212
            total_allowed = used + allowed
 
213
            overs = ','.join(overs)
 
214
 
 
215
            LOG.warn(_("%(overs)s quota exceeded for %(pid)s,"
200
216
                  " tried to run %(min_count)s instances. %(msg)s"), locals())
201
 
            raise exception.TooManyInstances(used=used, allowed=max_count)
 
217
            raise exception.TooManyInstances(overs=overs, req=min_count,
 
218
                                             used=used, allowed=total_allowed)
202
219
 
203
220
        return max_count, reservations
204
221
 
306
323
            return value
307
324
 
308
325
        options_from_image = {'os_type': prop('os_type'),
309
 
                              'architecture': prop('arch'),
310
326
                              'vm_mode': prop('vm_mode')}
311
327
 
312
328
        # If instance doesn't have auto_disk_config overridden by request, use
347
363
 
348
364
        block_device_mapping = block_device_mapping or []
349
365
 
 
366
        if instance_type['disabled']:
 
367
            raise exception.InstanceTypeNotFound(
 
368
                    instance_type_id=instance_type['id'])
 
369
 
350
370
        # Check quotas
351
371
        num_instances, quota_reservations = self._check_num_instances_quota(
352
372
                context, instance_type, min_count, max_count)
378
398
        kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk(
379
399
                context, kernel_id, ramdisk_id, image, image_service)
380
400
 
381
 
        self.ensure_default_security_group(context)
 
401
        self.security_group_api.ensure_default(context)
382
402
 
383
403
        if key_data is None and key_name:
384
404
            key_pair = self.db.key_pair_get(context, context.user_id, key_name)
607
627
        # send a state update notification for the initial create to
608
628
        # show it going from non-existent to BUILDING
609
629
        notifications.send_update_with_states(context, instance, None,
610
 
                vm_states.BUILDING, None, None)
 
630
                vm_states.BUILDING, None, None, service="api")
611
631
 
612
632
        for security_group_id in security_groups:
613
633
            self.db.instance_add_security_group(elevated,
760
780
 
761
781
        return (inst_ret_list, reservation_id)
762
782
 
763
 
    def ensure_default_security_group(self, context):
764
 
        """Ensure that a context has a security group.
765
 
 
766
 
        Creates a security group for the security context if it does not
767
 
        already exist.
768
 
 
769
 
        :param context: the security context
770
 
        """
771
 
        try:
772
 
            self.db.security_group_get_by_name(context,
773
 
                                               context.project_id,
774
 
                                               'default')
775
 
        except exception.NotFound:
776
 
            values = {'name': 'default',
777
 
                      'description': 'default',
778
 
                      'user_id': context.user_id,
779
 
                      'project_id': context.project_id}
780
 
            self.db.security_group_create(context, values)
781
 
 
782
 
    def trigger_security_group_rules_refresh(self, context, security_group_id):
783
 
        """Called when a rule is added to or removed from a security_group."""
784
 
 
785
 
        security_group = self.db.security_group_get(context, security_group_id)
786
 
 
787
 
        hosts = set()
788
 
        for instance in security_group['instances']:
789
 
            if instance['host'] is not None:
790
 
                hosts.add(instance['host'])
791
 
 
792
 
        for host in hosts:
793
 
            self.compute_rpcapi.refresh_security_group_rules(context,
794
 
                    security_group.id, host=host)
795
 
 
796
 
    def trigger_security_group_members_refresh(self, context, group_ids):
797
 
        """Called when a security group gains a new or loses a member.
798
 
 
799
 
        Sends an update request to each compute node for whom this is
800
 
        relevant.
801
 
        """
802
 
        # First, we get the security group rules that reference these groups as
803
 
        # the grantee..
804
 
        security_group_rules = set()
805
 
        for group_id in group_ids:
806
 
            security_group_rules.update(
807
 
                self.db.security_group_rule_get_by_security_group_grantee(
808
 
                                                                     context,
809
 
                                                                     group_id))
810
 
 
811
 
        # ..then we distill the security groups to which they belong..
812
 
        security_groups = set()
813
 
        for rule in security_group_rules:
814
 
            security_group = self.db.security_group_get(
815
 
                                                    context,
816
 
                                                    rule['parent_group_id'])
817
 
            security_groups.add(security_group)
818
 
 
819
 
        # ..then we find the instances that are members of these groups..
820
 
        instances = set()
821
 
        for security_group in security_groups:
822
 
            for instance in security_group['instances']:
823
 
                instances.add(instance)
824
 
 
825
 
        # ...then we find the hosts where they live...
826
 
        hosts = set()
827
 
        for instance in instances:
828
 
            if instance['host']:
829
 
                hosts.add(instance['host'])
830
 
 
831
 
        # ...and finally we tell these nodes to refresh their view of this
832
 
        # particular security group.
833
 
        for host in hosts:
834
 
            self.compute_rpcapi.refresh_security_group_members(context,
835
 
                    group_id, host=host)
836
 
 
837
783
    def trigger_provider_fw_rules_refresh(self, context):
838
784
        """Called when a rule is added/removed from a provider firewall"""
839
785
 
840
786
        hosts = [x['host'] for (x, idx)
841
787
                           in self.db.service_get_all_compute_sorted(context)]
842
788
        for host in hosts:
843
 
            rpc.cast(context,
844
 
                     self.db.queue_get_for(context, FLAGS.compute_topic, host),
845
 
                     {'method': 'refresh_provider_fw_rules', 'args': {}})
846
 
 
847
 
    def _is_security_group_associated_with_server(self, security_group,
848
 
                                                  instance_uuid):
849
 
        """Check if the security group is already associated
850
 
           with the instance. If Yes, return True.
851
 
        """
852
 
 
853
 
        if not security_group:
854
 
            return False
855
 
 
856
 
        instances = security_group.get('instances')
857
 
        if not instances:
858
 
            return False
859
 
 
860
 
        for inst in instances:
861
 
            if (instance_uuid == inst['uuid']):
862
 
                return True
863
 
 
864
 
        return False
865
 
 
866
 
    @wrap_check_policy
867
 
    def add_security_group(self, context, instance, security_group_name):
868
 
        """Add security group to the instance"""
869
 
        security_group = self.db.security_group_get_by_name(context,
870
 
                context.project_id,
871
 
                security_group_name)
872
 
 
873
 
        instance_uuid = instance['uuid']
874
 
 
875
 
        #check if the security group is associated with the server
876
 
        if self._is_security_group_associated_with_server(security_group,
877
 
                                                          instance_uuid):
878
 
            raise exception.SecurityGroupExistsForInstance(
879
 
                                        security_group_id=security_group['id'],
880
 
                                        instance_id=instance_uuid)
881
 
 
882
 
        #check if the instance is in running state
883
 
        if instance['power_state'] != power_state.RUNNING:
884
 
            raise exception.InstanceNotRunning(instance_id=instance_uuid)
885
 
 
886
 
        self.db.instance_add_security_group(context.elevated(),
887
 
                                            instance_uuid,
888
 
                                            security_group['id'])
889
 
        # NOTE(comstud): No instance_uuid argument to this compute manager
890
 
        # call
891
 
        self.compute_rpcapi.refresh_security_group_rules(context,
892
 
                security_group['id'], host=instance['host'])
893
 
 
894
 
    @wrap_check_policy
895
 
    def remove_security_group(self, context, instance, security_group_name):
896
 
        """Remove the security group associated with the instance"""
897
 
        security_group = self.db.security_group_get_by_name(context,
898
 
                context.project_id,
899
 
                security_group_name)
900
 
 
901
 
        instance_uuid = instance['uuid']
902
 
 
903
 
        #check if the security group is associated with the server
904
 
        if not self._is_security_group_associated_with_server(security_group,
905
 
                                                              instance_uuid):
906
 
            raise exception.SecurityGroupNotExistsForInstance(
907
 
                                    security_group_id=security_group['id'],
908
 
                                    instance_id=instance_uuid)
909
 
 
910
 
        #check if the instance is in running state
911
 
        if instance['power_state'] != power_state.RUNNING:
912
 
            raise exception.InstanceNotRunning(instance_id=instance_uuid)
913
 
 
914
 
        self.db.instance_remove_security_group(context.elevated(),
915
 
                                               instance_uuid,
916
 
                                               security_group['id'])
917
 
        # NOTE(comstud): No instance_uuid argument to this compute manager
918
 
        # call
919
 
        self.compute_rpcapi.refresh_security_group_rules(context,
920
 
                security_group['id'], host=instance['host'])
 
789
            self.compute_rpcapi.refresh_provider_fw_rules(context, host)
921
790
 
922
791
    @wrap_check_policy
923
792
    def update(self, context, instance, **kwargs):
935
804
        # Update the instance record and send a state update notification
936
805
        # if task or vm state changed
937
806
        old_ref, instance_ref = self.db.instance_update_and_get_original(
938
 
                context, instance["id"], kwargs)
939
 
        notifications.send_update(context, old_ref, instance_ref)
 
807
                context, instance['uuid'], kwargs)
 
808
        notifications.send_update(context, old_ref, instance_ref,
 
809
                service="api")
940
810
 
941
811
        return dict(instance_ref.iteritems())
942
812
 
957
827
        if instance['host']:
958
828
            self.update(context,
959
829
                        instance,
960
 
                        vm_state=vm_states.SOFT_DELETE,
961
830
                        task_state=task_states.POWERING_OFF,
962
 
                        deleted_at=utils.utcnow())
 
831
                        deleted_at=timeutils.utcnow())
963
832
 
964
833
            self.compute_rpcapi.power_off_instance(context, instance)
965
834
        else:
966
835
            LOG.warning(_('No host for instance, deleting immediately'),
967
836
                        instance=instance)
968
837
            try:
969
 
                self.db.instance_destroy(context, instance['id'])
 
838
                self.db.instance_destroy(context, instance['uuid'])
970
839
            except exception.InstanceNotFound:
971
840
                # NOTE(comstud): Race condition.  Instance already gone.
972
841
                pass
980
849
        try:
981
850
            if not instance['host']:
982
851
                # Just update database, nothing else we can do
983
 
                result = self.db.instance_destroy(context, instance['id'])
984
 
                QUOTAS.commit(context, reservations)
985
 
                return result
 
852
                constraint = self.db.constraint(host=self.db.equal_any(host))
 
853
                try:
 
854
                    result = self.db.instance_destroy(context,
 
855
                                                      instance['uuid'],
 
856
                                                      constraint)
 
857
                    QUOTAS.commit(context, reservations)
 
858
                    return result
 
859
                except exception.ConstraintNotMet:
 
860
                    # Refresh to get new host information
 
861
                    instance = self.get(context, instance['uuid'])
986
862
 
987
863
            self.update(context,
988
864
                        instance,
1031
907
    @check_instance_state(vm_state=[vm_states.SOFT_DELETE])
1032
908
    def restore(self, context, instance):
1033
909
        """Restore a previously deleted (but not reclaimed) instance."""
1034
 
        self.update(context,
1035
 
                    instance,
1036
 
                    vm_state=vm_states.ACTIVE,
1037
 
                    task_state=None,
1038
 
                    deleted_at=None)
1039
 
 
1040
910
        if instance['host']:
1041
 
            self.update(context, instance, task_state=task_states.POWERING_ON)
 
911
            self.update(context,
 
912
                        instance,
 
913
                        task_state=task_states.POWERING_ON,
 
914
                        deleted_at=None)
1042
915
            self.compute_rpcapi.power_on_instance(context, instance)
 
916
        else:
 
917
            self.update(context,
 
918
                        instance,
 
919
                        vm_state=vm_states.ACTIVE,
 
920
                        task_state=None,
 
921
                        deleted_at=None)
1043
922
 
1044
923
    @wrap_check_policy
1045
924
    @check_instance_state(vm_state=[vm_states.SOFT_DELETE])
1050
929
    @wrap_check_policy
1051
930
    @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
1052
931
                                    vm_states.RESCUED],
1053
 
                          task_state=[None, task_states.RESIZE_VERIFY])
 
932
                          task_state=[None])
1054
933
    def stop(self, context, instance, do_cast=True):
1055
934
        """Stop an instance."""
1056
935
        instance_uuid = instance["uuid"]
1058
937
 
1059
938
        self.update(context,
1060
939
                    instance,
1061
 
                    vm_state=vm_states.ACTIVE,
1062
940
                    task_state=task_states.STOPPING,
1063
 
                    terminated_at=utils.utcnow(),
1064
941
                    progress=0)
1065
942
 
1066
943
        self.compute_rpcapi.stop_instance(context, instance, cast=do_cast)
1069
946
    @check_instance_state(vm_state=[vm_states.STOPPED, vm_states.SHUTOFF])
1070
947
    def start(self, context, instance):
1071
948
        """Start an instance."""
1072
 
        vm_state = instance["vm_state"]
1073
 
        instance_uuid = instance["uuid"]
1074
949
        LOG.debug(_("Going to try to start instance"), instance=instance)
1075
950
 
1076
 
        if vm_state == vm_states.SHUTOFF:
1077
 
            if instance['shutdown_terminate']:
1078
 
                LOG.warning(_("Instance %(instance_uuid)s is not "
1079
 
                              "stopped. (%(vm_state)s") % locals())
1080
 
                return
1081
 
 
1082
 
            # NOTE(yamahata): nova compute doesn't reap instances
1083
 
            # which initiated shutdown itself. So reap it here.
1084
 
            self.stop(context, instance, do_cast=False)
1085
 
 
1086
951
        self.update(context,
1087
952
                    instance,
1088
 
                    vm_state=vm_states.STOPPED,
1089
953
                    task_state=task_states.STARTING)
1090
954
 
1091
955
        # TODO(yamahata): injected_files isn't supported right now.
1286
1150
                context, instance_uuid, 'task_state', [None], task_state)
1287
1151
 
1288
1152
        notifications.send_update_with_states(context, instance, old_vm_state,
1289
 
                instance["vm_state"], old_task_state, instance["task_state"])
 
1153
                instance["vm_state"], old_task_state, instance["task_state"],
 
1154
                service="api")
1290
1155
 
1291
1156
        properties = {
1292
1157
            'instance_uuid': instance_uuid,
1338
1203
    @wrap_check_policy
1339
1204
    @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
1340
1205
                                    vm_states.RESCUED],
1341
 
                          task_state=[None, task_states.RESIZE_VERIFY])
 
1206
                          task_state=[None])
1342
1207
    def reboot(self, context, instance, reboot_type):
1343
1208
        """Reboot the given instance."""
1344
1209
        state = {'SOFT': task_states.REBOOTING,
1358
1223
 
1359
1224
    @wrap_check_policy
1360
1225
    @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
1361
 
                          task_state=[None, task_states.RESIZE_VERIFY])
 
1226
                          task_state=[None])
1362
1227
    def rebuild(self, context, instance, image_href, admin_password, **kwargs):
1363
1228
        """Rebuild the given instance with the provided attributes."""
1364
1229
 
1496
1361
        new_instance_type_name = new_instance_type['name']
1497
1362
        LOG.debug(_("Old instance type %(current_instance_type_name)s, "
1498
1363
                " new instance type %(new_instance_type_name)s") % locals())
 
1364
 
 
1365
        # FIXME(sirp): both of these should raise InstanceTypeNotFound instead
1499
1366
        if not new_instance_type:
1500
1367
            raise exception.FlavorNotFound(flavor_id=flavor_id)
1501
1368
 
 
1369
        same_instance_type = (current_instance_type['id'] ==
 
1370
                              new_instance_type['id'])
 
1371
 
 
1372
        # NOTE(sirp): We don't want to force a customer to change their flavor
 
1373
        # when Ops is migrating off of a failed host.
 
1374
        if new_instance_type['disabled'] and not same_instance_type:
 
1375
            raise exception.FlavorNotFound(flavor_id=flavor_id)
 
1376
 
1502
1377
        # NOTE(markwash): look up the image early to avoid auth problems later
1503
1378
        image = self.image_service.show(context, instance['image_ref'])
1504
1379
 
1551
1426
    @wrap_check_policy
1552
1427
    @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
1553
1428
                                    vm_states.RESCUED],
1554
 
                          task_state=[None, task_states.RESIZE_VERIFY])
 
1429
                          task_state=[None])
1555
1430
    def pause(self, context, instance):
1556
1431
        """Pause the given instance."""
1557
1432
        self.update(context,
1578
1453
    @wrap_check_policy
1579
1454
    @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
1580
1455
                                    vm_states.RESCUED],
1581
 
                          task_state=[None, task_states.RESIZE_VERIFY])
 
1456
                          task_state=[None])
1582
1457
    def suspend(self, context, instance):
1583
1458
        """Suspend the given instance."""
1584
1459
        self.update(context,
1600
1475
    @wrap_check_policy
1601
1476
    @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
1602
1477
                                    vm_states.STOPPED],
1603
 
                          task_state=[None, task_states.RESIZE_VERIFY])
 
1478
                          task_state=[None])
1604
1479
    def rescue(self, context, instance, rescue_password=None):
1605
1480
        """Rescue the given instance."""
1606
1481
        self.update(context,
1622
1497
        self.compute_rpcapi.unrescue_instance(context, instance=instance)
1623
1498
 
1624
1499
    @wrap_check_policy
1625
 
    @check_instance_state(vm_state=[vm_states.ACTIVE])
 
1500
    @check_instance_state(vm_state=[vm_states.ACTIVE],
 
1501
                          task_state=[None])
1626
1502
    def set_admin_password(self, context, instance, password=None):
1627
1503
        """Set the root/admin password for the given instance."""
1628
1504
        self.update(context,
1652
1528
 
1653
1529
    @wrap_check_policy
1654
1530
    def get_console_output(self, context, instance, tail_length=None):
1655
 
        """Get console output for an an instance."""
 
1531
        """Get console output for an instance."""
1656
1532
        return self.compute_rpcapi.get_console_output(context,
1657
1533
                instance=instance, tail_length=tail_length)
1658
1534
 
1713
1589
        return instance
1714
1590
 
1715
1591
    @wrap_check_policy
1716
 
    def associate_floating_ip(self, context, instance, address):
1717
 
        """Makes calls to network_api to associate_floating_ip.
1718
 
 
1719
 
        :param address: is a string floating ip address
1720
 
        """
1721
 
        instance_uuid = instance['uuid']
1722
 
 
1723
 
        # TODO(tr3buchet): currently network_info doesn't contain floating IPs
1724
 
        # in its info, if this changes, the next few lines will need to
1725
 
        # accommodate the info containing floating as well as fixed ip
1726
 
        # addresses
1727
 
        nw_info = self.network_api.get_instance_nw_info(context.elevated(),
1728
 
                                                        instance)
1729
 
 
1730
 
        if not nw_info:
1731
 
            raise exception.FixedIpNotFoundForInstance(
1732
 
                    instance_id=instance_uuid)
1733
 
 
1734
 
        ips = [ip for ip in nw_info[0].fixed_ips()]
1735
 
 
1736
 
        if not ips:
1737
 
            raise exception.FixedIpNotFoundForInstance(
1738
 
                    instance_id=instance_uuid)
1739
 
 
1740
 
        # TODO(tr3buchet): this will associate the floating IP with the
1741
 
        # first fixed_ip (lowest id) an instance has. This should be
1742
 
        # changed to support specifying a particular fixed_ip if
1743
 
        # multiple exist.
1744
 
        if len(ips) > 1:
1745
 
            msg = _('multiple fixedips exist, using the first: %s')
1746
 
            LOG.warning(msg, ips[0]['address'])
1747
 
 
1748
 
        self.network_api.associate_floating_ip(context,
1749
 
                floating_address=address, fixed_address=ips[0]['address'])
1750
 
 
1751
 
    @wrap_check_policy
1752
1592
    def get_instance_metadata(self, context, instance):
1753
1593
        """Get all metadata associated with an instance."""
1754
 
        rv = self.db.instance_metadata_get(context, instance['id'])
 
1594
        rv = self.db.instance_metadata_get(context, instance['uuid'])
1755
1595
        return dict(rv.iteritems())
1756
1596
 
1757
1597
    @wrap_check_policy
1758
1598
    def delete_instance_metadata(self, context, instance, key):
1759
1599
        """Delete the given metadata item from an instance."""
1760
 
        self.db.instance_metadata_delete(context, instance['id'], key)
 
1600
        self.db.instance_metadata_delete(context, instance['uuid'], key)
1761
1601
 
1762
1602
    @wrap_check_policy
1763
1603
    def update_instance_metadata(self, context, instance,
1775
1615
            _metadata.update(metadata)
1776
1616
 
1777
1617
        self._check_metadata_properties_quota(context, _metadata)
1778
 
        self.db.instance_metadata_update(context, instance['id'],
 
1618
        self.db.instance_metadata_update(context, instance['uuid'],
1779
1619
                                         _metadata, True)
1780
1620
        return _metadata
1781
1621
 
1796
1636
        return self.db.block_device_mapping_get_all_by_instance(context,
1797
1637
                instance['uuid'])
1798
1638
 
 
1639
    @check_instance_state(vm_state=[vm_states.ACTIVE])
 
1640
    def live_migrate(self, context, instance, block_migration,
 
1641
                     disk_over_commit, host):
 
1642
        """Migrate a server lively to a new host."""
 
1643
        instance_uuid = instance["uuid"]
 
1644
        LOG.debug(_("Going to try to live migrate instance"),
 
1645
                  instance=instance)
 
1646
        self.scheduler_rpcapi.live_migration(context,
 
1647
                block_migration,
 
1648
                disk_over_commit,
 
1649
                instance["id"],
 
1650
                host,
 
1651
                topic=FLAGS.compute_topic)
 
1652
 
1799
1653
 
1800
1654
class HostAPI(base.Base):
1801
1655
    def __init__(self):
1814
1668
        """Reboots, shuts down or powers up the host."""
1815
1669
        # NOTE(comstud): No instance_uuid argument to this compute manager
1816
1670
        # call
1817
 
        topic = self.db.queue_get_for(context, FLAGS.compute_topic, host)
1818
1671
        return self.compute_rpcapi.host_power_action(context, action=action,
1819
1672
                host=host)
1820
1673
 
2036
1889
                'fingerprint': key_pair['fingerprint'],
2037
1890
            })
2038
1891
        return rval
 
1892
 
 
1893
 
 
1894
class SecurityGroupAPI(base.Base):
 
1895
    """
 
1896
    Sub-set of the Compute API related to managing security groups
 
1897
    and security group rules
 
1898
    """
 
1899
    def __init__(self, **kwargs):
 
1900
        super(SecurityGroupAPI, self).__init__(**kwargs)
 
1901
        self.security_group_rpcapi = compute_rpcapi.SecurityGroupAPI()
 
1902
        self.sgh = importutils.import_object(FLAGS.security_group_handler)
 
1903
 
 
1904
    def validate_property(self, value, property, allowed):
 
1905
        """
 
1906
        Validate given security group property.
 
1907
 
 
1908
        :param value:          the value to validate, as a string or unicode
 
1909
        :param property:       the property, either 'name' or 'description'
 
1910
        :param allowed:        the range of characters allowed
 
1911
        """
 
1912
 
 
1913
        try:
 
1914
            val = value.strip()
 
1915
        except AttributeError:
 
1916
            msg = _("Security group %s is not a string or unicode") % property
 
1917
            self.raise_invalid_property(msg)
 
1918
        if not val:
 
1919
            msg = _("Security group %s cannot be empty.") % property
 
1920
            self.raise_invalid_property(msg)
 
1921
 
 
1922
        if allowed and not re.match(allowed, val):
 
1923
            # Some validation to ensure that values match API spec.
 
1924
            # - Alphanumeric characters, spaces, dashes, and underscores.
 
1925
            # TODO(Daviey): LP: #813685 extend beyond group_name checking, and
 
1926
            #  probably create a param validator that can be used elsewhere.
 
1927
            msg = (_("Value (%(value)s) for parameter Group%(property)s is "
 
1928
                     "invalid. Content limited to '%(allowed)'.") %
 
1929
                   dict(value=value, allowed=allowed,
 
1930
                        property=property.capitalize()))
 
1931
            self.raise_invalid_property(msg)
 
1932
        if len(val) > 255:
 
1933
            msg = _("Security group %s should not be greater "
 
1934
                            "than 255 characters.") % property
 
1935
            self.raise_invalid_property(msg)
 
1936
 
 
1937
    def ensure_default(self, context):
 
1938
        """Ensure that a context has a security group.
 
1939
 
 
1940
        Creates a security group for the security context if it does not
 
1941
        already exist.
 
1942
 
 
1943
        :param context: the security context
 
1944
        """
 
1945
        try:
 
1946
            self.db.security_group_get_by_name(context,
 
1947
                                               context.project_id,
 
1948
                                               'default')
 
1949
        except exception.NotFound:
 
1950
            values = {'name': 'default',
 
1951
                      'description': 'default',
 
1952
                      'user_id': context.user_id,
 
1953
                      'project_id': context.project_id}
 
1954
            self.db.security_group_create(context, values)
 
1955
 
 
1956
    def create(self, context, name, description):
 
1957
        try:
 
1958
            reservations = QUOTAS.reserve(context, security_groups=1)
 
1959
        except exception.OverQuota:
 
1960
            msg = _("Quota exceeded, too many security groups.")
 
1961
            self.raise_over_quota(msg)
 
1962
 
 
1963
        LOG.audit(_("Create Security Group %s"), name, context=context)
 
1964
 
 
1965
        self.ensure_default(context)
 
1966
 
 
1967
        if self.db.security_group_exists(context, context.project_id, name):
 
1968
            msg = _('Security group %s already exists') % name
 
1969
            self.raise_group_already_exists(msg)
 
1970
 
 
1971
        try:
 
1972
            group = {'user_id': context.user_id,
 
1973
                     'project_id': context.project_id,
 
1974
                     'name': name,
 
1975
                     'description': description}
 
1976
            group_ref = self.db.security_group_create(context, group)
 
1977
            self.sgh.trigger_security_group_create_refresh(context, group)
 
1978
            # Commit the reservation
 
1979
            QUOTAS.commit(context, reservations)
 
1980
        except Exception:
 
1981
            with excutils.save_and_reraise_exception():
 
1982
                QUOTAS.rollback(context, reservations)
 
1983
 
 
1984
        return group_ref
 
1985
 
 
1986
    def get(self, context, name=None, id=None, map_exception=False):
 
1987
        self.ensure_default(context)
 
1988
        try:
 
1989
            if name:
 
1990
                return self.db.security_group_get_by_name(context,
 
1991
                                                          context.project_id,
 
1992
                                                          name)
 
1993
            elif id:
 
1994
                return self.db.security_group_get(context, id)
 
1995
        except exception.NotFound as exp:
 
1996
            if map_exception:
 
1997
                msg = unicode(exp)
 
1998
                self.raise_not_found(msg)
 
1999
            else:
 
2000
                raise
 
2001
 
 
2002
    def list(self, context, names=None, ids=None, project=None):
 
2003
        self.ensure_default(context)
 
2004
 
 
2005
        groups = []
 
2006
        if names or ids:
 
2007
            if names:
 
2008
                for name in names:
 
2009
                    groups.append(self.db.security_group_get_by_name(context,
 
2010
                                                                     project,
 
2011
                                                                     name))
 
2012
            if ids:
 
2013
                for id in ids:
 
2014
                    groups.append(self.db.security_group_get(context, id))
 
2015
 
 
2016
        elif context.is_admin:
 
2017
            groups = self.db.security_group_get_all(context)
 
2018
 
 
2019
        elif project:
 
2020
            groups = self.db.security_group_get_by_project(context, project)
 
2021
 
 
2022
        return groups
 
2023
 
 
2024
    def destroy(self, context, security_group):
 
2025
        if self.db.security_group_in_use(context, security_group.id):
 
2026
            msg = _("Security group is still in use")
 
2027
            self.raise_invalid_group(msg)
 
2028
 
 
2029
        # Get reservations
 
2030
        try:
 
2031
            reservations = QUOTAS.reserve(context, security_groups=-1)
 
2032
        except Exception:
 
2033
            reservations = None
 
2034
            LOG.exception(_("Failed to update usages deallocating "
 
2035
                            "security group"))
 
2036
 
 
2037
        LOG.audit(_("Delete security group %s"), security_group.name,
 
2038
                  context=context)
 
2039
        self.db.security_group_destroy(context, security_group.id)
 
2040
 
 
2041
        self.sgh.trigger_security_group_destroy_refresh(context,
 
2042
                                                        security_group.id)
 
2043
 
 
2044
        # Commit the reservations
 
2045
        if reservations:
 
2046
            QUOTAS.commit(context, reservations)
 
2047
 
 
2048
    def is_associated_with_server(self, security_group, instance_uuid):
 
2049
        """Check if the security group is already associated
 
2050
           with the instance. If Yes, return True.
 
2051
        """
 
2052
 
 
2053
        if not security_group:
 
2054
            return False
 
2055
 
 
2056
        instances = security_group.get('instances')
 
2057
        if not instances:
 
2058
            return False
 
2059
 
 
2060
        for inst in instances:
 
2061
            if (instance_uuid == inst['uuid']):
 
2062
                return True
 
2063
 
 
2064
        return False
 
2065
 
 
2066
    @wrap_check_security_groups_policy
 
2067
    def add_to_instance(self, context, instance, security_group_name):
 
2068
        """Add security group to the instance"""
 
2069
        security_group = self.db.security_group_get_by_name(context,
 
2070
                context.project_id,
 
2071
                security_group_name)
 
2072
 
 
2073
        instance_uuid = instance['uuid']
 
2074
 
 
2075
        #check if the security group is associated with the server
 
2076
        if self.is_associated_with_server(security_group, instance_uuid):
 
2077
            raise exception.SecurityGroupExistsForInstance(
 
2078
                                        security_group_id=security_group['id'],
 
2079
                                        instance_id=instance_uuid)
 
2080
 
 
2081
        #check if the instance is in running state
 
2082
        if instance['power_state'] != power_state.RUNNING:
 
2083
            raise exception.InstanceNotRunning(instance_id=instance_uuid)
 
2084
 
 
2085
        self.db.instance_add_security_group(context.elevated(),
 
2086
                                            instance_uuid,
 
2087
                                            security_group['id'])
 
2088
        params = {"security_group_id": security_group['id']}
 
2089
        # NOTE(comstud): No instance_uuid argument to this compute manager
 
2090
        # call
 
2091
        self.security_group_rpcapi.refresh_security_group_rules(context,
 
2092
                security_group['id'], host=instance['host'])
 
2093
 
 
2094
        self.trigger_handler('instance_add_security_group',
 
2095
                context, instance, security_group_name)
 
2096
 
 
2097
    @wrap_check_security_groups_policy
 
2098
    def remove_from_instance(self, context, instance, security_group_name):
 
2099
        """Remove the security group associated with the instance"""
 
2100
        security_group = self.db.security_group_get_by_name(context,
 
2101
                context.project_id,
 
2102
                security_group_name)
 
2103
 
 
2104
        instance_uuid = instance['uuid']
 
2105
 
 
2106
        #check if the security group is associated with the server
 
2107
        if not self.is_associated_with_server(security_group, instance_uuid):
 
2108
            raise exception.SecurityGroupNotExistsForInstance(
 
2109
                                    security_group_id=security_group['id'],
 
2110
                                    instance_id=instance_uuid)
 
2111
 
 
2112
        #check if the instance is in running state
 
2113
        if instance['power_state'] != power_state.RUNNING:
 
2114
            raise exception.InstanceNotRunning(instance_id=instance_uuid)
 
2115
 
 
2116
        self.db.instance_remove_security_group(context.elevated(),
 
2117
                                               instance_uuid,
 
2118
                                               security_group['id'])
 
2119
        params = {"security_group_id": security_group['id']}
 
2120
        # NOTE(comstud): No instance_uuid argument to this compute manager
 
2121
        # call
 
2122
        self.security_group_rpcapi.refresh_security_group_rules(context,
 
2123
                security_group['id'], host=instance['host'])
 
2124
 
 
2125
        self.trigger_handler('instance_remove_security_group',
 
2126
                context, instance, security_group_name)
 
2127
 
 
2128
    def trigger_handler(self, event, *args):
 
2129
        handle = getattr(self.sgh, 'trigger_%s_refresh' % event)
 
2130
        handle(*args)
 
2131
 
 
2132
    def trigger_rules_refresh(self, context, id):
 
2133
        """Called when a rule is added to or removed from a security_group."""
 
2134
 
 
2135
        security_group = self.db.security_group_get(context, id)
 
2136
 
 
2137
        hosts = set()
 
2138
        for instance in security_group['instances']:
 
2139
            if instance['host'] is not None:
 
2140
                hosts.add(instance['host'])
 
2141
 
 
2142
        for host in hosts:
 
2143
            self.security_group_rpcapi.refresh_security_group_rules(context,
 
2144
                    security_group.id, host=host)
 
2145
 
 
2146
    def trigger_members_refresh(self, context, group_ids):
 
2147
        """Called when a security group gains a new or loses a member.
 
2148
 
 
2149
        Sends an update request to each compute node for whom this is
 
2150
        relevant.
 
2151
        """
 
2152
        # First, we get the security group rules that reference these groups as
 
2153
        # the grantee..
 
2154
        security_group_rules = set()
 
2155
        for group_id in group_ids:
 
2156
            security_group_rules.update(
 
2157
                self.db.security_group_rule_get_by_security_group_grantee(
 
2158
                                                                     context,
 
2159
                                                                     group_id))
 
2160
 
 
2161
        # ..then we distill the security groups to which they belong..
 
2162
        security_groups = set()
 
2163
        for rule in security_group_rules:
 
2164
            security_group = self.db.security_group_get(
 
2165
                                                    context,
 
2166
                                                    rule['parent_group_id'])
 
2167
            security_groups.add(security_group)
 
2168
 
 
2169
        # ..then we find the instances that are members of these groups..
 
2170
        instances = set()
 
2171
        for security_group in security_groups:
 
2172
            for instance in security_group['instances']:
 
2173
                instances.add(instance)
 
2174
 
 
2175
        # ...then we find the hosts where they live...
 
2176
        hosts = set()
 
2177
        for instance in instances:
 
2178
            if instance['host']:
 
2179
                hosts.add(instance['host'])
 
2180
 
 
2181
        # ...and finally we tell these nodes to refresh their view of this
 
2182
        # particular security group.
 
2183
        for host in hosts:
 
2184
            self.security_group_rpcapi.refresh_security_group_members(context,
 
2185
                    group_id, host=host)
 
2186
 
 
2187
    def parse_cidr(self, cidr):
 
2188
        if cidr:
 
2189
            try:
 
2190
                cidr = urllib.unquote(cidr).decode()
 
2191
            except Exception as e:
 
2192
                self.raise_invalid_cidr(cidr, e)
 
2193
 
 
2194
            if not utils.is_valid_cidr(cidr):
 
2195
                self.raise_invalid_cidr(cidr)
 
2196
 
 
2197
            return cidr
 
2198
        else:
 
2199
            return '0.0.0.0/0'
 
2200
 
 
2201
    @staticmethod
 
2202
    def new_group_ingress_rule(grantee_group_id, protocol, from_port,
 
2203
                               to_port):
 
2204
        return SecurityGroupAPI._new_ingress_rule(protocol, from_port,
 
2205
                                to_port, group_id=grantee_group_id)
 
2206
 
 
2207
    @staticmethod
 
2208
    def new_cidr_ingress_rule(grantee_cidr, protocol, from_port, to_port):
 
2209
        return SecurityGroupAPI._new_ingress_rule(protocol, from_port,
 
2210
                                to_port, cidr=grantee_cidr)
 
2211
 
 
2212
    @staticmethod
 
2213
    def _new_ingress_rule(ip_protocol, from_port, to_port,
 
2214
                          group_id=None, cidr=None):
 
2215
        values = {}
 
2216
 
 
2217
        if group_id:
 
2218
            values['group_id'] = group_id
 
2219
            # Open everything if an explicit port range or type/code are not
 
2220
            # specified, but only if a source group was specified.
 
2221
            ip_proto_upper = ip_protocol.upper() if ip_protocol else ''
 
2222
            if (ip_proto_upper == 'ICMP' and
 
2223
                from_port is None and to_port is None):
 
2224
                from_port = -1
 
2225
                to_port = -1
 
2226
            elif (ip_proto_upper in ['TCP', 'UDP'] and from_port is None
 
2227
                  and to_port is None):
 
2228
                from_port = 1
 
2229
                to_port = 65535
 
2230
 
 
2231
        elif cidr:
 
2232
            values['cidr'] = cidr
 
2233
 
 
2234
        if ip_protocol and from_port is not None and to_port is not None:
 
2235
 
 
2236
            ip_protocol = str(ip_protocol)
 
2237
            try:
 
2238
                # Verify integer conversions
 
2239
                from_port = int(from_port)
 
2240
                to_port = int(to_port)
 
2241
            except ValueError:
 
2242
                if ip_protocol.upper() == 'ICMP':
 
2243
                    raise exception.InvalidInput(reason="Type and"
 
2244
                         " Code must be integers for ICMP protocol type")
 
2245
                else:
 
2246
                    raise exception.InvalidInput(reason="To and From ports "
 
2247
                          "must be integers")
 
2248
 
 
2249
            if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']:
 
2250
                raise exception.InvalidIpProtocol(protocol=ip_protocol)
 
2251
 
 
2252
            # Verify that from_port must always be less than
 
2253
            # or equal to to_port
 
2254
            if (ip_protocol.upper() in ['TCP', 'UDP'] and
 
2255
                (from_port > to_port)):
 
2256
                raise exception.InvalidPortRange(from_port=from_port,
 
2257
                      to_port=to_port, msg="Former value cannot"
 
2258
                                            " be greater than the later")
 
2259
 
 
2260
            # Verify valid TCP, UDP port ranges
 
2261
            if (ip_protocol.upper() in ['TCP', 'UDP'] and
 
2262
                (from_port < 1 or to_port > 65535)):
 
2263
                raise exception.InvalidPortRange(from_port=from_port,
 
2264
                      to_port=to_port, msg="Valid TCP ports should"
 
2265
                                           " be between 1-65535")
 
2266
 
 
2267
            # Verify ICMP type and code
 
2268
            if (ip_protocol.upper() == "ICMP" and
 
2269
                (from_port < -1 or from_port > 255 or
 
2270
                to_port < -1 or to_port > 255)):
 
2271
                raise exception.InvalidPortRange(from_port=from_port,
 
2272
                      to_port=to_port, msg="For ICMP, the"
 
2273
                                           " type:code must be valid")
 
2274
 
 
2275
            values['protocol'] = ip_protocol
 
2276
            values['from_port'] = from_port
 
2277
            values['to_port'] = to_port
 
2278
 
 
2279
        else:
 
2280
            # If cidr based filtering, protocol and ports are mandatory
 
2281
            if cidr:
 
2282
                return None
 
2283
 
 
2284
        return values
 
2285
 
 
2286
    def rule_exists(self, security_group, values):
 
2287
        """Indicates whether the specified rule values are already
 
2288
           defined in the given security group.
 
2289
        """
 
2290
        for rule in security_group.rules:
 
2291
            is_duplicate = True
 
2292
            keys = ('group_id', 'cidr', 'from_port', 'to_port', 'protocol')
 
2293
            for key in keys:
 
2294
                if rule.get(key) != values.get(key):
 
2295
                    is_duplicate = False
 
2296
                    break
 
2297
            if is_duplicate:
 
2298
                return rule.get('id') or True
 
2299
        return False
 
2300
 
 
2301
    def get_rule(self, context, id):
 
2302
        self.ensure_default(context)
 
2303
        try:
 
2304
            return self.db.security_group_rule_get(context, id)
 
2305
        except exception.NotFound:
 
2306
            msg = _("Rule (%s) not found") % id
 
2307
            self.raise_not_found(msg)
 
2308
 
 
2309
    def add_rules(self, context, id, name, vals):
 
2310
        count = QUOTAS.count(context, 'security_group_rules', id)
 
2311
        try:
 
2312
            projected = count + len(vals)
 
2313
            QUOTAS.limit_check(context, security_group_rules=projected)
 
2314
        except exception.OverQuota:
 
2315
            msg = _("Quota exceeded, too many security group rules.")
 
2316
            self.raise_over_quota(msg)
 
2317
 
 
2318
        msg = _("Authorize security group ingress %s")
 
2319
        LOG.audit(msg, name, context=context)
 
2320
 
 
2321
        rules = [self.db.security_group_rule_create(context, v) for v in vals]
 
2322
 
 
2323
        self.trigger_rules_refresh(context, id=id)
 
2324
        self.trigger_handler('security_group_rule_create', context,
 
2325
                             [r['id'] for r in rules])
 
2326
        return rules
 
2327
 
 
2328
    def remove_rules(self, context, security_group, rule_ids):
 
2329
        msg = _("Revoke security group ingress %s")
 
2330
        LOG.audit(msg, security_group['name'], context=context)
 
2331
 
 
2332
        for rule_id in rule_ids:
 
2333
            self.db.security_group_rule_destroy(context, rule_id)
 
2334
 
 
2335
        # NOTE(vish): we removed some rules, so refresh
 
2336
        self.trigger_rules_refresh(context, id=security_group['id'])
 
2337
        self.trigger_handler('security_group_rule_destroy', context, rule_ids)
 
2338
 
 
2339
    @staticmethod
 
2340
    def raise_invalid_property(msg):
 
2341
        raise NotImplementedError()
 
2342
 
 
2343
    @staticmethod
 
2344
    def raise_group_already_exists(msg):
 
2345
        raise NotImplementedError()
 
2346
 
 
2347
    @staticmethod
 
2348
    def raise_invalid_group(msg):
 
2349
        raise NotImplementedError()
 
2350
 
 
2351
    @staticmethod
 
2352
    def raise_invalid_cidr(cidr, decoding_exception=None):
 
2353
        raise NotImplementedError()
 
2354
 
 
2355
    @staticmethod
 
2356
    def raise_over_quota(msg):
 
2357
        raise NotImplementedError()
 
2358
 
 
2359
    @staticmethod
 
2360
    def raise_not_found(msg):
 
2361
        raise NotImplementedError()