blob: 240c962d1ca10111aeb8350c15945ca5f3df84e0 [file] [log] [blame]
ivan-zhu09111942013-08-01 08:09:16 +08001# Copyright 2012 IBM Corp.
2# Copyright 2013 Hewlett-Packard Development Company, L.P.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17import time
18import urllib
19
20from lxml import etree
21
22from tempest.common.rest_client import RestClientXML
23from tempest.common import waiters
24from tempest import exceptions
25from tempest.openstack.common import log as logging
26from tempest.services.compute.xml.common import Document
27from tempest.services.compute.xml.common import Element
28from tempest.services.compute.xml.common import Text
29from tempest.services.compute.xml.common import xml_to_json
ivan-zhu8f992be2013-07-31 14:56:58 +080030from tempest.services.compute.xml.common import XMLNS_V3
ivan-zhu09111942013-08-01 08:09:16 +080031
32
33LOG = logging.getLogger(__name__)
34
35
36def _translate_ip_xml_json(ip):
37 """
38 Convert the address version to int.
39 """
40 ip = dict(ip)
41 version = ip.get('version')
42 if version:
43 ip['version'] = int(version)
ivan-zhu8f992be2013-07-31 14:56:58 +080044 if ip.get('type'):
45 ip['type'] = ip.get('type')
46 if ip.get('mac_addr'):
47 ip['mac_addr'] = ip.get('mac_addr')
ivan-zhu09111942013-08-01 08:09:16 +080048 return ip
49
50
51def _translate_network_xml_to_json(network):
52 return [_translate_ip_xml_json(ip.attrib)
ivan-zhu8f992be2013-07-31 14:56:58 +080053 for ip in network.findall('{%s}ip' % XMLNS_V3)]
ivan-zhu09111942013-08-01 08:09:16 +080054
55
56def _translate_addresses_xml_to_json(xml_addresses):
57 return dict((network.attrib['id'], _translate_network_xml_to_json(network))
ivan-zhu8f992be2013-07-31 14:56:58 +080058 for network in xml_addresses.findall('{%s}network' % XMLNS_V3))
ivan-zhu09111942013-08-01 08:09:16 +080059
60
61def _translate_server_xml_to_json(xml_dom):
62 """Convert server XML to server JSON.
63
64 The addresses collection does not convert well by the dumb xml_to_json.
65 This method does some pre and post-processing to deal with that.
66
67 Translate XML addresses subtree to JSON.
68
69 Having xml_doc similar to
ivan-zhu8f992be2013-07-31 14:56:58 +080070 <api:server xmlns:api="http://docs.openstack.org/compute/api/v3">
ivan-zhu09111942013-08-01 08:09:16 +080071 <api:addresses>
72 <api:network id="foo_novanetwork">
73 <api:ip version="4" addr="192.168.0.4"/>
74 </api:network>
75 <api:network id="bar_novanetwork">
76 <api:ip version="4" addr="10.1.0.4"/>
77 <api:ip version="6" addr="2001:0:0:1:2:3:4:5"/>
78 </api:network>
79 </api:addresses>
80 </api:server>
81
82 the _translate_server_xml_to_json(etree.fromstring(xml_doc)) should produce
83 something like
84
85 {'addresses': {'bar_novanetwork': [{'addr': '10.1.0.4', 'version': 4},
86 {'addr': '2001:0:0:1:2:3:4:5',
87 'version': 6}],
88 'foo_novanetwork': [{'addr': '192.168.0.4', 'version': 4}]}}
89 """
ivan-zhu8f992be2013-07-31 14:56:58 +080090 nsmap = {'api': XMLNS_V3}
ivan-zhu09111942013-08-01 08:09:16 +080091 addresses = xml_dom.xpath('/api:server/api:addresses', namespaces=nsmap)
92 if addresses:
93 if len(addresses) > 1:
94 raise ValueError('Expected only single `addresses` element.')
95 json_addresses = _translate_addresses_xml_to_json(addresses[0])
96 json = xml_to_json(xml_dom)
97 json['addresses'] = json_addresses
98 else:
99 json = xml_to_json(xml_dom)
ivan-zhu8f992be2013-07-31 14:56:58 +0800100 disk_config = ('{http://docs.openstack.org'
101 '/compute/ext/disk_config/api/v3}disk_config')
ivan-zhu09111942013-08-01 08:09:16 +0800102 terminated_at = ('{http://docs.openstack.org/'
ivan-zhu8f992be2013-07-31 14:56:58 +0800103 'compute/ext/os-server-usage/api/v3}terminated_at')
ivan-zhu09111942013-08-01 08:09:16 +0800104 launched_at = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800105 '/compute/ext/os-server-usage/api/v3}launched_at')
ivan-zhu09111942013-08-01 08:09:16 +0800106 power_state = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800107 '/compute/ext/extended_status/api/v3}power_state')
ivan-zhu09111942013-08-01 08:09:16 +0800108 availability_zone = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800109 '/compute/ext/extended_availability_zone/api/v3}'
ivan-zhu09111942013-08-01 08:09:16 +0800110 'availability_zone')
111 vm_state = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800112 '/compute/ext/extended_status/api/v3}vm_state')
ivan-zhu09111942013-08-01 08:09:16 +0800113 task_state = ('{http://docs.openstack.org'
ivan-zhu8f992be2013-07-31 14:56:58 +0800114 '/compute/ext/extended_status/api/v3}task_state')
ivan-zhuf0bf3012013-11-25 16:03:42 +0800115 access_ip_v4 = ('{http://docs.openstack.org/compute/ext/'
116 'os-access-ips/api/v3}access_ip_v4')
117 access_ip_v6 = ('{http://docs.openstack.org/compute/ext/'
118 'os-access-ips/api/v3}access_ip_v6')
ivan-zhu8f992be2013-07-31 14:56:58 +0800119 if disk_config in json:
120 json['os-disk-config:disk_config'] = json.pop(disk_config)
ivan-zhu09111942013-08-01 08:09:16 +0800121 if terminated_at in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800122 json['os-server-usage:terminated_at'] = json.pop(terminated_at)
ivan-zhu09111942013-08-01 08:09:16 +0800123 if launched_at in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800124 json['os-server-usage:launched_at'] = json.pop(launched_at)
ivan-zhu09111942013-08-01 08:09:16 +0800125 if power_state in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800126 json['os-extended-status:power_state'] = json.pop(power_state)
ivan-zhu09111942013-08-01 08:09:16 +0800127 if availability_zone in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800128 json['os-extended-availability-zone:availability_zone'] = json.pop(
129 availability_zone)
ivan-zhu09111942013-08-01 08:09:16 +0800130 if vm_state in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800131 json['os-extended-status:vm_state'] = json.pop(vm_state)
ivan-zhu09111942013-08-01 08:09:16 +0800132 if task_state in json:
ivan-zhu8f992be2013-07-31 14:56:58 +0800133 json['os-extended-status:task_state'] = json.pop(task_state)
ivan-zhuf0bf3012013-11-25 16:03:42 +0800134 if access_ip_v4 in json:
135 json['os-access-ips:access_ip_v4'] = json.pop(access_ip_v4)
136 if access_ip_v6 in json:
137 json['os-access-ips:access_ip_v6'] = json.pop(access_ip_v6)
ivan-zhu09111942013-08-01 08:09:16 +0800138 return json
139
140
ivan-zhu8f992be2013-07-31 14:56:58 +0800141class ServersV3ClientXML(RestClientXML):
ivan-zhu09111942013-08-01 08:09:16 +0800142
ivan-zhu8f992be2013-07-31 14:56:58 +0800143 def __init__(self, config, username, password, auth_url,
144 tenant_name=None, auth_version='v2'):
145 super(ServersV3ClientXML, self).__init__(config, username, password,
146 auth_url, tenant_name,
147 auth_version=auth_version)
148 self.service = self.config.compute.catalog_v3_type
ivan-zhu09111942013-08-01 08:09:16 +0800149
150 def _parse_key_value(self, node):
151 """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
152 data = {}
153 for node in node.getchildren():
154 data[node.get('key')] = node.text
155 return data
156
157 def _parse_links(self, node, json):
158 del json['link']
159 json['links'] = []
160 for linknode in node.findall('{http://www.w3.org/2005/Atom}link'):
161 json['links'].append(xml_to_json(linknode))
162
163 def _parse_server(self, body):
164 json = _translate_server_xml_to_json(body)
165
166 if 'metadata' in json and json['metadata']:
167 # NOTE(danms): if there was metadata, we need to re-parse
168 # that as a special type
ivan-zhu8f992be2013-07-31 14:56:58 +0800169 metadata_tag = body.find('{%s}metadata' % XMLNS_V3)
ivan-zhu09111942013-08-01 08:09:16 +0800170 json["metadata"] = self._parse_key_value(metadata_tag)
171 if 'link' in json:
172 self._parse_links(body, json)
173 for sub in ['image', 'flavor']:
174 if sub in json and 'link' in json[sub]:
175 self._parse_links(body, json[sub])
176 return json
177
178 def _parse_xml_virtual_interfaces(self, xml_dom):
179 """
180 Return server's virtual interfaces XML as JSON.
181 """
182 data = {"virtual_interfaces": []}
183 for iface in xml_dom.getchildren():
184 data["virtual_interfaces"].append(
185 {"id": iface.get("id"),
186 "mac_address": iface.get("mac_address")})
187 return data
188
189 def get_server(self, server_id):
190 """Returns the details of an existing server."""
191 resp, body = self.get("servers/%s" % str(server_id), self.headers)
192 server = self._parse_server(etree.fromstring(body))
193 return resp, server
194
Zhi Kun Liude892722013-12-30 15:27:52 +0800195 def migrate_server(self, server_id, **kwargs):
196 """Migrates the given server ."""
197 return self.action(server_id, 'migrate', None, **kwargs)
198
ivan-zhu09111942013-08-01 08:09:16 +0800199 def lock_server(self, server_id, **kwargs):
200 """Locks the given server."""
201 return self.action(server_id, 'lock', None, **kwargs)
202
203 def unlock_server(self, server_id, **kwargs):
204 """Unlocks the given server."""
205 return self.action(server_id, 'unlock', None, **kwargs)
206
207 def suspend_server(self, server_id, **kwargs):
208 """Suspends the provided server."""
209 return self.action(server_id, 'suspend', None, **kwargs)
210
211 def resume_server(self, server_id, **kwargs):
212 """Un-suspends the provided server."""
213 return self.action(server_id, 'resume', None, **kwargs)
214
215 def pause_server(self, server_id, **kwargs):
216 """Pauses the provided server."""
217 return self.action(server_id, 'pause', None, **kwargs)
218
219 def unpause_server(self, server_id, **kwargs):
220 """Un-pauses the provided server."""
221 return self.action(server_id, 'unpause', None, **kwargs)
222
223 def reset_state(self, server_id, state='error'):
224 """Resets the state of a server to active/error."""
ivan-zhu8f992be2013-07-31 14:56:58 +0800225 return self.action(server_id, 'reset_state', None, state=state)
ivan-zhu09111942013-08-01 08:09:16 +0800226
ivan-zhu2ca89b32013-08-07 22:37:32 +0800227 def shelve_server(self, server_id, **kwargs):
228 """Shelves the provided server."""
229 return self.action(server_id, 'shelve', None, **kwargs)
230
231 def unshelve_server(self, server_id, **kwargs):
232 """Un-shelves the provided server."""
233 return self.action(server_id, 'unshelve', None, **kwargs)
234
ivan-zhu09111942013-08-01 08:09:16 +0800235 def delete_server(self, server_id):
236 """Deletes the given server."""
237 return self.delete("servers/%s" % str(server_id))
238
239 def _parse_array(self, node):
240 array = []
241 for child in node.getchildren():
242 array.append(xml_to_json(child))
243 return array
244
245 def list_servers(self, params=None):
246 url = 'servers'
247 if params:
248 url += '?%s' % urllib.urlencode(params)
249
250 resp, body = self.get(url, self.headers)
251 servers = self._parse_array(etree.fromstring(body))
252 return resp, {"servers": servers}
253
254 def list_servers_with_detail(self, params=None):
255 url = 'servers/detail'
256 if params:
257 url += '?%s' % urllib.urlencode(params)
258
259 resp, body = self.get(url, self.headers)
260 servers = self._parse_array(etree.fromstring(body))
261 return resp, {"servers": servers}
262
ivan-zhu8f992be2013-07-31 14:56:58 +0800263 def update_server(self, server_id, name=None, meta=None, access_ip_v4=None,
264 access_ip_v6=None, disk_config=None):
ivan-zhu09111942013-08-01 08:09:16 +0800265 doc = Document()
266 server = Element("server")
267 doc.append(server)
268
269 if name is not None:
270 server.add_attr("name", name)
ivan-zhuf0bf3012013-11-25 16:03:42 +0800271 if access_ip_v4 or access_ip_v6:
272 server.add_attr('xmlns:os-access-ips',
273 "http://docs.openstack.org/compute/ext/"
274 "os-access-ips/api/v3")
ivan-zhu8f992be2013-07-31 14:56:58 +0800275 if access_ip_v4 is not None:
ivan-zhuf0bf3012013-11-25 16:03:42 +0800276 server.add_attr("os-access-ips:access_ip_v4", access_ip_v4)
ivan-zhu8f992be2013-07-31 14:56:58 +0800277 if access_ip_v6 is not None:
ivan-zhuf0bf3012013-11-25 16:03:42 +0800278 server.add_attr("os-access-ips:access_ip_v6", access_ip_v6)
ivan-zhu50969522013-08-26 11:10:39 +0800279 if disk_config is not None:
280 server.add_attr('xmlns:os-disk-config', "http://docs.openstack.org"
281 "/compute/ext/disk_config/api/v3")
282 server.add_attr("os-disk-config:disk_config", disk_config)
ivan-zhu09111942013-08-01 08:09:16 +0800283 if meta is not None:
284 metadata = Element("metadata")
285 server.append(metadata)
286 for k, v in meta:
287 meta = Element("meta", key=k)
288 meta.append(Text(v))
289 metadata.append(meta)
290
291 resp, body = self.put('servers/%s' % str(server_id),
292 str(doc), self.headers)
293 return resp, xml_to_json(etree.fromstring(body))
294
295 def create_server(self, name, image_ref, flavor_ref, **kwargs):
296 """
297 Creates an instance of a server.
298 name (Required): The name of the server.
299 image_ref (Required): Reference to the image used to build the server.
300 flavor_ref (Required): The flavor used to build the server.
301 Following optional keyword arguments are accepted:
ivan-zhu8f992be2013-07-31 14:56:58 +0800302 admin_password: Sets the initial root password.
ivan-zhu09111942013-08-01 08:09:16 +0800303 key_name: Key name of keypair that was created earlier.
304 meta: A dictionary of values to be used as metadata.
ivan-zhu09111942013-08-01 08:09:16 +0800305 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
ivan-zhu09111942013-08-01 08:09:16 +0800403 resp, body = self.post('servers', str(Document(server)), self.headers)
404 server = self._parse_server(etree.fromstring(body))
405 return resp, server
406
Zhi Kun Liude892722013-12-30 15:27:52 +0800407 def wait_for_server_status(self, server_id, status, extra_timeout=0,
408 raise_on_error=True):
ivan-zhu09111942013-08-01 08:09:16 +0800409 """Waits for a server to reach a given status."""
Ken'ichi Ohmichi39437e22013-10-06 00:21:38 +0900410 return waiters.wait_for_server_status(self, server_id, status,
Zhi Kun Liude892722013-12-30 15:27:52 +0800411 extra_timeout=extra_timeout,
412 raise_on_error=raise_on_error)
ivan-zhu09111942013-08-01 08:09:16 +0800413
414 def wait_for_server_termination(self, server_id, ignore_error=False):
415 """Waits for server to reach termination."""
416 start_time = int(time.time())
417 while True:
418 try:
419 resp, body = self.get_server(server_id)
420 except exceptions.NotFound:
421 return
422
423 server_status = body['status']
424 if server_status == 'ERROR' and not ignore_error:
425 raise exceptions.BuildErrorException
426
427 if int(time.time()) - start_time >= self.build_timeout:
428 raise exceptions.TimeoutException
429
430 time.sleep(self.build_interval)
431
432 def _parse_network(self, node):
433 addrs = []
434 for child in node.getchildren():
435 addrs.append({'version': int(child.get('version')),
436 'addr': child.get('addr')})
437 return {node.get('id'): addrs}
438
439 def list_addresses(self, server_id):
440 """Lists all addresses for a server."""
441 resp, body = self.get("servers/%s/ips" % str(server_id), self.headers)
442
443 networks = {}
444 xml_list = etree.fromstring(body)
445 for child in xml_list.getchildren():
446 network = self._parse_network(child)
447 networks.update(**network)
448
449 return resp, networks
450
451 def list_addresses_by_network(self, server_id, network_id):
452 """Lists all addresses of a specific network type for a server."""
453 resp, body = self.get("servers/%s/ips/%s" % (str(server_id),
454 network_id),
455 self.headers)
456 network = self._parse_network(etree.fromstring(body))
457
458 return resp, network
459
460 def action(self, server_id, action_name, response_key, **kwargs):
461 if 'xmlns' not in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800462 kwargs['xmlns'] = XMLNS_V3
ivan-zhu09111942013-08-01 08:09:16 +0800463 doc = Document((Element(action_name, **kwargs)))
464 resp, body = self.post("servers/%s/action" % server_id,
465 str(doc), self.headers)
466 if response_key is not None:
467 body = xml_to_json(etree.fromstring(body))
468 return resp, body
469
ivan-zhu2ca89b32013-08-07 22:37:32 +0800470 def create_backup(self, server_id, backup_type, rotation, name):
471 """Backup a server instance."""
472 return self.action(server_id, "create_backup", None,
473 backup_type=backup_type,
474 rotation=rotation,
475 name=name)
476
ivan-zhu09111942013-08-01 08:09:16 +0800477 def change_password(self, server_id, password):
ivan-zhu8f992be2013-07-31 14:56:58 +0800478 return self.action(server_id, "change_password", None,
Ken'ichi Ohmichie985ca82013-11-12 15:10:35 +0900479 admin_password=password)
ivan-zhu09111942013-08-01 08:09:16 +0800480
481 def reboot(self, server_id, reboot_type):
482 return self.action(server_id, "reboot", None, type=reboot_type)
483
484 def rebuild(self, server_id, image_ref, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800485 kwargs['image_ref'] = image_ref
ivan-zhu09111942013-08-01 08:09:16 +0800486 if 'disk_config' in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800487 kwargs['os-disk-config:disk_config'] = kwargs['disk_config']
ivan-zhu09111942013-08-01 08:09:16 +0800488 del kwargs['disk_config']
ivan-zhu8f992be2013-07-31 14:56:58 +0800489 kwargs['xmlns:os-disk-config'] = "http://docs.openstack.org/"\
490 "compute/ext/disk_config/api/v3"
ivan-zhu09111942013-08-01 08:09:16 +0800491 kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
492 if 'xmlns' not in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800493 kwargs['xmlns'] = XMLNS_V3
ivan-zhu09111942013-08-01 08:09:16 +0800494
495 attrs = kwargs.copy()
496 if 'metadata' in attrs:
497 del attrs['metadata']
498 rebuild = Element("rebuild",
499 **attrs)
500
501 if 'metadata' in kwargs:
502 metadata = Element("metadata")
503 rebuild.append(metadata)
504 for k, v in kwargs['metadata'].items():
505 meta = Element("meta", key=k)
506 meta.append(Text(v))
507 metadata.append(meta)
508
509 resp, body = self.post('servers/%s/action' % server_id,
510 str(Document(rebuild)), self.headers)
511 server = self._parse_server(etree.fromstring(body))
512 return resp, server
513
514 def resize(self, server_id, flavor_ref, **kwargs):
515 if 'disk_config' in kwargs:
ivan-zhu8f992be2013-07-31 14:56:58 +0800516 kwargs['os-disk-config:disk_config'] = kwargs['disk_config']
ivan-zhu09111942013-08-01 08:09:16 +0800517 del kwargs['disk_config']
ivan-zhu8f992be2013-07-31 14:56:58 +0800518 kwargs['xmlns:os-disk-config'] = "http://docs.openstack.org/"\
519 "compute/ext/disk_config/api/v3"
ivan-zhu09111942013-08-01 08:09:16 +0800520 kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
ivan-zhu8f992be2013-07-31 14:56:58 +0800521 kwargs['flavor_ref'] = flavor_ref
ivan-zhu09111942013-08-01 08:09:16 +0800522 return self.action(server_id, 'resize', None, **kwargs)
523
524 def confirm_resize(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800525 return self.action(server_id, 'confirm_resize', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800526
527 def revert_resize(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800528 return self.action(server_id, 'revert_resize', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800529
530 def stop(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800531 return self.action(server_id, 'stop', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800532
533 def start(self, server_id, **kwargs):
ivan-zhu8f992be2013-07-31 14:56:58 +0800534 return self.action(server_id, 'start', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800535
ivan-zhu8f992be2013-07-31 14:56:58 +0800536 def create_image(self, server_id, name, meta=None):
537 """Creates an image of the original server."""
538 post_body = Element('create_image', name=name)
539
540 if meta:
541 metadata = Element('metadata')
542 post_body.append(metadata)
543 for k, v in meta.items():
544 data = Element('meta', key=k)
545 data.append(Text(v))
546 metadata.append(data)
547 resp, body = self.post('servers/%s/action' % str(server_id),
548 str(Document(post_body)), self.headers)
549 return resp, body
ivan-zhu09111942013-08-01 08:09:16 +0800550
ivan-zhu09111942013-08-01 08:09:16 +0800551 def live_migrate_server(self, server_id, dest_host, use_block_migration):
552 """This should be called with administrator privileges ."""
553
ivan-zhu8f992be2013-07-31 14:56:58 +0800554 req_body = Element("migrate_live",
555 xmlns=XMLNS_V3,
ivan-zhu09111942013-08-01 08:09:16 +0800556 disk_over_commit=False,
557 block_migration=use_block_migration,
558 host=dest_host)
559
560 resp, body = self.post("servers/%s/action" % str(server_id),
561 str(Document(req_body)), self.headers)
562 return resp, body
563
564 def list_server_metadata(self, server_id):
565 resp, body = self.get("servers/%s/metadata" % str(server_id),
566 self.headers)
567 body = self._parse_key_value(etree.fromstring(body))
568 return resp, body
569
570 def set_server_metadata(self, server_id, meta, no_metadata_field=False):
571 doc = Document()
572 if not no_metadata_field:
573 metadata = Element("metadata")
574 doc.append(metadata)
575 for k, v in meta.items():
576 meta_element = Element("meta", key=k)
577 meta_element.append(Text(v))
578 metadata.append(meta_element)
579 resp, body = self.put('servers/%s/metadata' % str(server_id),
580 str(doc), self.headers)
581 return resp, xml_to_json(etree.fromstring(body))
582
583 def update_server_metadata(self, server_id, meta):
584 doc = Document()
585 metadata = Element("metadata")
586 doc.append(metadata)
587 for k, v in meta.items():
588 meta_element = Element("meta", key=k)
589 meta_element.append(Text(v))
590 metadata.append(meta_element)
591 resp, body = self.post("/servers/%s/metadata" % str(server_id),
592 str(doc), headers=self.headers)
593 body = xml_to_json(etree.fromstring(body))
594 return resp, body
595
596 def get_server_metadata_item(self, server_id, key):
597 resp, body = self.get("servers/%s/metadata/%s" % (str(server_id), key),
598 headers=self.headers)
599 return resp, dict([(etree.fromstring(body).attrib['key'],
600 xml_to_json(etree.fromstring(body)))])
601
602 def set_server_metadata_item(self, server_id, key, meta):
603 doc = Document()
604 for k, v in meta.items():
ivan-zhu82094252013-11-21 10:16:11 +0800605 meta_element = Element("metadata", key=k)
ivan-zhu09111942013-08-01 08:09:16 +0800606 meta_element.append(Text(v))
607 doc.append(meta_element)
608 resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
609 str(doc), self.headers)
610 return resp, xml_to_json(etree.fromstring(body))
611
612 def delete_server_metadata_item(self, server_id, key):
613 resp, body = self.delete("servers/%s/metadata/%s" %
614 (str(server_id), key))
615 return resp, body
616
617 def get_console_output(self, server_id, length):
ivan-zhu8f992be2013-07-31 14:56:58 +0800618 return self.action(server_id, 'get_console_output', 'output',
ivan-zhu09111942013-08-01 08:09:16 +0800619 length=length)
620
Yuiko Takada7835d502014-01-15 10:15:03 +0000621 def rescue_server(self, server_id, **kwargs):
ivan-zhu09111942013-08-01 08:09:16 +0800622 """Rescue the provided server."""
Yuiko Takada7835d502014-01-15 10:15:03 +0000623 return self.action(server_id, 'rescue', None, **kwargs)
ivan-zhu09111942013-08-01 08:09:16 +0800624
625 def unrescue_server(self, server_id):
626 """Unrescue the provided server."""
627 return self.action(server_id, 'unrescue', None)
628
629 def attach_volume(self, server_id, volume_id, device='/dev/vdz'):
ivan-zhu8f992be2013-07-31 14:56:58 +0800630 return self.action(server_id, "attach", None, volume_id=volume_id,
631 device=device)
ivan-zhu09111942013-08-01 08:09:16 +0800632
633 def detach_volume(self, server_id, volume_id):
ivan-zhu8f992be2013-07-31 14:56:58 +0800634 return self.action(server_id, "detach", None, volume_id=volume_id)
ivan-zhu09111942013-08-01 08:09:16 +0800635
636 def get_server_diagnostics(self, server_id):
637 """Get the usage data for a server."""
ivan-zhu8f992be2013-07-31 14:56:58 +0800638 resp, body = self.get("servers/%s/os-server-diagnostics" % server_id,
ivan-zhu09111942013-08-01 08:09:16 +0800639 self.headers)
640 body = xml_to_json(etree.fromstring(body))
641 return resp, body
642
643 def list_instance_actions(self, server_id):
644 """List the provided server action."""
645 resp, body = self.get("servers/%s/os-instance-actions" % server_id,
646 self.headers)
647 body = self._parse_array(etree.fromstring(body))
648 return resp, body
649
650 def get_instance_action(self, server_id, request_id):
651 """Returns the action details of the provided server."""
652 resp, body = self.get("servers/%s/os-instance-actions/%s" %
653 (server_id, request_id), self.headers)
654 body = xml_to_json(etree.fromstring(body))
655 return resp, body
ivan-zhu2ca89b32013-08-07 22:37:32 +0800656
657 def force_delete_server(self, server_id, **kwargs):
658 """Force delete a server."""
659 return self.action(server_id, 'force_delete', None, **kwargs)
660
661 def restore_soft_deleted_server(self, server_id, **kwargs):
662 """Restore a soft-deleted server."""
663 return self.action(server_id, 'restore', None, **kwargs)