~ubuntu-cloud-archive/ubuntu/precise/nova/trunk

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Adam Gandelman, Chuck Short, Vishvananda Ishaya
  • Date: 2012-09-20 07:45:50 UTC
  • mfrom: (1.1.62)
  • Revision ID: package-import@ubuntu.com-20120920074550-fzmmmzqcntnw1vu7
Tags: 2012.2~rc1-0ubuntu1
[ Adam Gandelman ]
* Ensure /etc/nova/rootwrap.d/ is only writable by root, ensure
  those permissions on /etc/nova/rootwrap.conf as well as
  all individual filter configurations.

[ Chuck Short ]
* Fix lintian warnings
* debian/*.lograote: compress logfiles when they are rotated. (LP:
  #1049915)
* debian/control: 
  - Suggest ceph-common for nova-volume.
  - Add python-cinderclient as a build depends.

[Vishvananda Ishaya]
* Split up vncproxy and xvpvncproxy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import base64
18
18
import os
 
19
import re
19
20
import socket
20
 
from xml.dom import minidom
21
21
 
22
22
import webob
23
23
from webob import exc
 
24
from xml.dom import minidom
24
25
 
25
26
from nova.api.openstack import common
26
27
from nova.api.openstack.compute import ips
158
159
        server_node = self.find_first_child_named(node, 'server')
159
160
 
160
161
        attributes = ["name", "imageRef", "flavorRef", "adminPass",
161
 
                      "accessIPv4", "accessIPv6", "key_name"]
 
162
                      "accessIPv4", "accessIPv6", "key_name",
 
163
                      "availability_zone", "min_count", "max_count"]
162
164
        for attr in attributes:
163
165
            if server_node.getAttribute(attr):
164
166
                server[attr] = server_node.getAttribute(attr)
165
167
 
 
168
        res_id = server_node.getAttribute('return_reservation_id')
 
169
        if res_id:
 
170
            server['return_reservation_id'] = utils.bool_from_str(res_id)
 
171
 
 
172
        scheduler_hints = self._extract_scheduler_hints(server_node)
 
173
        if scheduler_hints:
 
174
            server['OS-SCH-HNT:scheduler_hints'] = scheduler_hints
 
175
 
166
176
        metadata_node = self.find_first_child_named(server_node, "metadata")
167
177
        if metadata_node is not None:
168
178
            server["metadata"] = self.extract_metadata(metadata_node)
169
179
 
 
180
        user_data_node = self.find_first_child_named(server_node, "user_data")
 
181
        if user_data_node is not None:
 
182
            server["user_data"] = self.extract_text(user_data_node)
 
183
 
170
184
        personality = self._extract_personality(server_node)
171
185
        if personality is not None:
172
186
            server["personality"] = personality
179
193
        if security_groups is not None:
180
194
            server["security_groups"] = security_groups
181
195
 
 
196
        # NOTE(vish): Support this incorrect version because it was in the code
 
197
        #             base for a while and we don't want to accidentally break
 
198
        #             anyone that might be using it.
182
199
        auto_disk_config = server_node.getAttribute('auto_disk_config')
183
200
        if auto_disk_config:
184
 
            server['auto_disk_config'] = utils.bool_from_str(auto_disk_config)
 
201
            server['OS-DCF:diskConfig'] = utils.bool_from_str(auto_disk_config)
 
202
 
 
203
        auto_disk_config = server_node.getAttribute('OS-DCF:diskConfig')
 
204
        if auto_disk_config:
 
205
            server['OS-DCF:diskConfig'] = utils.bool_from_str(auto_disk_config)
185
206
 
186
207
        return server
187
208
 
 
209
    def _extract_scheduler_hints(self, server_node):
 
210
        """Marshal the scheduler hints attribute of a parsed request"""
 
211
        node = self.find_first_child_named(server_node,
 
212
                                           "OS-SCH-HNT:scheduler_hints")
 
213
        # NOTE(vish): Support the os: prefix because it is what we use
 
214
        #             for json, even though OS-SCH-HNT: is more correct
 
215
        if not node:
 
216
            node = self.find_first_child_named(server_node,
 
217
                                               "os:scheduler_hints")
 
218
        if node:
 
219
            scheduler_hints = {}
 
220
            for child in self.extract_elements(node):
 
221
                scheduler_hints.setdefault(child.nodeName, [])
 
222
                value = self.extract_text(child).strip()
 
223
                scheduler_hints[child.nodeName].append(value)
 
224
            return scheduler_hints
 
225
        else:
 
226
            return None
 
227
 
188
228
    def _extract_networks(self, server_node):
189
229
        """Marshal the networks attribute of a parsed request."""
190
230
        node = self.find_first_child_named(server_node, "networks")
282
322
            raise AttributeError("No imageRef was specified in request")
283
323
        rebuild["imageRef"] = node.getAttribute("imageRef")
284
324
 
 
325
        if node.hasAttribute("adminPass"):
 
326
            rebuild["adminPass"] = node.getAttribute("adminPass")
 
327
 
 
328
        if node.hasAttribute("accessIPv4"):
 
329
            rebuild["accessIPv4"] = node.getAttribute("accessIPv4")
 
330
 
 
331
        if node.hasAttribute("accessIPv6"):
 
332
            rebuild["accessIPv6"] = node.getAttribute("accessIPv6")
 
333
 
285
334
        return rebuild
286
335
 
287
336
    def _action_resize(self, node):
447
496
                search_opts['user_id'] = context.user_id
448
497
 
449
498
        limit, marker = common.get_limit_and_marker(req)
450
 
        instance_list = self.compute_api.get_all(context,
451
 
                                                 search_opts=search_opts,
452
 
                                                 limit=limit, marker=marker)
 
499
        try:
 
500
            instance_list = self.compute_api.get_all(context,
 
501
                                                     search_opts=search_opts,
 
502
                                                     limit=limit,
 
503
                                                     marker=marker)
 
504
        except exception.MarkerNotFound as e:
 
505
            msg = _('marker [%s] not found') % marker
 
506
            raise webob.exc.HTTPBadRequest(explanation=msg)
453
507
 
454
508
        if is_detail:
455
509
            self._add_instance_faults(context, instance_list)
500
554
            except TypeError:
501
555
                expl = _('Bad personality format')
502
556
                raise exc.HTTPBadRequest(explanation=expl)
503
 
            try:
504
 
                contents = base64.b64decode(contents)
505
 
            except TypeError:
 
557
            contents = self._decode_base64(contents)
 
558
            if contents is None:
506
559
                expl = _('Personality content for %s cannot be decoded') % path
507
560
                raise exc.HTTPBadRequest(explanation=expl)
508
561
            injected_files.append((path, contents))
572
625
 
573
626
        return networks
574
627
 
 
628
    # NOTE(vish): Without this regex, b64decode will happily
 
629
    #             ignore illegal bytes in the base64 encoded
 
630
    #             data.
 
631
    B64_REGEX = re.compile('^(?:[A-Za-z0-9+\/]{4})*'
 
632
                           '(?:[A-Za-z0-9+\/]{2}=='
 
633
                           '|[A-Za-z0-9+\/]{3}=)?$')
 
634
 
 
635
    def _decode_base64(self, data):
 
636
        data = re.sub(r'\s', '', data)
 
637
        if not self.B64_REGEX.match(data):
 
638
            return None
 
639
        try:
 
640
            return base64.b64decode(data)
 
641
        except TypeError:
 
642
            return None
 
643
 
575
644
    def _validate_user_data(self, user_data):
576
645
        """Check if the user_data is encoded properly."""
577
646
        if not user_data:
578
647
            return
579
 
        try:
580
 
            user_data = base64.b64decode(user_data)
581
 
        except TypeError:
 
648
        if self._decode_base64(user_data) is None:
582
649
            expl = _('Userdata content cannot be decoded')
583
650
            raise exc.HTTPBadRequest(explanation=expl)
584
651
 
613
680
    @wsgi.deserializers(xml=CreateDeserializer)
614
681
    def create(self, req, body):
615
682
        """Creates a new server for a given user."""
616
 
        if not body:
617
 
            raise exc.HTTPUnprocessableEntity()
618
 
 
619
 
        if not 'server' in body:
620
 
            raise exc.HTTPUnprocessableEntity()
621
 
 
622
 
        body['server']['key_name'] = self._get_key_name(req, body)
 
683
        if not self.is_valid_body(body, 'server'):
 
684
            raise exc.HTTPUnprocessableEntity()
623
685
 
624
686
        context = req.environ['nova.context']
625
687
        server_dict = body['server']
732
794
            auto_disk_config = server_dict.get('auto_disk_config')
733
795
 
734
796
        scheduler_hints = {}
735
 
        if self.ext_mgr.is_loaded('os-scheduler-hints'):
 
797
        if self.ext_mgr.is_loaded('OS-SCH-HNT'):
736
798
            scheduler_hints = server_dict.get('scheduler_hints', {})
737
799
 
738
800
        try:
786
848
            msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type,
787
849
                                                 'err_msg': err.value}
788
850
            raise exc.HTTPBadRequest(explanation=msg)
 
851
        except UnicodeDecodeError as error:
 
852
            msg = "UnicodeError: %s" % unicode(error)
 
853
            raise exc.HTTPBadRequest(explanation=msg)
789
854
        # Let the caller deal with unhandled exceptions.
790
855
 
791
856
        # If the caller wanted a reservation_id, return it
815
880
    @wsgi.serializers(xml=ServerTemplate)
816
881
    def update(self, req, id, body):
817
882
        """Update server then pass on to version-specific controller."""
818
 
        if len(req.body) == 0:
819
 
            raise exc.HTTPUnprocessableEntity()
820
 
 
821
 
        if not body:
822
 
            raise exc.HTTPUnprocessableEntity()
823
 
 
824
 
        if not 'server' in body:
 
883
        if not self.is_valid_body(body, 'server'):
825
884
            raise exc.HTTPUnprocessableEntity()
826
885
 
827
886
        ctxt = req.environ['nova.context']
949
1008
        except exception.FlavorNotFound:
950
1009
            msg = _("Unable to locate requested flavor.")
951
1010
            raise exc.HTTPBadRequest(explanation=msg)
952
 
        except exception.CannotResizeToSameSize:
953
 
            msg = _("Resize requires a change in size.")
 
1011
        except exception.CannotResizeToSameFlavor:
 
1012
            msg = _("Resize requires a flavor change.")
954
1013
            raise exc.HTTPBadRequest(explanation=msg)
955
1014
        except exception.InstanceInvalidState as state_error:
956
1015
            common.raise_http_conflict_for_instance_invalid_state(state_error,
969
1028
            common.raise_http_conflict_for_instance_invalid_state(state_error,
970
1029
                    'delete')
971
1030
 
972
 
    def _get_key_name(self, req, body):
973
 
        if 'server' in body:
974
 
            try:
975
 
                return body['server'].get('key_name')
976
 
            except AttributeError:
977
 
                msg = _("Malformed server entity")
978
 
                raise exc.HTTPBadRequest(explanation=msg)
979
 
 
980
1031
    def _image_ref_from_req_data(self, data):
981
1032
        try:
982
1033
            return unicode(data['server']['imageRef'])
1173
1224
 
1174
1225
        instance = self._get_server(context, req, id)
1175
1226
 
 
1227
        bdms = self.compute_api.get_instance_bdms(context, instance)
 
1228
 
1176
1229
        try:
1177
 
            image = self.compute_api.snapshot(context,
1178
 
                                              instance,
1179
 
                                              image_name,
1180
 
                                              extra_properties=props)
 
1230
            if self.compute_api.is_volume_backed_instance(context, instance,
 
1231
                                                          bdms):
 
1232
                img = instance['image_ref']
 
1233
                src_image = self.compute_api.image_service.show(context, img)
 
1234
                image_meta = dict(src_image)
 
1235
 
 
1236
                image = self.compute_api.snapshot_volume_backed(
 
1237
                                                       context,
 
1238
                                                       instance,
 
1239
                                                       image_meta,
 
1240
                                                       image_name,
 
1241
                                                       extra_properties=props)
 
1242
            else:
 
1243
                image = self.compute_api.snapshot(context,
 
1244
                                                  instance,
 
1245
                                                  image_name,
 
1246
                                                  extra_properties=props)
1181
1247
        except exception.InstanceInvalidState as state_error:
1182
1248
            common.raise_http_conflict_for_instance_invalid_state(state_error,
1183
 
                    'createImage')
 
1249
                        'createImage')
1184
1250
 
1185
1251
        # build location of newly-created image entity
1186
1252
        image_id = str(image['id'])