blob: a5cc2916dc57adb17184040bf1b5e5f246a8ee4b [file] [log] [blame]
ivan-zhu09111942013-08-01 08:09:16 +08001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2#
3# Copyright 2012 IBM Corp.
4# Copyright 2013 Hewlett-Packard Development Company, L.P.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19import time
20import urllib
21
22from lxml import etree
23
24from tempest.common.rest_client import RestClientXML
25from tempest.common import waiters
26from tempest import exceptions
27from tempest.openstack.common import log as logging
28from tempest.services.compute.xml.common import Document
29from tempest.services.compute.xml.common import Element
30from tempest.services.compute.xml.common import Text
31from tempest.services.compute.xml.common import xml_to_json
ivan-zhu8f992be2013-07-31 14:56:58 +080032from tempest.services.compute.xml.common import XMLNS_V3
ivan-zhu09111942013-08-01 08:09:16 +080033
34
35LOG = logging.getLogger(__name__)
36
37
38def _translate_ip_xml_json(ip):
39 """
40 Convert the address version to int.
41 """
42 ip = dict(ip)
43 version = ip.get('version')
44 if version:
45 ip['version'] = int(version)
ivan-zhu8f992be2013-07-31 14:56:58 +080046 if ip.get('type'):
47 ip['type'] = ip.get('type')
48 if ip.get('mac_addr'):
49 ip['mac_addr'] = ip.get('mac_addr')
ivan-zhu09111942013-08-01 08:09:16 +080050 return ip
51
52
53def _translate_network_xml_to_json(network):
54 return [_translate_ip_xml_json(ip.attrib)
ivan-zhu8f992be2013-07-31 14:56:58 +080055 for ip in network.findall('{%s}ip' % XMLNS_V3)]
ivan-zhu09111942013-08-01 08:09:16 +080056
57
58def _translate_addresses_xml_to_json(xml_addresses):
59 return dict((network.attrib['id'], _translate_network_xml_to_json(network))
ivan-zhu8f992be2013-07-31 14:56:58 +080060 for network in xml_addresses.findall('{%s}network' % XMLNS_V3))
ivan-zhu09111942013-08-01 08:09:16 +080061
62
63def _translate_server_xml_to_json(xml_dom):
64 """Convert server XML to server JSON.
65
66 The addresses collection does not convert well by the dumb xml_to_json.
67 This method does some pre and post-processing to deal with that.
68
69 Translate XML addresses subtree to JSON.
70
71 Having xml_doc similar to
ivan-zhu8f992be2013-07-31 14:56:58 +080072 <api:server xmlns:api="http://docs.openstack.org/compute/api/v3">
ivan-zhu09111942013-08-01 08:09:16 +080073 <api:addresses>
74 <api:network id="foo_novanetwork">
75 <api:ip version="4" addr="192.168.0.4"/>
76 </api:network>
77 <api:network id="bar_novanetwork">
78 <api:ip version="4" addr="10.1.0.4"/>
79 <api:ip version="6" addr="2001:0:0:1:2:3:4:5"/>
80 </api:network>
81 </api:addresses>
82 </api:server>
83
84 the _translate_server_xml_to_json(etree.fromstring(xml_doc)) should produce
85 something like
86
87 {'addresses': {'bar_novanetwork': [{'addr': '10.1.0.4', 'version': 4},
88 {'addr': '2001:0:0:1:2:3:4:5',
89 'version': 6}],
90 'foo_novanetwork': [{'addr': '192.168.0.4', 'version': 4}]}}
91 """
ivan-zhu8f992be2013-07-31 14:56:58 +080092 nsmap = {'api': XMLNS_V3}
ivan-zhu09111942013-08-01 08:09:16 +080093 addresses = xml_dom.xpath('/api:server/api:addresses', namespaces=nsmap)
94 if addresses:
95 if len(addresses) > 1:
96 raise ValueError('Expected only single `addresses` element.')
97 json_addresses = _translate_addresses_xml_to_json(addresses[0])
98 json = xml_to_json(xml_dom)
99 json['addresses'] = json_addresses
100 else:
101 json = xml_to_json(xml_dom)
ivan-zhu8f992be2013-07-31 14:56:58 +0800102 disk_config = ('{http://docs.openstack.org'
103 '/compute/ext/disk_config/api/v3}disk_config')
ivan-zhu09111942013-08-01 08:09:16 +0800104 terminated_at = ('{http://docs.openstack.org/'
ivan-zhu8f992be2013-07-31 14:56:58 +0800105 'compute/ext/os-server-usage/api/v3}terminated_at')
ivan-zhu09111942013-08-01 08:09:16 +0800106 launched_at = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800107 '/compute/ext/os-server-usage/api/v3}launched_at')
ivan-zhu09111942013-08-01 08:09:16 +0800108 power_state = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800109 '/compute/ext/extended_status/api/v3}power_state')
ivan-zhu09111942013-08-01 08:09:16 +0800110 availability_zone = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800111 '/compute/ext/extended_availability_zone/api/v3}'
ivan-zhu09111942013-08-01 08:09:16 +0800112 'availability_zone')
113 vm_state = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800114 '/compute/ext/extended_status/api/v3}vm_state')
ivan-zhu09111942013-08-01 08:09:16 +0800115 task_state = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800116 '/compute/ext/extended_status/api/v3}task_state')
ivan-zhuf0bf3012013-11-25 16:03:42 +0800117 access_ip_v4 = ('{http://docs.openstack.org/compute/ext/'
118 'os-access-ips/api/v3}access_ip_v4')
119 access_ip_v6 = ('{http://docs.openstack.org/compute/ext/'
120 'os-access-ips/api/v3}access_ip_v6')
ivan-zhu8f992be2013-07-31 14:56:58 +0800121 if disk_config in json:
122 json['os-disk-config:disk_config'] = json.pop(disk_config)
ivan-zhu09111942013-08-01 08:09:16 +0800123 if terminated_at in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800124 json['os-server-usage:terminated_at'] = json.pop(terminated_at)
ivan-zhu09111942013-08-01 08:09:16 +0800125 if launched_at in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800126 json['os-server-usage:launched_at'] = json.pop(launched_at)
ivan-zhu09111942013-08-01 08:09:16 +0800127 if power_state in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800128 json['os-extended-status:power_state'] = json.pop(power_state)
ivan-zhu09111942013-08-01 08:09:16 +0800129 if availability_zone in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800130 json['os-extended-availability-zone:availability_zone'] = json.pop(
131 availability_zone)
ivan-zhu09111942013-08-01 08:09:16 +0800132 if vm_state in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800133 json['os-extended-status:vm_state'] = json.pop(vm_state)
ivan-zhu09111942013-08-01 08:09:16 +0800134 if task_state in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800135 json['os-extended-status:task_state'] = json.pop(task_state)
ivan-zhuf0bf3012013-11-25 16:03:42 +0800136 if access_ip_v4 in json:
137 json['os-access-ips:access_ip_v4'] = json.pop(access_ip_v4)
138 if access_ip_v6 in json:
139 json['os-access-ips:access_ip_v6'] = json.pop(access_ip_v6)
ivan-zhu09111942013-08-01 08:09:16 +0800140 return json
141
142
ivan-zhu8f992be2013-07-31 14:56:58 +0800143class ServersV3ClientXML(RestClientXML):
ivan-zhu09111942013-08-01 08:09:16 +0800144
ivan-zhu8f992be2013-07-31 14:56:58 +0800145 def __init__(self, config, username, password, auth_url,
146 tenant_name=None, auth_version='v2'):
147 super(ServersV3ClientXML, self).__init__(config, username, password,
148 auth_url, tenant_name,
149 auth_version=auth_version)
150 self.service = self.config.compute.catalog_v3_type
ivan-zhu09111942013-08-01 08:09:16 +0800151
152 def _parse_key_value(self, node):
153 """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
154 data = {}
155 for node in node.getchildren():
156 data[node.get('key')] = node.text
157 return data
158
159 def _parse_links(self, node, json):
160 del json['link']
161 json['links'] = []
162 for linknode in node.findall('{http://www.w3.org/2005/Atom}link'):
163 json['links'].append(xml_to_json(linknode))
164
165 def _parse_server(self, body):
166 json = _translate_server_xml_to_json(body)
167
168 if 'metadata' in json and json['metadata']:
169 # NOTE(danms): if there was metadata, we need to re-parse
170 # that as a special type
ivan-zhu8f992be2013-07-31 14:56:58 +0800171 metadata_tag = body.find('{%s}metadata' % XMLNS_V3)
ivan-zhu09111942013-08-01 08:09:16 +0800172 json["metadata"] = self._parse_key_value(metadata_tag)
173 if 'link' in json:
174 self._parse_links(body, json)
175 for sub in ['image', 'flavor']:
176 if sub in json and 'link' in json[sub]:
177 self._parse_links(body, json[sub])
178 return json
179
180 def _parse_xml_virtual_interfaces(self, xml_dom):
181 """
182 Return server's virtual interfaces XML as JSON.
183 """
184 data = {"virtual_interfaces": []}
185 for iface in xml_dom.getchildren():
186 data["virtual_interfaces"].append(
187 {"id": iface.get("id"),
188 "mac_address": iface.get("mac_address")})
189 return data
190
191 def get_server(self, server_id):
192 """Returns the details of an existing server."""
193 resp, body = self.get("servers/%s" % str(server_id), self.headers)
194 server = self._parse_server(etree.fromstring(body))
195 return resp, server
196
197 def lock_server(self, server_id, **kwargs):
198 """Locks the given server."""
199 return self.action(server_id, 'lock', None, **kwargs)
200
201 def unlock_server(self, server_id, **kwargs):
202 """Unlocks the given server."""
203 return self.action(server_id, 'unlock', None, **kwargs)
204
205 def suspend_server(self, server_id, **kwargs):
206 """Suspends the provided server."""
207 return self.action(server_id, 'suspend', None, **kwargs)
208
209 def resume_server(self, server_id, **kwargs):
210 """Un-suspends the provided server."""
211 return self.action(server_id, 'resume', None, **kwargs)
212
213 def pause_server(self, server_id, **kwargs):
214 """Pauses the provided server."""
215 return self.action(server_id, 'pause', None, **kwargs)
216
217 def unpause_server(self, server_id, **kwargs):
218 """Un-pauses the provided server."""
219 return self.action(server_id, 'unpause', None, **kwargs)
220
221 def reset_state(self, server_id, state='error'):
222 """Resets the state of a server to active/error."""
ivan-zhu8f992be2013-07-31 14:56:58 +0800223 return self.action(server_id, 'reset_state', None, state=state)
ivan-zhu09111942013-08-01 08:09:16 +0800224
ivan-zhu2ca89b32013-08-07 22:37:32 +0800225 def shelve_server(self, server_id, **kwargs):
226 """Shelves the provided server."""
227 return self.action(server_id, 'shelve', None, **kwargs)
228
229 def unshelve_server(self, server_id, **kwargs):
230 """Un-shelves the provided server."""
231 return self.action(server_id, 'unshelve', None, **kwargs)
232
ivan-zhu09111942013-08-01 08:09:16 +0800233 def delete_server(self, server_id):
234 """Deletes the given server."""
235 return self.delete("servers/%s" % str(server_id))
236
237 def _parse_array(self, node):
238 array = []
239 for child in node.getchildren():
240 array.append(xml_to_json(child))
241 return array
242
243 def list_servers(self, params=None):
244 url = 'servers'
245 if params:
246 url += '?%s' % urllib.urlencode(params)
247
248 resp, body = self.get(url, self.headers)
249 servers = self._parse_array(etree.fromstring(body))
250 return resp, {"servers": servers}
251
252 def list_servers_with_detail(self, params=None):
253 url = 'servers/detail'
254 if params:
255 url += '?%s' % urllib.urlencode(params)
256
257 resp, body = self.get(url, self.headers)
258 servers = self._parse_array(etree.fromstring(body))
259 return resp, {"servers": servers}
260
ivan-zhu8f992be2013-07-31 14:56:58 +0800261 def update_server(self, server_id, name=None, meta=None, access_ip_v4=None,
262 access_ip_v6=None, disk_config=None):
ivan-zhu09111942013-08-01 08:09:16 +0800263 doc = Document()
264 server = Element("server")
265 doc.append(server)
266
267 if name is not None:
268 server.add_attr("name", name)
ivan-zhuf0bf3012013-11-25 16:03:42 +0800269 if access_ip_v4 or access_ip_v6:
270 server.add_attr('xmlns:os-access-ips',
271 "http://docs.openstack.org/compute/ext/"
272 "os-access-ips/api/v3")
ivan-zhu8f992be2013-07-31 14:56:58 +0800273 if access_ip_v4 is not None:
ivan-zhuf0bf3012013-11-25 16:03:42 +0800274 server.add_attr("os-access-ips:access_ip_v4", access_ip_v4)
ivan-zhu8f992be2013-07-31 14:56:58 +0800275 if access_ip_v6 is not None:
ivan-zhuf0bf3012013-11-25 16:03:42 +0800276 server.add_attr("os-access-ips:access_ip_v6", access_ip_v6)
ivan-zhu50969522013-08-26 11:10:39 +0800277 if disk_config is not None:
278 server.add_attr('xmlns:os-disk-config', "http://docs.openstack.org"
279 "/compute/ext/disk_config/api/v3")
280 server.add_attr("os-disk-config:disk_config", disk_config)
ivan-zhu09111942013-08-01 08:09:16 +0800281 if meta is not None:
282 metadata = Element("metadata")
283 server.append(metadata)
284 for k, v in meta:
285 meta = Element("meta", key=k)
286 meta.append(Text(v))
287 metadata.append(meta)
288
289 resp, body = self.put('servers/%s' % str(server_id),
290 str(doc), self.headers)
291 return resp, xml_to_json(etree.fromstring(body))
292
293 def create_server(self, name, image_ref, flavor_ref, **kwargs):
294 """
295 Creates an instance of a server.
296 name (Required): The name of the server.
297 image_ref (Required): Reference to the image used to build the server.
298 flavor_ref (Required): The flavor used to build the server.
299 Following optional keyword arguments are accepted:
ivan-zhu8f992be2013-07-31 14:56:58 +0800300 admin_password: Sets the initial root password.
ivan-zhu09111942013-08-01 08:09:16 +0800301 key_name: Key name of keypair that was created earlier.
302 meta: A dictionary of values to be used as metadata.
303 personality: A list of dictionaries for files to be injected into
304 the server.
305 security_groups: A list of security group dicts.
306 networks: A list of network dicts with UUID and fixed_ip.
307 user_data: User data for instance.
308 availability_zone: Availability zone in which to launch instance.
ivan-zhu8f992be2013-07-31 14:56:58 +0800309 access_ip_v4: The IPv4 access address for the server.
310 access_ip_v6: The IPv6 access address for the server.
ivan-zhu09111942013-08-01 08:09:16 +0800311 min_count: Count of minimum number of instances to launch.
312 max_count: Count of maximum number of instances to launch.
313 disk_config: Determines if user or admin controls disk configuration.
ivan-zhu8f992be2013-07-31 14:56:58 +0800314 return_reservation_id: Enable/Disable the return of reservation id.
ivan-zhu09111942013-08-01 08:09:16 +0800315 """
316 server = Element("server",
ivan-zhu8f992be2013-07-31 14:56:58 +0800317 xmlns=XMLNS_V3,
318 flavor_ref=flavor_ref,
319 image_ref=image_ref,
ivan-zhu09111942013-08-01 08:09:16 +0800320 name=name)
ivan-zhu2ca89b32013-08-07 22:37:32 +0800321 attrs = ["admin_password", "key_name",
322 ('os-access-ips:access_ip_v4',
323 'access_ip_v4',
324 'xmlns:os-access-ips',
325 "http://docs.openstack.org/compute/ext/"
326 "os-access-ips/api/v3"),
327 ('os-access-ips:access_ip_v6',
328 'access_ip_v6',
329 'xmlns:os-access-ips',
330 "http://docs.openstack.org/compute/ext/"
331 "os-access-ips/api/v3"),
ivan-zhu8f992be2013-07-31 14:56:58 +0800332 ("os-user-data:user_data",
333 'user_data',
334 'xmlns:os-user-data',
335 "http://docs.openstack.org/compute/ext/userdata/api/v3"),
336 ("os-availability-zone:availability_zone",
337 'availability_zone',
338 'xmlns:os-availability-zone',
339 "http://docs.openstack.org/compute/ext/"
340 "availabilityzone/api/v3"),
341 ("os-multiple-create:min_count",
342 'min_count',
343 'xmlns:os-multiple-create',
344 "http://docs.openstack.org/compute/ext/"
345 "multiplecreate/api/v3"),
346 ("os-multiple-create:max_count",
347 'max_count',
348 'xmlns:os-multiple-create',
349 "http://docs.openstack.org/compute/ext/"
350 "multiplecreate/api/v3"),
351 ("os-multiple-create:return_reservation_id",
352 "return_reservation_id",
353 'xmlns:os-multiple-create',
354 "http://docs.openstack.org/compute/ext/"
355 "multiplecreate/api/v3"),
356 ("os-disk-config:disk_config",
357 "disk_config",
358 "xmlns:os-disk-config",
359 "http://docs.openstack.org/"
360 "compute/ext/disk_config/api/v3")]
ivan-zhu09111942013-08-01 08:09:16 +0800361
ivan-zhu8f992be2013-07-31 14:56:58 +0800362 for attr in attrs:
363 if isinstance(attr, tuple):
364 post_param = attr[0]
365 key = attr[1]
366 value = kwargs.get(key)
367 if value is not None:
368 server.add_attr(attr[2], attr[3])
369 server.add_attr(post_param, value)
370 else:
371 post_param = attr
372 key = attr
373 value = kwargs.get(key)
374 if value is not None:
375 server.add_attr(post_param, value)
ivan-zhu09111942013-08-01 08:09:16 +0800376
377 if 'security_groups' in kwargs:
ivan-zhu2ca89b32013-08-07 22:37:32 +0800378 server.add_attr("xmlns:os-security-groups",
379 "http://docs.openstack.org/compute/ext/"
380 "securitygroups/api/v3")
381 secgroups = Element("os-security-groups:security_groups")
ivan-zhu09111942013-08-01 08:09:16 +0800382 server.append(secgroups)
383 for secgroup in kwargs['security_groups']:
384 s = Element("security_group", name=secgroup['name'])
385 secgroups.append(s)
386
387 if 'networks' in kwargs:
388 networks = Element("networks")
389 server.append(networks)
390 for network in kwargs['networks']:
391 s = Element("network", uuid=network['uuid'],
392 fixed_ip=network['fixed_ip'])
393 networks.append(s)
394
395 if 'meta' in kwargs:
396 metadata = Element("metadata")
397 server.append(metadata)
398 for k, v in kwargs['meta'].items():
399 meta = Element("meta", key=k)
400 meta.append(Text(v))
401 metadata.append(meta)
402
403 if 'personality' in kwargs:
404 personality = Element('personality')
405 server.append(personality)
406 for k in kwargs['personality']:
407 temp = Element('file', path=k['path'])
408 temp.append(Text(k['contents']))
409 personality.append(temp)
410
411 resp, body = self.post('servers', str(Document(server)), self.headers)
412 server = self._parse_server(etree.fromstring(body))
413 return resp, server
414
Ken'ichi Ohmichi39437e22013-10-06 00:21:38 +0900415 def wait_for_server_status(self, server_id, status, extra_timeout=0):
ivan-zhu09111942013-08-01 08:09:16 +0800416 """Waits for a server to reach a given status."""
Ken'ichi Ohmichi39437e22013-10-06 00:21:38 +0900417 return waiters.wait_for_server_status(self, server_id, status,
418 extra_timeout=extra_timeout)
ivan-zhu09111942013-08-01 08:09:16 +0800419
420 def wait_for_server_termination(self, server_id, ignore_error=False):
421 """Waits for server to reach termination."""
422 start_time = int(time.time())
423 while True:
424 try:
425 resp, body = self.get_server(server_id)
426 except exceptions.NotFound:
427 return
428
429 server_status = body['status']
430 if server_status == 'ERROR' and not ignore_error:
431 raise exceptions.BuildErrorException
432
433 if int(time.time()) - start_time >= self.build_timeout:
434 raise exceptions.TimeoutException
435
436 time.sleep(self.build_interval)
437
438 def _parse_network(self, node):
439 addrs = []
440 for child in node.getchildren():
441 addrs.append({'version': int(child.get('version')),
442 'addr': child.get('addr')})
443 return {node.get('id'): addrs}
444
445 def list_addresses(self, server_id):
446 """Lists all addresses for a server."""
447 resp, body = self.get("servers/%s/ips" % str(server_id), self.headers)
448
449 networks = {}
450 xml_list = etree.fromstring(body)
451 for child in xml_list.getchildren():
452 network = self._parse_network(child)
453 networks.update(**network)
454
455 return resp, networks
456
457 def list_addresses_by_network(self, server_id, network_id):
458 """Lists all addresses of a specific network type for a server."""
459 resp, body = self.get("servers/%s/ips/%s" % (str(server_id),
460 network_id),
461 self.headers)
462 network = self._parse_network(etree.fromstring(body))
463
464 return resp, network
465
466 def action(self, server_id, action_name, response_key, **kwargs):
467 if 'xmlns' not in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800468 kwargs['xmlns'] = XMLNS_V3
ivan-zhu09111942013-08-01 08:09:16 +0800469 doc = Document((Element(action_name, **kwargs)))
470 resp, body = self.post("servers/%s/action" % server_id,
471 str(doc), self.headers)
472 if response_key is not None:
473 body = xml_to_json(etree.fromstring(body))
474 return resp, body
475
ivan-zhu2ca89b32013-08-07 22:37:32 +0800476 def create_backup(self, server_id, backup_type, rotation, name):
477 """Backup a server instance."""
478 return self.action(server_id, "create_backup", None,
479 backup_type=backup_type,
480 rotation=rotation,
481 name=name)
482
ivan-zhu09111942013-08-01 08:09:16 +0800483 def change_password(self, server_id, password):
ivan-zhu8f992be2013-07-31 14:56:58 +0800484 return self.action(server_id, "change_password", None,
Ken'ichi Ohmichie985ca82013-11-12 15:10:35 +0900485 admin_password=password)
ivan-zhu09111942013-08-01 08:09:16 +0800486
487 def reboot(self, server_id, reboot_type):
488 return self.action(server_id, "reboot", None, type=reboot_type)
489
490 def rebuild(self, server_id, image_ref, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800491 kwargs['image_ref'] = image_ref
ivan-zhu09111942013-08-01 08:09:16 +0800492 if 'disk_config' in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800493 kwargs['os-disk-config:disk_config'] = kwargs['disk_config']
ivan-zhu09111942013-08-01 08:09:16 +0800494 del kwargs['disk_config']
ivan-zhu8f992be2013-07-31 14:56:58 +0800495 kwargs['xmlns:os-disk-config'] = "http://docs.openstack.org/"\
496 "compute/ext/disk_config/api/v3"
ivan-zhu09111942013-08-01 08:09:16 +0800497 kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
498 if 'xmlns' not in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800499 kwargs['xmlns'] = XMLNS_V3
ivan-zhu09111942013-08-01 08:09:16 +0800500
501 attrs = kwargs.copy()
502 if 'metadata' in attrs:
503 del attrs['metadata']
504 rebuild = Element("rebuild",
505 **attrs)
506
507 if 'metadata' in kwargs:
508 metadata = Element("metadata")
509 rebuild.append(metadata)
510 for k, v in kwargs['metadata'].items():
511 meta = Element("meta", key=k)
512 meta.append(Text(v))
513 metadata.append(meta)
514
515 resp, body = self.post('servers/%s/action' % server_id,
516 str(Document(rebuild)), self.headers)
517 server = self._parse_server(etree.fromstring(body))
518 return resp, server
519
520 def resize(self, server_id, flavor_ref, **kwargs):
521 if 'disk_config' in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800522 kwargs['os-disk-config:disk_config'] = kwargs['disk_config']
ivan-zhu09111942013-08-01 08:09:16 +0800523 del kwargs['disk_config']
ivan-zhu8f992be2013-07-31 14:56:58 +0800524 kwargs['xmlns:os-disk-config'] = "http://docs.openstack.org/"\
525 "compute/ext/disk_config/api/v3"
ivan-zhu09111942013-08-01 08:09:16 +0800526 kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
ivan-zhu8f992be2013-07-31 14:56:58 +0800527 kwargs['flavor_ref'] = flavor_ref
ivan-zhu09111942013-08-01 08:09:16 +0800528 return self.action(server_id, 'resize', None, **kwargs)
529
530 def confirm_resize(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800531 return self.action(server_id, 'confirm_resize', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800532
533 def revert_resize(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800534 return self.action(server_id, 'revert_resize', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800535
536 def stop(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800537 return self.action(server_id, 'stop', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800538
539 def start(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800540 return self.action(server_id, 'start', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800541
ivan-zhu8f992be2013-07-31 14:56:58 +0800542 def create_image(self, server_id, name, meta=None):
543 """Creates an image of the original server."""
544 post_body = Element('create_image', name=name)
545
546 if meta:
547 metadata = Element('metadata')
548 post_body.append(metadata)
549 for k, v in meta.items():
550 data = Element('meta', key=k)
551 data.append(Text(v))
552 metadata.append(data)
553 resp, body = self.post('servers/%s/action' % str(server_id),
554 str(Document(post_body)), self.headers)
555 return resp, body
ivan-zhu09111942013-08-01 08:09:16 +0800556
ivan-zhu09111942013-08-01 08:09:16 +0800557 def live_migrate_server(self, server_id, dest_host, use_block_migration):
558 """This should be called with administrator privileges ."""
559
ivan-zhu8f992be2013-07-31 14:56:58 +0800560 req_body = Element("migrate_live",
561 xmlns=XMLNS_V3,
ivan-zhu09111942013-08-01 08:09:16 +0800562 disk_over_commit=False,
563 block_migration=use_block_migration,
564 host=dest_host)
565
566 resp, body = self.post("servers/%s/action" % str(server_id),
567 str(Document(req_body)), self.headers)
568 return resp, body
569
570 def list_server_metadata(self, server_id):
571 resp, body = self.get("servers/%s/metadata" % str(server_id),
572 self.headers)
573 body = self._parse_key_value(etree.fromstring(body))
574 return resp, body
575
576 def set_server_metadata(self, server_id, meta, no_metadata_field=False):
577 doc = Document()
578 if not no_metadata_field:
579 metadata = Element("metadata")
580 doc.append(metadata)
581 for k, v in meta.items():
582 meta_element = Element("meta", key=k)
583 meta_element.append(Text(v))
584 metadata.append(meta_element)
585 resp, body = self.put('servers/%s/metadata' % str(server_id),
586 str(doc), self.headers)
587 return resp, xml_to_json(etree.fromstring(body))
588
589 def update_server_metadata(self, server_id, meta):
590 doc = Document()
591 metadata = Element("metadata")
592 doc.append(metadata)
593 for k, v in meta.items():
594 meta_element = Element("meta", key=k)
595 meta_element.append(Text(v))
596 metadata.append(meta_element)
597 resp, body = self.post("/servers/%s/metadata" % str(server_id),
598 str(doc), headers=self.headers)
599 body = xml_to_json(etree.fromstring(body))
600 return resp, body
601
602 def get_server_metadata_item(self, server_id, key):
603 resp, body = self.get("servers/%s/metadata/%s" % (str(server_id), key),
604 headers=self.headers)
605 return resp, dict([(etree.fromstring(body).attrib['key'],
606 xml_to_json(etree.fromstring(body)))])
607
608 def set_server_metadata_item(self, server_id, key, meta):
609 doc = Document()
610 for k, v in meta.items():
611 meta_element = Element("meta", key=k)
612 meta_element.append(Text(v))
613 doc.append(meta_element)
614 resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
615 str(doc), self.headers)
616 return resp, xml_to_json(etree.fromstring(body))
617
618 def delete_server_metadata_item(self, server_id, key):
619 resp, body = self.delete("servers/%s/metadata/%s" %
620 (str(server_id), key))
621 return resp, body
622
623 def get_console_output(self, server_id, length):
ivan-zhu8f992be2013-07-31 14:56:58 +0800624 return self.action(server_id, 'get_console_output', 'output',
ivan-zhu09111942013-08-01 08:09:16 +0800625 length=length)
626
Ken'ichi Ohmichie985ca82013-11-12 15:10:35 +0900627 def rescue_server(self, server_id, admin_password=None):
ivan-zhu09111942013-08-01 08:09:16 +0800628 """Rescue the provided server."""
Ken'ichi Ohmichie985ca82013-11-12 15:10:35 +0900629 return self.action(server_id, 'rescue', None,
630 admin_password=admin_password)
ivan-zhu09111942013-08-01 08:09:16 +0800631
632 def unrescue_server(self, server_id):
633 """Unrescue the provided server."""
634 return self.action(server_id, 'unrescue', None)
635
636 def attach_volume(self, server_id, volume_id, device='/dev/vdz'):
ivan-zhu8f992be2013-07-31 14:56:58 +0800637 return self.action(server_id, "attach", None, volume_id=volume_id,
638 device=device)
ivan-zhu09111942013-08-01 08:09:16 +0800639
640 def detach_volume(self, server_id, volume_id):
ivan-zhu8f992be2013-07-31 14:56:58 +0800641 return self.action(server_id, "detach", None, volume_id=volume_id)
ivan-zhu09111942013-08-01 08:09:16 +0800642
643 def get_server_diagnostics(self, server_id):
644 """Get the usage data for a server."""
ivan-zhu8f992be2013-07-31 14:56:58 +0800645 resp, body = self.get("servers/%s/os-server-diagnostics" % server_id,
ivan-zhu09111942013-08-01 08:09:16 +0800646 self.headers)
647 body = xml_to_json(etree.fromstring(body))
648 return resp, body
649
650 def list_instance_actions(self, server_id):
651 """List the provided server action."""
652 resp, body = self.get("servers/%s/os-instance-actions" % server_id,
653 self.headers)
654 body = self._parse_array(etree.fromstring(body))
655 return resp, body
656
657 def get_instance_action(self, server_id, request_id):
658 """Returns the action details of the provided server."""
659 resp, body = self.get("servers/%s/os-instance-actions/%s" %
660 (server_id, request_id), self.headers)
661 body = xml_to_json(etree.fromstring(body))
662 return resp, body
ivan-zhu2ca89b32013-08-07 22:37:32 +0800663
664 def force_delete_server(self, server_id, **kwargs):
665 """Force delete a server."""
666 return self.action(server_id, 'force_delete', None, **kwargs)
667
668 def restore_soft_deleted_server(self, server_id, **kwargs):
669 """Restore a soft-deleted server."""
670 return self.action(server_id, 'restore', None, **kwargs)