blob: ea2ea271ff5c40ca3f1e6f45cde42f0cefabb24b [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
18from tempest.common.rest_client import RestClientXML
19from tempest import exceptions
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040020from tempest.openstack.common import log as logging
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010021from tempest.services.compute.xml.common import Document
22from tempest.services.compute.xml.common import Element
huangtianhua1346d702013-12-09 18:42:35 +080023from tempest.services.compute.xml.common import Text
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010024from tempest.services.compute.xml.common import xml_to_json
25from tempest.services.compute.xml.common import XMLNS_11
26
27LOG = logging.getLogger(__name__)
28
29
30class SnapshotsClientXML(RestClientXML):
31 """Client class to send CRUD Volume API requests."""
32
33 def __init__(self, config, username, password, auth_url, tenant_name=None):
34 super(SnapshotsClientXML, self).__init__(config, username, password,
35 auth_url, tenant_name)
36
37 self.service = self.config.volume.catalog_type
38 self.build_interval = self.config.volume.build_interval
39 self.build_timeout = self.config.volume.build_timeout
40
41 def list_snapshots(self, params=None):
42 """List all snapshot."""
43 url = 'snapshots'
44
45 if params:
46 url += '?%s' % urllib.urlencode(params)
47
48 resp, body = self.get(url, self.headers)
49 body = etree.fromstring(body)
Giulio Fidentee92956b2013-06-06 18:38:48 +020050 snapshots = []
51 for snap in body:
52 snapshots.append(xml_to_json(snap))
53 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
62 resp, body = self.get(url, self.headers)
63 body = etree.fromstring(body)
64 snapshots = []
Giulio Fidentee92956b2013-06-06 18:38:48 +020065 for snap in body:
66 snapshots.append(xml_to_json(snap))
67 return resp, snapshots
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010068
69 def get_snapshot(self, snapshot_id):
70 """Returns the details of a single snapshot."""
71 url = "snapshots/%s" % str(snapshot_id)
72 resp, body = self.get(url, self.headers)
73 body = etree.fromstring(body)
74 return resp, xml_to_json(body)
75
76 def create_snapshot(self, volume_id, **kwargs):
Attila Fazekasb2902af2013-02-16 16:22:44 +010077 """Creates a new snapshot.
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010078 volume_id(Required): id of the volume.
79 force: Create a snapshot even if the volume attached (Default=False)
80 display_name: Optional snapshot Name.
81 display_description: User friendly snapshot description.
82 """
Chang Bo Guocc1623c2013-09-13 20:11:27 -070083 # NOTE(afazekas): it should use the volume namespace
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010084 snapshot = Element("snapshot", xmlns=XMLNS_11, volume_id=volume_id)
85 for key, value in kwargs.items():
86 snapshot.add_attr(key, value)
87 resp, body = self.post('snapshots', str(Document(snapshot)),
88 self.headers)
89 body = xml_to_json(etree.fromstring(body))
90 return resp, body
91
QingXin Mengdc95f5e2013-09-16 19:06:44 -070092 def update_snapshot(self, snapshot_id, **kwargs):
93 """Updates a snapshot."""
94 put_body = Element("snapshot", xmlns=XMLNS_11, **kwargs)
95
96 resp, body = self.put('snapshots/%s' % snapshot_id,
97 str(Document(put_body)),
98 self.headers)
99 body = xml_to_json(etree.fromstring(body))
100 return resp, body
101
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200102 # NOTE(afazekas): just for the wait function
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100103 def _get_snapshot_status(self, snapshot_id):
104 resp, body = self.get_snapshot(snapshot_id)
105 status = body['status']
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200106 # NOTE(afazekas): snapshot can reach an "error"
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100107 # state in a "normal" lifecycle
108 if (status == 'error'):
109 raise exceptions.SnapshotBuildErrorException(
Sean Dague14c68182013-04-14 15:34:30 -0400110 snapshot_id=snapshot_id)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100111
112 return status
113
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200114 # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100115 def wait_for_snapshot_status(self, snapshot_id, status):
116 """Waits for a Snapshot to reach a given status."""
117 start_time = time.time()
118 old_value = value = self._get_snapshot_status(snapshot_id)
119 while True:
120 dtime = time.time() - start_time
121 time.sleep(self.build_interval)
122 if value != old_value:
123 LOG.info('Value transition from "%s" to "%s"'
124 'in %d second(s).', old_value,
125 value, dtime)
126 if (value == status):
127 return value
128
129 if dtime > self.build_timeout:
130 message = ('Time Limit Exceeded! (%ds)'
131 'while waiting for %s, '
132 'but we got %s.' %
133 (self.build_timeout, status, value))
134 raise exceptions.TimeoutException(message)
135 time.sleep(self.build_interval)
136 old_value = value
137 value = self._get_snapshot_status(snapshot_id)
138
139 def delete_snapshot(self, snapshot_id):
140 """Delete Snapshot."""
141 return self.delete("snapshots/%s" % str(snapshot_id))
142
143 def is_resource_deleted(self, id):
144 try:
145 self.get_snapshot(id)
146 except exceptions.NotFound:
147 return True
148 return False
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800149
150 def reset_snapshot_status(self, snapshot_id, status):
151 """Reset the specified snapshot's status."""
152 post_body = Element("os-reset_status",
153 status=status
154 )
155 url = 'snapshots/%s/action' % str(snapshot_id)
156 resp, body = self.post(url, str(Document(post_body)), self.headers)
157 if body:
158 body = xml_to_json(etree.fromstring(body))
159 return resp, body
160
161 def update_snapshot_status(self, snapshot_id, status, progress):
162 """Update the specified snapshot's status."""
163 post_body = Element("os-update_snapshot_status",
164 status=status,
165 progress=progress
166 )
167 url = 'snapshots/%s/action' % str(snapshot_id)
168 resp, body = self.post(url, str(Document(post_body)), self.headers)
169 if body:
170 body = xml_to_json(etree.fromstring(body))
171 return resp, body
huangtianhua1346d702013-12-09 18:42:35 +0800172
173 def _metadata_body(self, meta):
174 post_body = Element('metadata')
175 for k, v in meta.items():
176 data = Element('meta', key=k)
177 data.append(Text(v))
178 post_body.append(data)
179 return post_body
180
181 def _parse_key_value(self, node):
182 """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
183 data = {}
184 for node in node.getchildren():
185 data[node.get('key')] = node.text
186 return data
187
188 def create_snapshot_metadata(self, snapshot_id, metadata):
189 """Create metadata for the snapshot."""
190 post_body = self._metadata_body(metadata)
191 resp, body = self.post('snapshots/%s/metadata' % snapshot_id,
192 str(Document(post_body)),
193 self.headers)
194 body = self._parse_key_value(etree.fromstring(body))
195 return resp, body
196
197 def get_snapshot_metadata(self, snapshot_id):
198 """Get metadata of the snapshot."""
199 url = "snapshots/%s/metadata" % str(snapshot_id)
200 resp, body = self.get(url, self.headers)
201 body = self._parse_key_value(etree.fromstring(body))
202 return resp, body
203
204 def update_snapshot_metadata(self, snapshot_id, metadata):
205 """Update metadata for the snapshot."""
206 put_body = self._metadata_body(metadata)
207 url = "snapshots/%s/metadata" % str(snapshot_id)
208 resp, body = self.put(url, str(Document(put_body)), self.headers)
209 body = self._parse_key_value(etree.fromstring(body))
210 return resp, body
211
212 def update_snapshot_metadata_item(self, snapshot_id, id, meta_item):
213 """Update metadata item for the snapshot."""
214 for k, v in meta_item.items():
215 put_body = Element('meta', key=k)
216 put_body.append(Text(v))
217 url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
218 resp, body = self.put(url, str(Document(put_body)), self.headers)
219 body = xml_to_json(etree.fromstring(body))
220 return resp, body
221
222 def delete_snapshot_metadata_item(self, snapshot_id, id):
223 """Delete metadata item for the snapshot."""
224 url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
225 return self.delete(url)