158
159
server_node = self.find_first_child_named(node, 'server')
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)
168
res_id = server_node.getAttribute('return_reservation_id')
170
server['return_reservation_id'] = utils.bool_from_str(res_id)
172
scheduler_hints = self._extract_scheduler_hints(server_node)
174
server['OS-SCH-HNT:scheduler_hints'] = scheduler_hints
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)
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)
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
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)
203
auto_disk_config = server_node.getAttribute('OS-DCF:diskConfig')
205
server['OS-DCF:diskConfig'] = utils.bool_from_str(auto_disk_config)
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
216
node = self.find_first_child_named(server_node,
217
"os: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
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")
325
if node.hasAttribute("adminPass"):
326
rebuild["adminPass"] = node.getAttribute("adminPass")
328
if node.hasAttribute("accessIPv4"):
329
rebuild["accessIPv4"] = node.getAttribute("accessIPv4")
331
if node.hasAttribute("accessIPv6"):
332
rebuild["accessIPv6"] = node.getAttribute("accessIPv6")
287
336
def _action_resize(self, node):
447
496
search_opts['user_id'] = context.user_id
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)
500
instance_list = self.compute_api.get_all(context,
501
search_opts=search_opts,
504
except exception.MarkerNotFound as e:
505
msg = _('marker [%s] not found') % marker
506
raise webob.exc.HTTPBadRequest(explanation=msg)
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)
504
contents = base64.b64decode(contents)
557
contents = self._decode_base64(contents)
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))
628
# NOTE(vish): Without this regex, b64decode will happily
629
# ignore illegal bytes in the base64 encoded
631
B64_REGEX = re.compile('^(?:[A-Za-z0-9+\/]{4})*'
632
'(?:[A-Za-z0-9+\/]{2}=='
633
'|[A-Za-z0-9+\/]{3}=)?$')
635
def _decode_base64(self, data):
636
data = re.sub(r'\s', '', data)
637
if not self.B64_REGEX.match(data):
640
return base64.b64decode(data)
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:
580
user_data = base64.b64decode(user_data)
648
if self._decode_base64(user_data) is None:
582
649
expl = _('Userdata content cannot be decoded')
583
650
raise exc.HTTPBadRequest(explanation=expl)
613
680
@wsgi.deserializers(xml=CreateDeserializer)
614
681
def create(self, req, body):
615
682
"""Creates a new server for a given user."""
617
raise exc.HTTPUnprocessableEntity()
619
if not 'server' in body:
620
raise exc.HTTPUnprocessableEntity()
622
body['server']['key_name'] = self._get_key_name(req, body)
683
if not self.is_valid_body(body, 'server'):
684
raise exc.HTTPUnprocessableEntity()
624
686
context = req.environ['nova.context']
625
687
server_dict = body['server']
732
794
auto_disk_config = server_dict.get('auto_disk_config')
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', {})
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.
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()
822
raise exc.HTTPUnprocessableEntity()
824
if not 'server' in body:
883
if not self.is_valid_body(body, 'server'):
825
884
raise exc.HTTPUnprocessableEntity()
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,
972
def _get_key_name(self, req, body):
975
return body['server'].get('key_name')
976
except AttributeError:
977
msg = _("Malformed server entity")
978
raise exc.HTTPBadRequest(explanation=msg)
980
1031
def _image_ref_from_req_data(self, data):
982
1033
return unicode(data['server']['imageRef'])
1174
1225
instance = self._get_server(context, req, id)
1227
bdms = self.compute_api.get_instance_bdms(context, instance)
1177
image = self.compute_api.snapshot(context,
1180
extra_properties=props)
1230
if self.compute_api.is_volume_backed_instance(context, instance,
1232
img = instance['image_ref']
1233
src_image = self.compute_api.image_service.show(context, img)
1234
image_meta = dict(src_image)
1236
image = self.compute_api.snapshot_volume_backed(
1241
extra_properties=props)
1243
image = self.compute_api.snapshot(context,
1246
extra_properties=props)
1181
1247
except exception.InstanceInvalidState as state_error:
1182
1248
common.raise_http_conflict_for_instance_invalid_state(state_error,
1185
1251
# build location of newly-created image entity
1186
1252
image_id = str(image['id'])