blob: 6c5a14cd7c36f83e79ef2520f9ec9123792b83f5 [file] [log] [blame]
Kurt Taylor6a6f5be2013-04-02 18:53:47 -04001# Copyright 2012 IBM Corp.
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -04002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040016import urllib
17
18from lxml import etree
19
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040020from tempest.common.rest_client import RestClientXML
Matt Riedemannc00f3262013-12-14 12:03:55 -080021from tempest.common import waiters
Matthew Treinisha83a16e2012-12-07 13:44:02 -050022from tempest import exceptions
dwallecke62b9f02012-10-10 23:34:42 -050023from tempest.services.compute.xml.common import Document
24from tempest.services.compute.xml.common import Element
25from tempest.services.compute.xml.common import Text
26from tempest.services.compute.xml.common import xml_to_json
27from tempest.services.compute.xml.common import XMLNS_11
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040028
29
30class ImagesClientXML(RestClientXML):
31
32 def __init__(self, config, username, password, auth_url, tenant_name=None):
33 super(ImagesClientXML, self).__init__(config, username, password,
34 auth_url, tenant_name)
35 self.service = self.config.compute.catalog_type
36 self.build_interval = self.config.compute.build_interval
37 self.build_timeout = self.config.compute.build_timeout
38
39 def _parse_server(self, node):
Attila Fazekas7b487be2013-02-12 11:14:41 +010040 data = xml_to_json(node)
41 return self._parse_links(node, data)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040042
43 def _parse_image(self, node):
Sean Daguef237ccb2013-01-04 15:19:14 -050044 """Parses detailed XML image information into dictionary."""
Attila Fazekas7b487be2013-02-12 11:14:41 +010045 data = xml_to_json(node)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040046
Attila Fazekas7b487be2013-02-12 11:14:41 +010047 self._parse_links(node, data)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040048
49 # parse all metadata
Attila Fazekas7b487be2013-02-12 11:14:41 +010050 if 'metadata' in data:
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040051 tag = node.find('{%s}metadata' % XMLNS_11)
Attila Fazekas7b487be2013-02-12 11:14:41 +010052 data['metadata'] = dict((x.get('key'), x.text)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040053 for x in tag.getchildren())
54
55 # parse server information
Attila Fazekas7b487be2013-02-12 11:14:41 +010056 if 'server' in data:
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040057 tag = node.find('{%s}server' % XMLNS_11)
Attila Fazekas7b487be2013-02-12 11:14:41 +010058 data['server'] = self._parse_server(tag)
59 return data
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040060
Attila Fazekas7b487be2013-02-12 11:14:41 +010061 def _parse_links(self, node, data):
Sean Daguef237ccb2013-01-04 15:19:14 -050062 """Append multiple links under a list."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040063 # look for links
Attila Fazekas7b487be2013-02-12 11:14:41 +010064 if 'link' in data:
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040065 # remove single link element
Attila Fazekas7b487be2013-02-12 11:14:41 +010066 del data['link']
67 data['links'] = [xml_to_json(x) for x in
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040068 node.findall('{http://www.w3.org/2005/Atom}link')]
Attila Fazekas7b487be2013-02-12 11:14:41 +010069 return data
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040070
Matthew Treinish4e1b3e62013-01-10 11:30:49 -050071 def _parse_images(self, xml):
Attila Fazekas7b487be2013-02-12 11:14:41 +010072 data = {'images': []}
Matthew Treinish4e1b3e62013-01-10 11:30:49 -050073 images = xml.getchildren()
74 for image in images:
Attila Fazekas7b487be2013-02-12 11:14:41 +010075 data['images'].append(self._parse_image(image))
76 return data
Matthew Treinish4e1b3e62013-01-10 11:30:49 -050077
nayna-pateleda1d122013-03-20 14:44:31 +000078 def _parse_key_value(self, node):
79 """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
80 data = {}
81 for node in node.getchildren():
82 data[node.get('key')] = node.text
83 return data
84
85 def _parse_metadata(self, node):
86 """Parse the response body without children."""
87 data = {}
88 data[node.get('key')] = node.text
89 return data
90
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040091 def create_image(self, server_id, name, meta=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050092 """Creates an image of the original server."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040093 post_body = Element('createImage', name=name)
94
95 if meta:
96 metadata = Element('metadata')
97 post_body.append(metadata)
98 for k, v in meta.items():
99 data = Element('meta', key=k)
100 data.append(Text(v))
101 metadata.append(data)
102 resp, body = self.post('servers/%s/action' % str(server_id),
103 str(Document(post_body)), self.headers)
104 return resp, body
105
106 def list_images(self, params=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500107 """Returns a list of all images filtered by any parameters."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400108 url = 'images'
109 if params:
Matthew Treinish26dd0fa2012-12-04 17:14:37 -0500110 url += '?%s' % urllib.urlencode(params)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400111
112 resp, body = self.get(url, self.headers)
Matthew Treinish4e1b3e62013-01-10 11:30:49 -0500113 body = self._parse_images(etree.fromstring(body))
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400114 return resp, body['images']
115
116 def list_images_with_detail(self, params=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500117 """Returns a detailed list of images filtered by any parameters."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400118 url = 'images/detail'
119 if params:
120 param_list = urllib.urlencode(params)
121
122 url = "images/detail?" + param_list
123
124 resp, body = self.get(url, self.headers)
Matthew Treinish4e1b3e62013-01-10 11:30:49 -0500125 body = self._parse_images(etree.fromstring(body))
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400126 return resp, body['images']
127
128 def get_image(self, image_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500129 """Returns the details of a single image."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400130 resp, body = self.get("images/%s" % str(image_id), self.headers)
Attila Fazekas54a42862013-07-28 22:31:06 +0200131 self.expected_success(200, resp)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400132 body = self._parse_image(etree.fromstring(body))
133 return resp, body
134
135 def delete_image(self, image_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500136 """Deletes the provided image."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400137 return self.delete("images/%s" % str(image_id), self.headers)
138
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400139 def wait_for_image_status(self, image_id, status):
140 """Waits for an image to reach a given status."""
Matt Riedemannc00f3262013-12-14 12:03:55 -0800141 waiters.wait_for_image_status(self, image_id, status)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400142
nayna-pateleda1d122013-03-20 14:44:31 +0000143 def _metadata_body(self, meta):
144 post_body = Element('metadata')
145 for k, v in meta.items():
146 data = Element('meta', key=k)
147 data.append(Text(v))
148 post_body.append(data)
149 return post_body
150
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400151 def list_image_metadata(self, image_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500152 """Lists all metadata items for an image."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400153 resp, body = self.get("images/%s/metadata" % str(image_id),
154 self.headers)
nayna-pateleda1d122013-03-20 14:44:31 +0000155 body = self._parse_key_value(etree.fromstring(body))
156 return resp, body
Attila Fazekas7b487be2013-02-12 11:14:41 +0100157
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400158 def set_image_metadata(self, image_id, meta):
Sean Daguef237ccb2013-01-04 15:19:14 -0500159 """Sets the metadata for an image."""
nayna-pateleda1d122013-03-20 14:44:31 +0000160 post_body = self._metadata_body(meta)
161 resp, body = self.put('images/%s/metadata' % image_id,
162 str(Document(post_body)), self.headers)
163 body = self._parse_key_value(etree.fromstring(body))
164 return resp, body
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400165
166 def update_image_metadata(self, image_id, meta):
Sean Daguef237ccb2013-01-04 15:19:14 -0500167 """Updates the metadata for an image."""
nayna-pateleda1d122013-03-20 14:44:31 +0000168 post_body = self._metadata_body(meta)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400169 resp, body = self.post('images/%s/metadata' % str(image_id),
nayna-pateleda1d122013-03-20 14:44:31 +0000170 str(Document(post_body)), self.headers)
171 body = self._parse_key_value(etree.fromstring(body))
172 return resp, body
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400173
174 def get_image_metadata_item(self, image_id, key):
Sean Daguef237ccb2013-01-04 15:19:14 -0500175 """Returns the value for a specific image metadata key."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400176 resp, body = self.get("images/%s/metadata/%s.xml" %
177 (str(image_id), key), self.headers)
nayna-pateleda1d122013-03-20 14:44:31 +0000178 body = self._parse_metadata(etree.fromstring(body))
179 return resp, body
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400180
181 def set_image_metadata_item(self, image_id, key, meta):
Sean Daguef237ccb2013-01-04 15:19:14 -0500182 """Sets the value for a specific image metadata key."""
nayna-pateleda1d122013-03-20 14:44:31 +0000183 for k, v in meta.items():
184 post_body = Element('meta', key=key)
185 post_body.append(Text(v))
186 resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
187 str(Document(post_body)), self.headers)
Attila Fazekas7b487be2013-02-12 11:14:41 +0100188 body = xml_to_json(etree.fromstring(body))
nayna-pateleda1d122013-03-20 14:44:31 +0000189 return resp, body
Attila Fazekas7b487be2013-02-12 11:14:41 +0100190
191 def update_image_metadata_item(self, image_id, key, meta):
192 """Sets the value for a specific image metadata key."""
193 post_body = Document('meta', Text(meta), key=key)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400194 resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
195 post_body, self.headers)
196 body = xml_to_json(etree.fromstring(body))
197 return resp, body['meta']
198
199 def delete_image_metadata_item(self, image_id, key):
Sean Daguef237ccb2013-01-04 15:19:14 -0500200 """Deletes a single image metadata key/value pair."""
nayna-pateleda1d122013-03-20 14:44:31 +0000201 return self.delete("images/%s/metadata/%s" % (str(image_id), key),
202 self.headers)
Matthew Treinish0d660492013-06-04 17:26:09 -0400203
204 def is_resource_deleted(self, id):
205 try:
206 self.get_image(id)
207 except exceptions.NotFound:
208 return True
209 return False