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

« back to all changes in this revision

Viewing changes to nova/api/openstack/compute/servers.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-09-09 13:11:11 UTC
  • mfrom: (1.1.74)
  • Revision ID: package-import@ubuntu.com-20130909131111-aw02ice50wac9tma
Tags: 1:2013.2~b3-0ubuntu1
* New usptream release. 
* debian/patches/avoid_requirements_cheetah.patch: Dropped
* debian/patches/fix-sqlalchemy-0.7.9-usage.patch: Dropped
* debian/patches/fix-requirements.patch: Refreshed.
* debian/patches/path-to-the-xenhost.conf-fixup.patch: Refreshed
* debian/control: Add python-jinja2
* debian/control: Dropped python-cheetah

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
from nova.api.openstack.compute.views import servers as views_servers
28
28
from nova.api.openstack import wsgi
29
29
from nova.api.openstack import xmlutil
 
30
from nova import block_device
30
31
from nova import compute
31
32
from nova.compute import flavors
32
33
from nova import exception
33
 
from nova.openstack.common import importutils
 
34
from nova.openstack.common.gettextutils import _
34
35
from nova.openstack.common import log as logging
35
36
from nova.openstack.common.rpc import common as rpc_common
36
37
from nova.openstack.common import strutils
37
38
from nova.openstack.common import timeutils
38
39
from nova.openstack.common import uuidutils
 
40
from nova import policy
39
41
from nova import utils
40
42
 
41
43
 
219
221
        if block_device_mapping is not None:
220
222
            server["block_device_mapping"] = block_device_mapping
221
223
 
 
224
        block_device_mapping_v2 = self._extract_block_device_mapping_v2(
 
225
            server_node)
 
226
        if block_device_mapping_v2 is not None:
 
227
            server["block_device_mapping_v2"] = block_device_mapping_v2
 
228
 
222
229
        # NOTE(vish): Support this incorrect version because it was in the code
223
230
        #             base for a while and we don't want to accidentally break
224
231
        #             anyone that might be using it.
261
268
        else:
262
269
            return None
263
270
 
 
271
    def _extract_block_device_mapping_v2(self, server_node):
 
272
        """Marshal the new block_device_mappings."""
 
273
        node = self.find_first_child_named(server_node,
 
274
                                           "block_device_mapping_v2")
 
275
        if node:
 
276
            block_device_mapping = []
 
277
            for child in self.extract_elements(node):
 
278
                if child.nodeName != "mapping":
 
279
                    continue
 
280
                block_device_mapping.append(
 
281
                    dict((attr, child.getAttribute(attr))
 
282
                        for attr in block_device.bdm_new_api_fields
 
283
                        if child.getAttribute(attr)))
 
284
            return block_device_mapping
 
285
 
264
286
    def _extract_scheduler_hints(self, server_node):
265
287
        """Marshal the scheduler hints attribute of a parsed request."""
266
288
        node = self.find_first_child_named_in_namespace(server_node,
463
485
        super(Controller, self).__init__(**kwargs)
464
486
        self.compute_api = compute.API()
465
487
        self.ext_mgr = ext_mgr
466
 
        self.neutron_attempted = False
467
488
 
468
489
    @wsgi.serializers(xml=MinimalServersTemplate)
469
490
    def index(self, req):
494
515
                self._get_server_search_options())
495
516
 
496
517
        # Verify search by 'status' contains a valid status.
497
 
        # Convert it to filter by vm_state for compute_api.
 
518
        # Convert it to filter by vm_state or task_state for compute_api.
498
519
        status = search_opts.pop('status', None)
499
520
        if status is not None:
500
 
            state = common.vm_state_from_status(status)
501
 
            if state is None:
 
521
            vm_state, task_state = common.task_and_vm_state_from_status(status)
 
522
            if not vm_state and not task_state:
502
523
                return {'servers': []}
503
 
            search_opts['vm_state'] = state
 
524
            search_opts['vm_state'] = vm_state
 
525
            # When we search by vm state, task state will return 'default'.
 
526
            # So we don't need task_state search_opt.
 
527
            if 'default' not in task_state:
 
528
                search_opts['task_state'] = task_state
504
529
 
505
530
        if 'changes-since' in search_opts:
506
531
            try:
580
605
    def _validate_server_name(self, value):
581
606
        self._check_string_length(value, 'Server name', max_length=255)
582
607
 
583
 
    def _validate_int_value(self, str_value, str_name,
584
 
            min_value=None, max_value=None):
585
 
        try:
586
 
            value = int(str(str_value))
587
 
        except ValueError:
588
 
            msg = _('%(value_name)s must be an integer')
589
 
            raise exc.HTTPBadRequest(explanation=msg % (
590
 
                {'value_name': str_name}))
591
 
 
592
 
        if min_value is not None:
593
 
            if value < min_value:
594
 
                msg = _('%(value_name)s must be >= %(min_value)d')
595
 
                raise exc.HTTPBadRequest(explanation=msg % (
596
 
                    {'value_name': str_name,
597
 
                     'min_value': min_value}))
598
 
        if max_value is not None:
599
 
            if value > max_value:
600
 
                msg = _('%{value_name}s must be <= %(max_value)d')
601
 
                raise exc.HTTPBadRequest(explanation=msg % (
602
 
                    {'value_name': str_name,
603
 
                     'max_value': max_value}))
604
 
        return value
605
 
 
606
 
    def _validate_block_device(self, bd):
607
 
        self._check_string_length(bd['device_name'],
608
 
                'Device name', max_length=255)
609
 
 
610
 
        if ' ' in bd['device_name']:
611
 
            msg = _("Device name cannot include spaces.")
612
 
            raise exc.HTTPBadRequest(explanation=msg)
613
 
 
614
 
        if 'volume_size' in bd:
615
 
            self._validate_int_value(bd['volume_size'], 'volume_size',
616
 
                                     min_value=0)
617
 
 
618
608
    def _get_injected_files(self, personality):
619
609
        """Create a list of injected files from the personality attribute.
620
610
 
640
630
            injected_files.append((path, contents))
641
631
        return injected_files
642
632
 
643
 
    def _is_neutron_v2(self):
644
 
        # NOTE(dprince): neutronclient is not a requirement
645
 
        if self.neutron_attempted:
646
 
            return self.have_neutron
647
 
 
648
 
        try:
649
 
            # compatibility with Folsom/Grizzly configs
650
 
            cls_name = CONF.network_api_class
651
 
            if cls_name == 'nova.network.quantumv2.api.API':
652
 
                cls_name = 'nova.network.neutronv2.api.API'
653
 
            self.neutron_attempted = True
654
 
 
655
 
            from nova.network.neutronv2 import api as neutron_api
656
 
            self.have_neutron = issubclass(
657
 
                importutils.import_class(cls_name),
658
 
                neutron_api.API)
659
 
        except ImportError:
660
 
            self.have_neutron = False
661
 
 
662
 
        return self.have_neutron
663
 
 
664
633
    def _get_requested_networks(self, requested_networks):
665
634
        """Create a list of requested networks from the networks attribute."""
666
635
        networks = []
669
638
                port_id = network.get('port', None)
670
639
                if port_id:
671
640
                    network_uuid = None
672
 
                    if not self._is_neutron_v2():
 
641
                    if not utils.is_neutron():
673
642
                        # port parameter is only for neutron v2.0
674
643
                        msg = _("Unknown argment : port")
675
644
                        raise exc.HTTPBadRequest(explanation=msg)
699
668
 
700
669
                # For neutronv2, requestd_networks
701
670
                # should be tuple of (network_uuid, fixed_ip, port_id)
702
 
                if self._is_neutron_v2():
 
671
                if utils.is_neutron():
703
672
                    networks.append((network_uuid, address, port_id))
704
673
                else:
705
674
                    # check if the network id is already present in the list,
812
781
 
813
782
        requested_networks = None
814
783
        if (self.ext_mgr.is_loaded('os-networks')
815
 
                or self._is_neutron_v2()):
 
784
                or utils.is_neutron()):
816
785
            requested_networks = server_dict.get('networks')
817
786
 
818
787
        if requested_networks is not None:
851
820
            availability_zone = server_dict.get('availability_zone')
852
821
 
853
822
        block_device_mapping = None
 
823
        block_device_mapping_v2 = None
 
824
        legacy_bdm = True
854
825
        if self.ext_mgr.is_loaded('os-volumes'):
855
826
            block_device_mapping = server_dict.get('block_device_mapping', [])
856
827
            for bdm in block_device_mapping:
857
 
                # Ignore empty volume size
858
 
                if 'volume_size' in bdm and not bdm['volume_size']:
859
 
                    del bdm['volume_size']
860
 
                self._validate_block_device(bdm)
 
828
                try:
 
829
                    block_device.validate_device_name(bdm.get("device_name"))
 
830
                    block_device.validate_and_default_volume_size(bdm)
 
831
                except exception.InvalidBDMFormat as e:
 
832
                    raise exc.HTTPBadRequest(explanation=e.format_message())
 
833
 
861
834
                if 'delete_on_termination' in bdm:
862
835
                    bdm['delete_on_termination'] = strutils.bool_from_string(
863
836
                        bdm['delete_on_termination'])
864
837
 
 
838
            if self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot'):
 
839
                # Consider the new data format for block device mapping
 
840
                block_device_mapping_v2 = server_dict.get(
 
841
                    'block_device_mapping_v2', [])
 
842
                # NOTE (ndipanov):  Disable usage of both legacy and new
 
843
                #                   block device format in the same request
 
844
                if block_device_mapping and block_device_mapping_v2:
 
845
                    expl = _('Using different block_device_mapping syntaxes '
 
846
                             'is not allowed in the same request.')
 
847
                    raise exc.HTTPBadRequest(explanation=expl)
 
848
 
 
849
                # Assume legacy format
 
850
                legacy_bdm = not bool(block_device_mapping_v2)
 
851
 
 
852
                try:
 
853
                    block_device_mapping_v2 = [
 
854
                        block_device.BlockDeviceDict.from_api(bdm_dict)
 
855
                        for bdm_dict in block_device_mapping_v2]
 
856
                except exception.InvalidBDMFormat as e:
 
857
                    raise exc.HTTPBadRequest(explanation=e.format_message())
 
858
 
 
859
        block_device_mapping = (block_device_mapping or
 
860
                                block_device_mapping_v2)
 
861
 
865
862
        ret_resv_id = False
866
863
        # min_count and max_count are optional.  If they exist, they may come
867
864
        # in as strings.  Verify that they are valid integers and > 0.
874
871
            min_count = server_dict.get('min_count', 1)
875
872
            max_count = server_dict.get('max_count', min_count)
876
873
 
877
 
        min_count = self._validate_int_value(min_count, "min_count",
878
 
                                             min_value=1)
879
 
        max_count = self._validate_int_value(max_count, "max_count",
880
 
                                             min_value=1)
 
874
        try:
 
875
            min_count = utils.validate_integer(
 
876
                min_count, "min_count", min_value=1)
 
877
            max_count = utils.validate_integer(
 
878
                max_count, "max_count", min_value=1)
 
879
        except exception.InvalidInput as e:
 
880
            raise exc.HTTPBadRequest(explanation=e.format_message())
881
881
 
882
882
        if min_count > max_count:
883
883
            msg = _('min_count must be <= max_count')
893
893
 
894
894
        try:
895
895
            _get_inst_type = flavors.get_flavor_by_flavor_id
896
 
            inst_type = _get_inst_type(flavor_id, read_deleted="no")
 
896
            inst_type = _get_inst_type(flavor_id, ctxt=context,
 
897
                                       read_deleted="no")
897
898
 
898
899
            (instances, resv_id) = self.compute_api.create(context,
899
900
                            inst_type,
915
916
                            config_drive=config_drive,
916
917
                            block_device_mapping=block_device_mapping,
917
918
                            auto_disk_config=auto_disk_config,
918
 
                            scheduler_hints=scheduler_hints)
 
919
                            scheduler_hints=scheduler_hints,
 
920
                            legacy_bdm=legacy_bdm)
919
921
        except exception.QuotaError as error:
920
922
            raise exc.HTTPRequestEntityTooLarge(
921
923
                explanation=error.format_message(),
948
950
                exception.InstanceTypeNotFound,
949
951
                exception.InvalidMetadata,
950
952
                exception.InvalidRequest,
951
 
                exception.SecurityGroupNotFound) as error:
 
953
                exception.PortNotFound,
 
954
                exception.SecurityGroupNotFound,
 
955
                exception.InvalidBDM) as error:
952
956
            raise exc.HTTPBadRequest(explanation=error.format_message())
 
957
        except exception.PortInUse as error:
 
958
            raise exc.HTTPConflict(explanation=error.format_message())
953
959
 
954
960
        # If the caller wanted a reservation_id, return it
955
961
        if ret_resv_id:
969
975
    def _delete(self, context, req, instance_uuid):
970
976
        instance = self._get_server(context, req, instance_uuid)
971
977
        if CONF.reclaim_instance_interval:
972
 
            self.compute_api.soft_delete(context, instance)
 
978
            try:
 
979
                self.compute_api.soft_delete(context, instance)
 
980
            except exception.InstanceInvalidState:
 
981
                # Note(yufang521247): instance which has never been active
 
982
                # is not allowed to be soft_deleted. Thus we have to call
 
983
                # delete() to clean up the instance.
 
984
                self.compute_api.delete(context, instance)
973
985
        else:
974
986
            self.compute_api.delete(context, instance)
975
987
 
1018
1030
            instance = self.compute_api.get(ctxt, id,
1019
1031
                                            want_objects=True)
1020
1032
            req.cache_db_instance(instance)
1021
 
            self.compute_api.update(ctxt, instance, **update_dict)
 
1033
            policy.enforce(ctxt, 'compute:update', instance)
 
1034
            instance.update(update_dict)
 
1035
            instance.save()
1022
1036
        except exception.NotFound:
1023
1037
            msg = _("Instance could not be found")
1024
1038
            raise exc.HTTPNotFound(explanation=msg)
1025
1039
 
1026
 
        # FIXME(danms): Until compute_api.update() is object-aware,
1027
 
        # we need to apply the updates to the instance object so
1028
 
        # that views will return the new data
1029
 
        instance.update(update_dict)
1030
 
 
1031
1040
        return self._view_builder.show(req, instance)
1032
1041
 
1033
1042
    @wsgi.response(202)
1164
1173
        """
1165
1174
        image_ref = data['server'].get('imageRef')
1166
1175
        bdm = data['server'].get('block_device_mapping')
 
1176
        bdm_v2 = data['server'].get('block_device_mapping_v2')
1167
1177
 
1168
 
        if not image_ref and bdm and self.ext_mgr.is_loaded('os-volumes'):
 
1178
        if (not image_ref and (
 
1179
                (bdm and self.ext_mgr.is_loaded('os-volumes')) or
 
1180
                (bdm_v2 and
 
1181
                 self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot')))):
1169
1182
            return ''
1170
1183
        else:
1171
1184
            image_href = self._image_ref_from_req_data(data)
1362
1375
            if self.compute_api.is_volume_backed_instance(context, instance,
1363
1376
                                                          bdms):
1364
1377
                img = instance['image_ref']
1365
 
                src_image = self.compute_api.image_service.show(context, img)
1366
 
                image_meta = dict(src_image)
 
1378
                if not img:
 
1379
                    # NOTE(Vincent Hou) The private method
 
1380
                    # _get_bdm_image_metadata only works, when boot
 
1381
                    # device is set to 'vda'. It needs to be fixed later,
 
1382
                    # but tentatively we use it here.
 
1383
                    image_meta = {'properties': self.compute_api.
 
1384
                                    _get_bdm_image_metadata(context, bdms)}
 
1385
                else:
 
1386
                    src_image = self.compute_api.image_service.\
 
1387
                                                show(context, img)
 
1388
                    image_meta = dict(src_image)
1367
1389
 
1368
1390
                image = self.compute_api.snapshot_volume_backed(
1369
1391
                                                       context,