blob: 763670763a25c38becd70727273018f03ede8800 [file] [log] [blame]
Attila Fazekas36b1fcf2013-01-31 16:41:04 +01001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010013import time
14import urllib
15
16from lxml import etree
17
vponomaryov960eeb42014-02-22 18:25:25 +020018from tempest.common import rest_client
Matthew Treinish28f164c2014-03-04 18:55:06 +000019from tempest.common import xml_utils as common
Matthew Treinish684d8992014-01-30 16:27:40 +000020from tempest import config
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010021from tempest import exceptions
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040022from tempest.openstack.common import log as logging
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010023
Matthew Treinish684d8992014-01-30 16:27:40 +000024CONF = config.CONF
25
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010026LOG = logging.getLogger(__name__)
27
28
vponomaryov960eeb42014-02-22 18:25:25 +020029class SnapshotsClientXML(rest_client.RestClient):
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010030 """Client class to send CRUD Volume API requests."""
vponomaryov960eeb42014-02-22 18:25:25 +020031 TYPE = "xml"
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010032
Andrea Frittoli8bbdb162014-01-06 11:06:13 +000033 def __init__(self, auth_provider):
34 super(SnapshotsClientXML, self).__init__(auth_provider)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010035
Matthew Treinish684d8992014-01-30 16:27:40 +000036 self.service = CONF.volume.catalog_type
37 self.build_interval = CONF.volume.build_interval
38 self.build_timeout = CONF.volume.build_timeout
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010039
40 def list_snapshots(self, params=None):
41 """List all snapshot."""
42 url = 'snapshots'
43
44 if params:
45 url += '?%s' % urllib.urlencode(params)
46
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020047 resp, body = self.get(url)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010048 body = etree.fromstring(body)
Giulio Fidentee92956b2013-06-06 18:38:48 +020049 snapshots = []
50 for snap in body:
Yuiko Takada4d41c2f2014-03-07 11:58:31 +000051 snapshots.append(common.xml_to_json(snap))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +000052 self.expected_success(200, resp.status)
Giulio Fidentee92956b2013-06-06 18:38:48 +020053 return resp, snapshots
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010054
55 def list_snapshots_with_detail(self, params=None):
56 """List all the details of snapshot."""
57 url = 'snapshots/detail'
58
59 if params:
60 url += '?%s' % urllib.urlencode(params)
61
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020062 resp, body = self.get(url)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010063 body = etree.fromstring(body)
64 snapshots = []
Giulio Fidentee92956b2013-06-06 18:38:48 +020065 for snap in body:
Yuiko Takada4d41c2f2014-03-07 11:58:31 +000066 snapshots.append(common.xml_to_json(snap))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +000067 self.expected_success(200, resp.status)
Giulio Fidentee92956b2013-06-06 18:38:48 +020068 return resp, snapshots
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010069
70 def get_snapshot(self, snapshot_id):
71 """Returns the details of a single snapshot."""
72 url = "snapshots/%s" % str(snapshot_id)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020073 resp, body = self.get(url)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010074 body = etree.fromstring(body)
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +000075 self.expected_success(200, resp.status)
Yuiko Takada4d41c2f2014-03-07 11:58:31 +000076 return resp, common.xml_to_json(body)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010077
78 def create_snapshot(self, volume_id, **kwargs):
Attila Fazekasb2902af2013-02-16 16:22:44 +010079 """Creates a new snapshot.
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010080 volume_id(Required): id of the volume.
81 force: Create a snapshot even if the volume attached (Default=False)
82 display_name: Optional snapshot Name.
83 display_description: User friendly snapshot description.
84 """
Chang Bo Guocc1623c2013-09-13 20:11:27 -070085 # NOTE(afazekas): it should use the volume namespace
Yuiko Takada4d41c2f2014-03-07 11:58:31 +000086 snapshot = common.Element("snapshot", xmlns=common.XMLNS_11,
87 volume_id=volume_id)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010088 for key, value in kwargs.items():
89 snapshot.add_attr(key, value)
Yuiko Takada4d41c2f2014-03-07 11:58:31 +000090 resp, body = self.post('snapshots',
91 str(common.Document(snapshot)))
92 body = common.xml_to_json(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +000093 self.expected_success(200, resp.status)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010094 return resp, body
95
QingXin Mengdc95f5e2013-09-16 19:06:44 -070096 def update_snapshot(self, snapshot_id, **kwargs):
97 """Updates a snapshot."""
Yuiko Takada4d41c2f2014-03-07 11:58:31 +000098 put_body = common.Element("snapshot", xmlns=common.XMLNS_11, **kwargs)
QingXin Mengdc95f5e2013-09-16 19:06:44 -070099
100 resp, body = self.put('snapshots/%s' % snapshot_id,
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000101 str(common.Document(put_body)))
102 body = common.xml_to_json(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000103 self.expected_success(200, resp.status)
QingXin Mengdc95f5e2013-09-16 19:06:44 -0700104 return resp, body
105
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200106 # NOTE(afazekas): just for the wait function
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100107 def _get_snapshot_status(self, snapshot_id):
108 resp, body = self.get_snapshot(snapshot_id)
109 status = body['status']
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200110 # NOTE(afazekas): snapshot can reach an "error"
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100111 # state in a "normal" lifecycle
112 if (status == 'error'):
113 raise exceptions.SnapshotBuildErrorException(
Sean Dague14c68182013-04-14 15:34:30 -0400114 snapshot_id=snapshot_id)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100115
116 return status
117
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200118 # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100119 def wait_for_snapshot_status(self, snapshot_id, status):
120 """Waits for a Snapshot to reach a given status."""
121 start_time = time.time()
122 old_value = value = self._get_snapshot_status(snapshot_id)
123 while True:
124 dtime = time.time() - start_time
125 time.sleep(self.build_interval)
126 if value != old_value:
127 LOG.info('Value transition from "%s" to "%s"'
128 'in %d second(s).', old_value,
129 value, dtime)
130 if (value == status):
131 return value
132
133 if dtime > self.build_timeout:
134 message = ('Time Limit Exceeded! (%ds)'
135 'while waiting for %s, '
136 'but we got %s.' %
137 (self.build_timeout, status, value))
138 raise exceptions.TimeoutException(message)
139 time.sleep(self.build_interval)
140 old_value = value
141 value = self._get_snapshot_status(snapshot_id)
142
143 def delete_snapshot(self, snapshot_id):
144 """Delete Snapshot."""
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000145 resp, body = self.delete("snapshots/%s" % str(snapshot_id))
146 self.expected_success(202, resp.status)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100147
148 def is_resource_deleted(self, id):
149 try:
150 self.get_snapshot(id)
151 except exceptions.NotFound:
152 return True
153 return False
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800154
155 def reset_snapshot_status(self, snapshot_id, status):
156 """Reset the specified snapshot's status."""
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000157 post_body = common.Element("os-reset_status", status=status)
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800158 url = 'snapshots/%s/action' % str(snapshot_id)
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000159 resp, body = self.post(url, str(common.Document(post_body)))
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800160 if body:
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000161 body = common.xml_to_json(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000162 self.expected_success(202, resp.status)
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800163 return resp, body
164
165 def update_snapshot_status(self, snapshot_id, status, progress):
166 """Update the specified snapshot's status."""
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000167 post_body = common.Element("os-update_snapshot_status",
168 status=status,
169 progress=progress
170 )
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800171 url = 'snapshots/%s/action' % str(snapshot_id)
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000172 resp, body = self.post(url, str(common.Document(post_body)))
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800173 if body:
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000174 body = common.xml_to_json(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000175 self.expected_success(202, resp.status)
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800176 return resp, body
huangtianhua1346d702013-12-09 18:42:35 +0800177
178 def _metadata_body(self, meta):
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000179 post_body = common.Element('metadata')
huangtianhua1346d702013-12-09 18:42:35 +0800180 for k, v in meta.items():
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000181 data = common.Element('meta', key=k)
182 data.append(common.Text(v))
huangtianhua1346d702013-12-09 18:42:35 +0800183 post_body.append(data)
184 return post_body
185
186 def _parse_key_value(self, node):
187 """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
188 data = {}
189 for node in node.getchildren():
190 data[node.get('key')] = node.text
191 return data
192
193 def create_snapshot_metadata(self, snapshot_id, metadata):
194 """Create metadata for the snapshot."""
195 post_body = self._metadata_body(metadata)
196 resp, body = self.post('snapshots/%s/metadata' % snapshot_id,
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000197 str(common.Document(post_body)))
huangtianhua1346d702013-12-09 18:42:35 +0800198 body = self._parse_key_value(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000199 self.expected_success(200, resp.status)
huangtianhua1346d702013-12-09 18:42:35 +0800200 return resp, body
201
202 def get_snapshot_metadata(self, snapshot_id):
203 """Get metadata of the snapshot."""
204 url = "snapshots/%s/metadata" % str(snapshot_id)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200205 resp, body = self.get(url)
huangtianhua1346d702013-12-09 18:42:35 +0800206 body = self._parse_key_value(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000207 self.expected_success(200, resp.status)
huangtianhua1346d702013-12-09 18:42:35 +0800208 return resp, body
209
210 def update_snapshot_metadata(self, snapshot_id, metadata):
211 """Update metadata for the snapshot."""
212 put_body = self._metadata_body(metadata)
213 url = "snapshots/%s/metadata" % str(snapshot_id)
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000214 resp, body = self.put(url, str(common.Document(put_body)))
huangtianhua1346d702013-12-09 18:42:35 +0800215 body = self._parse_key_value(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000216 self.expected_success(200, resp.status)
huangtianhua1346d702013-12-09 18:42:35 +0800217 return resp, body
218
219 def update_snapshot_metadata_item(self, snapshot_id, id, meta_item):
220 """Update metadata item for the snapshot."""
221 for k, v in meta_item.items():
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000222 put_body = common.Element('meta', key=k)
223 put_body.append(common.Text(v))
huangtianhua1346d702013-12-09 18:42:35 +0800224 url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000225 resp, body = self.put(url, str(common.Document(put_body)))
226 body = common.xml_to_json(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000227 self.expected_success(200, resp.status)
huangtianhua1346d702013-12-09 18:42:35 +0800228 return resp, body
229
230 def delete_snapshot_metadata_item(self, snapshot_id, id):
231 """Delete metadata item for the snapshot."""
232 url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000233 resp, body = self.delete(url)
234 self.expected_success(200, resp.status)
235 return resp, body
wanghaofa3908c2014-01-15 19:34:03 +0800236
237 def force_delete_snapshot(self, snapshot_id):
238 """Force Delete Snapshot."""
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000239 post_body = common.Element("os-force_delete")
wanghaofa3908c2014-01-15 19:34:03 +0800240 url = 'snapshots/%s/action' % str(snapshot_id)
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000241 resp, body = self.post(url, str(common.Document(post_body)))
wanghaofa3908c2014-01-15 19:34:03 +0800242 if body:
Yuiko Takada4d41c2f2014-03-07 11:58:31 +0000243 body = common.xml_to_json(etree.fromstring(body))
Swapnil Kulkarnid9df38c2014-08-16 18:06:52 +0000244 self.expected_success(202, resp.status)
wanghaofa3908c2014-01-15 19:34:03 +0800245 return resp, body