blob: 458001b5dab7f591a837d6eb22a9657a5cb6d9e4 [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 Treinish684d8992014-01-30 16:27:40 +000019from tempest import config
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010020from tempest import exceptions
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040021from tempest.openstack.common import log as logging
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010022from tempest.services.compute.xml.common import Document
23from tempest.services.compute.xml.common import Element
huangtianhua1346d702013-12-09 18:42:35 +080024from tempest.services.compute.xml.common import Text
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010025from tempest.services.compute.xml.common import xml_to_json
26from tempest.services.compute.xml.common import XMLNS_11
27
Matthew Treinish684d8992014-01-30 16:27:40 +000028CONF = config.CONF
29
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010030LOG = logging.getLogger(__name__)
31
32
vponomaryov960eeb42014-02-22 18:25:25 +020033class SnapshotsClientXML(rest_client.RestClient):
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010034 """Client class to send CRUD Volume API requests."""
vponomaryov960eeb42014-02-22 18:25:25 +020035 TYPE = "xml"
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010036
Andrea Frittoli8bbdb162014-01-06 11:06:13 +000037 def __init__(self, auth_provider):
38 super(SnapshotsClientXML, self).__init__(auth_provider)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010039
Matthew Treinish684d8992014-01-30 16:27:40 +000040 self.service = CONF.volume.catalog_type
41 self.build_interval = CONF.volume.build_interval
42 self.build_timeout = CONF.volume.build_timeout
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010043
44 def list_snapshots(self, params=None):
45 """List all snapshot."""
46 url = 'snapshots'
47
48 if params:
49 url += '?%s' % urllib.urlencode(params)
50
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020051 resp, body = self.get(url)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010052 body = etree.fromstring(body)
Giulio Fidentee92956b2013-06-06 18:38:48 +020053 snapshots = []
54 for snap in body:
55 snapshots.append(xml_to_json(snap))
56 return resp, snapshots
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010057
58 def list_snapshots_with_detail(self, params=None):
59 """List all the details of snapshot."""
60 url = 'snapshots/detail'
61
62 if params:
63 url += '?%s' % urllib.urlencode(params)
64
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020065 resp, body = self.get(url)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010066 body = etree.fromstring(body)
67 snapshots = []
Giulio Fidentee92956b2013-06-06 18:38:48 +020068 for snap in body:
69 snapshots.append(xml_to_json(snap))
70 return resp, snapshots
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010071
72 def get_snapshot(self, snapshot_id):
73 """Returns the details of a single snapshot."""
74 url = "snapshots/%s" % str(snapshot_id)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020075 resp, body = self.get(url)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010076 body = etree.fromstring(body)
77 return resp, xml_to_json(body)
78
79 def create_snapshot(self, volume_id, **kwargs):
Attila Fazekasb2902af2013-02-16 16:22:44 +010080 """Creates a new snapshot.
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010081 volume_id(Required): id of the volume.
82 force: Create a snapshot even if the volume attached (Default=False)
83 display_name: Optional snapshot Name.
84 display_description: User friendly snapshot description.
85 """
Chang Bo Guocc1623c2013-09-13 20:11:27 -070086 # NOTE(afazekas): it should use the volume namespace
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010087 snapshot = Element("snapshot", xmlns=XMLNS_11, volume_id=volume_id)
88 for key, value in kwargs.items():
89 snapshot.add_attr(key, value)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020090 resp, body = self.post('snapshots', str(Document(snapshot)))
Attila Fazekas36b1fcf2013-01-31 16:41:04 +010091 body = xml_to_json(etree.fromstring(body))
92 return resp, body
93
QingXin Mengdc95f5e2013-09-16 19:06:44 -070094 def update_snapshot(self, snapshot_id, **kwargs):
95 """Updates a snapshot."""
96 put_body = Element("snapshot", xmlns=XMLNS_11, **kwargs)
97
98 resp, body = self.put('snapshots/%s' % snapshot_id,
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +020099 str(Document(put_body)))
QingXin Mengdc95f5e2013-09-16 19:06:44 -0700100 body = xml_to_json(etree.fromstring(body))
101 return resp, body
102
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200103 # NOTE(afazekas): just for the wait function
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100104 def _get_snapshot_status(self, snapshot_id):
105 resp, body = self.get_snapshot(snapshot_id)
106 status = body['status']
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200107 # NOTE(afazekas): snapshot can reach an "error"
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100108 # state in a "normal" lifecycle
109 if (status == 'error'):
110 raise exceptions.SnapshotBuildErrorException(
Sean Dague14c68182013-04-14 15:34:30 -0400111 snapshot_id=snapshot_id)
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100112
113 return status
114
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200115 # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
Attila Fazekas36b1fcf2013-01-31 16:41:04 +0100116 def wait_for_snapshot_status(self, snapshot_id, status):
117 """Waits for a Snapshot to reach a given status."""
118 start_time = time.time()
119 old_value = value = self._get_snapshot_status(snapshot_id)
120 while True:
121 dtime = time.time() - start_time
122 time.sleep(self.build_interval)
123 if value != old_value:
124 LOG.info('Value transition from "%s" to "%s"'
125 'in %d second(s).', old_value,
126 value, dtime)
127 if (value == status):
128 return value
129
130 if dtime > self.build_timeout:
131 message = ('Time Limit Exceeded! (%ds)'
132 'while waiting for %s, '
133 'but we got %s.' %
134 (self.build_timeout, status, value))
135 raise exceptions.TimeoutException(message)
136 time.sleep(self.build_interval)
137 old_value = value
138 value = self._get_snapshot_status(snapshot_id)
139
140 def delete_snapshot(self, snapshot_id):
141 """Delete Snapshot."""
142 return self.delete("snapshots/%s" % str(snapshot_id))
143
144 def is_resource_deleted(self, id):
145 try:
146 self.get_snapshot(id)
147 except exceptions.NotFound:
148 return True
149 return False
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800150
151 def reset_snapshot_status(self, snapshot_id, status):
152 """Reset the specified snapshot's status."""
153 post_body = Element("os-reset_status",
154 status=status
155 )
156 url = 'snapshots/%s/action' % str(snapshot_id)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200157 resp, body = self.post(url, str(Document(post_body)))
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800158 if body:
159 body = xml_to_json(etree.fromstring(body))
160 return resp, body
161
162 def update_snapshot_status(self, snapshot_id, status, progress):
163 """Update the specified snapshot's status."""
164 post_body = Element("os-update_snapshot_status",
165 status=status,
166 progress=progress
167 )
168 url = 'snapshots/%s/action' % str(snapshot_id)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200169 resp, body = self.post(url, str(Document(post_body)))
zhangyanzid4d3c6d2013-11-06 09:27:13 +0800170 if body:
171 body = xml_to_json(etree.fromstring(body))
172 return resp, body
huangtianhua1346d702013-12-09 18:42:35 +0800173
174 def _metadata_body(self, meta):
175 post_body = Element('metadata')
176 for k, v in meta.items():
177 data = Element('meta', key=k)
178 data.append(Text(v))
179 post_body.append(data)
180 return post_body
181
182 def _parse_key_value(self, node):
183 """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
184 data = {}
185 for node in node.getchildren():
186 data[node.get('key')] = node.text
187 return data
188
189 def create_snapshot_metadata(self, snapshot_id, metadata):
190 """Create metadata for the snapshot."""
191 post_body = self._metadata_body(metadata)
192 resp, body = self.post('snapshots/%s/metadata' % snapshot_id,
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200193 str(Document(post_body)))
huangtianhua1346d702013-12-09 18:42:35 +0800194 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)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200200 resp, body = self.get(url)
huangtianhua1346d702013-12-09 18:42:35 +0800201 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)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200208 resp, body = self.put(url, str(Document(put_body)))
huangtianhua1346d702013-12-09 18:42:35 +0800209 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))
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200218 resp, body = self.put(url, str(Document(put_body)))
huangtianhua1346d702013-12-09 18:42:35 +0800219 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)
wanghaofa3908c2014-01-15 19:34:03 +0800226
227 def force_delete_snapshot(self, snapshot_id):
228 """Force Delete Snapshot."""
229 post_body = Element("os-force_delete")
230 url = 'snapshots/%s/action' % str(snapshot_id)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200231 resp, body = self.post(url, str(Document(post_body)))
wanghaofa3908c2014-01-15 19:34:03 +0800232 if body:
233 body = xml_to_json(etree.fromstring(body))
234 return resp, body