blob: 98de7df84133f06c5e6527889bf246f29f726580 [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 Treinish684d8992014-01-30 16:27:40 +000022from tempest import config
Matthew Treinisha83a16e2012-12-07 13:44:02 -050023from tempest import exceptions
dwallecke62b9f02012-10-10 23:34:42 -050024from tempest.services.compute.xml.common import Document
25from tempest.services.compute.xml.common import Element
26from tempest.services.compute.xml.common import Text
27from tempest.services.compute.xml.common import xml_to_json
28from tempest.services.compute.xml.common import XMLNS_11
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040029
Matthew Treinish684d8992014-01-30 16:27:40 +000030CONF = config.CONF
31
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040032
33class ImagesClientXML(RestClientXML):
34
Matthew Treinish684d8992014-01-30 16:27:40 +000035 def __init__(self, username, password, auth_url, tenant_name=None):
36 super(ImagesClientXML, self).__init__(username, password,
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040037 auth_url, tenant_name)
Matthew Treinish684d8992014-01-30 16:27:40 +000038 self.service = CONF.compute.catalog_type
39 self.build_interval = CONF.compute.build_interval
40 self.build_timeout = CONF.compute.build_timeout
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040041
42 def _parse_server(self, node):
Attila Fazekas7b487be2013-02-12 11:14:41 +010043 data = xml_to_json(node)
44 return self._parse_links(node, data)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040045
46 def _parse_image(self, node):
Sean Daguef237ccb2013-01-04 15:19:14 -050047 """Parses detailed XML image information into dictionary."""
Attila Fazekas7b487be2013-02-12 11:14:41 +010048 data = xml_to_json(node)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040049
Attila Fazekas7b487be2013-02-12 11:14:41 +010050 self._parse_links(node, data)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040051
52 # parse all metadata
Attila Fazekas7b487be2013-02-12 11:14:41 +010053 if 'metadata' in data:
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040054 tag = node.find('{%s}metadata' % XMLNS_11)
Attila Fazekas7b487be2013-02-12 11:14:41 +010055 data['metadata'] = dict((x.get('key'), x.text)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040056 for x in tag.getchildren())
57
58 # parse server information
Attila Fazekas7b487be2013-02-12 11:14:41 +010059 if 'server' in data:
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040060 tag = node.find('{%s}server' % XMLNS_11)
Attila Fazekas7b487be2013-02-12 11:14:41 +010061 data['server'] = self._parse_server(tag)
62 return data
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040063
Attila Fazekas7b487be2013-02-12 11:14:41 +010064 def _parse_links(self, node, data):
Sean Daguef237ccb2013-01-04 15:19:14 -050065 """Append multiple links under a list."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040066 # look for links
Attila Fazekas7b487be2013-02-12 11:14:41 +010067 if 'link' in data:
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040068 # remove single link element
Attila Fazekas7b487be2013-02-12 11:14:41 +010069 del data['link']
70 data['links'] = [xml_to_json(x) for x in
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040071 node.findall('{http://www.w3.org/2005/Atom}link')]
Attila Fazekas7b487be2013-02-12 11:14:41 +010072 return data
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040073
Matthew Treinish4e1b3e62013-01-10 11:30:49 -050074 def _parse_images(self, xml):
Attila Fazekas7b487be2013-02-12 11:14:41 +010075 data = {'images': []}
Matthew Treinish4e1b3e62013-01-10 11:30:49 -050076 images = xml.getchildren()
77 for image in images:
Attila Fazekas7b487be2013-02-12 11:14:41 +010078 data['images'].append(self._parse_image(image))
79 return data
Matthew Treinish4e1b3e62013-01-10 11:30:49 -050080
nayna-pateleda1d122013-03-20 14:44:31 +000081 def _parse_key_value(self, node):
82 """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
83 data = {}
84 for node in node.getchildren():
85 data[node.get('key')] = node.text
86 return data
87
88 def _parse_metadata(self, node):
89 """Parse the response body without children."""
90 data = {}
91 data[node.get('key')] = node.text
92 return data
93
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040094 def create_image(self, server_id, name, meta=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050095 """Creates an image of the original server."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -040096 post_body = Element('createImage', name=name)
97
98 if meta:
99 metadata = Element('metadata')
100 post_body.append(metadata)
101 for k, v in meta.items():
102 data = Element('meta', key=k)
103 data.append(Text(v))
104 metadata.append(data)
105 resp, body = self.post('servers/%s/action' % str(server_id),
106 str(Document(post_body)), self.headers)
107 return resp, body
108
109 def list_images(self, params=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500110 """Returns a list of all images filtered by any parameters."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400111 url = 'images'
112 if params:
Matthew Treinish26dd0fa2012-12-04 17:14:37 -0500113 url += '?%s' % urllib.urlencode(params)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400114
115 resp, body = self.get(url, self.headers)
Matthew Treinish4e1b3e62013-01-10 11:30:49 -0500116 body = self._parse_images(etree.fromstring(body))
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400117 return resp, body['images']
118
119 def list_images_with_detail(self, params=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500120 """Returns a detailed list of images filtered by any parameters."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400121 url = 'images/detail'
122 if params:
123 param_list = urllib.urlencode(params)
124
125 url = "images/detail?" + param_list
126
127 resp, body = self.get(url, self.headers)
Matthew Treinish4e1b3e62013-01-10 11:30:49 -0500128 body = self._parse_images(etree.fromstring(body))
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400129 return resp, body['images']
130
131 def get_image(self, image_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500132 """Returns the details of a single image."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400133 resp, body = self.get("images/%s" % str(image_id), self.headers)
Attila Fazekas54a42862013-07-28 22:31:06 +0200134 self.expected_success(200, resp)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400135 body = self._parse_image(etree.fromstring(body))
136 return resp, body
137
138 def delete_image(self, image_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500139 """Deletes the provided image."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400140 return self.delete("images/%s" % str(image_id), self.headers)
141
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400142 def wait_for_image_status(self, image_id, status):
143 """Waits for an image to reach a given status."""
Matt Riedemannc00f3262013-12-14 12:03:55 -0800144 waiters.wait_for_image_status(self, image_id, status)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400145
nayna-pateleda1d122013-03-20 14:44:31 +0000146 def _metadata_body(self, meta):
147 post_body = Element('metadata')
148 for k, v in meta.items():
149 data = Element('meta', key=k)
150 data.append(Text(v))
151 post_body.append(data)
152 return post_body
153
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400154 def list_image_metadata(self, image_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500155 """Lists all metadata items for an image."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400156 resp, body = self.get("images/%s/metadata" % str(image_id),
157 self.headers)
nayna-pateleda1d122013-03-20 14:44:31 +0000158 body = self._parse_key_value(etree.fromstring(body))
159 return resp, body
Attila Fazekas7b487be2013-02-12 11:14:41 +0100160
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400161 def set_image_metadata(self, image_id, meta):
Sean Daguef237ccb2013-01-04 15:19:14 -0500162 """Sets the metadata for an image."""
nayna-pateleda1d122013-03-20 14:44:31 +0000163 post_body = self._metadata_body(meta)
164 resp, body = self.put('images/%s/metadata' % image_id,
165 str(Document(post_body)), self.headers)
166 body = self._parse_key_value(etree.fromstring(body))
167 return resp, body
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400168
169 def update_image_metadata(self, image_id, meta):
Sean Daguef237ccb2013-01-04 15:19:14 -0500170 """Updates the metadata for an image."""
nayna-pateleda1d122013-03-20 14:44:31 +0000171 post_body = self._metadata_body(meta)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400172 resp, body = self.post('images/%s/metadata' % str(image_id),
nayna-pateleda1d122013-03-20 14:44:31 +0000173 str(Document(post_body)), self.headers)
174 body = self._parse_key_value(etree.fromstring(body))
175 return resp, body
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400176
177 def get_image_metadata_item(self, image_id, key):
Sean Daguef237ccb2013-01-04 15:19:14 -0500178 """Returns the value for a specific image metadata key."""
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400179 resp, body = self.get("images/%s/metadata/%s.xml" %
180 (str(image_id), key), self.headers)
nayna-pateleda1d122013-03-20 14:44:31 +0000181 body = self._parse_metadata(etree.fromstring(body))
182 return resp, body
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400183
184 def set_image_metadata_item(self, image_id, key, meta):
Sean Daguef237ccb2013-01-04 15:19:14 -0500185 """Sets the value for a specific image metadata key."""
nayna-pateleda1d122013-03-20 14:44:31 +0000186 for k, v in meta.items():
187 post_body = Element('meta', key=key)
188 post_body.append(Text(v))
189 resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
190 str(Document(post_body)), self.headers)
Attila Fazekas7b487be2013-02-12 11:14:41 +0100191 body = xml_to_json(etree.fromstring(body))
nayna-pateleda1d122013-03-20 14:44:31 +0000192 return resp, body
Attila Fazekas7b487be2013-02-12 11:14:41 +0100193
194 def update_image_metadata_item(self, image_id, key, meta):
195 """Sets the value for a specific image metadata key."""
196 post_body = Document('meta', Text(meta), key=key)
Mauro S. M. Rodrigues6e373242012-08-27 18:59:19 -0400197 resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
198 post_body, self.headers)
199 body = xml_to_json(etree.fromstring(body))
200 return resp, body['meta']
201
202 def delete_image_metadata_item(self, image_id, key):
Sean Daguef237ccb2013-01-04 15:19:14 -0500203 """Deletes a single image metadata key/value pair."""
nayna-pateleda1d122013-03-20 14:44:31 +0000204 return self.delete("images/%s/metadata/%s" % (str(image_id), key),
205 self.headers)
Matthew Treinish0d660492013-06-04 17:26:09 -0400206
207 def is_resource_deleted(self, id):
208 try:
209 self.get_image(id)
210 except exceptions.NotFound:
211 return True
212 return False