blob: a18ebd2e6e083ff591d372651789b35dd17a0b12 [file] [log] [blame]
Daryl Wallecked8bef32011-12-05 23:02:08 -06001from tempest import exceptions
chris fattarsi5098fa22012-04-17 13:27:00 -07002from tempest.common.rest_client import RestClient
Daryl Wallecke5b83d42011-11-10 14:39:02 -06003import json
Daryl Wallecke5b83d42011-11-10 14:39:02 -06004import time
5
6
Dan Smithcf8fab62012-08-14 08:03:48 -07007class ServersClientJSON(RestClient):
Daryl Wallecke5b83d42011-11-10 14:39:02 -06008
Daryl Walleck587385b2012-03-03 13:00:26 -06009 def __init__(self, config, username, password, auth_url, tenant_name=None):
Dan Smithcf8fab62012-08-14 08:03:48 -070010 super(ServersClientJSON, self).__init__(config, username, password,
11 auth_url, tenant_name)
chris fattarsi5098fa22012-04-17 13:27:00 -070012 self.service = self.config.compute.catalog_type
Daryl Wallecke5b83d42011-11-10 14:39:02 -060013
Rohit Karajgi95446a22011-12-12 06:21:43 -080014 def create_server(self, name, image_ref, flavor_ref, **kwargs):
Daryl Wallecke5b83d42011-11-10 14:39:02 -060015 """
16 Creates an instance of a server.
Rohit Karajgi95446a22011-12-12 06:21:43 -080017 name (Required): The name of the server.
18 image_ref (Required): Reference to the image used to build the server.
19 flavor_ref (Required): The flavor used to build the server.
20 Following optional keyword arguments are accepted:
Daryl Wallecke5b83d42011-11-10 14:39:02 -060021 adminPass: Sets the initial root password.
chris fattarsia6c2a732012-05-02 17:00:19 -070022 key_name: Key name of keypair that was created earlier.
David Kranz28e79de2012-04-12 15:49:41 -040023 meta: A dictionary of values to be used as metadata.
Daryl Wallecke5b83d42011-11-10 14:39:02 -060024 personality: A list of dictionaries for files to be injected into
25 the server.
Rohit Karajgi95446a22011-12-12 06:21:43 -080026 security_groups: A list of security group dicts.
27 networks: A list of network dicts with UUID and fixed_ip.
28 user_data: User data for instance.
29 availability_zone: Availability zone in which to launch instance.
Daryl Wallecke5b83d42011-11-10 14:39:02 -060030 accessIPv4: The IPv4 access address for the server.
31 accessIPv6: The IPv6 access address for the server.
Rohit Karajgi95446a22011-12-12 06:21:43 -080032 min_count: Count of minimum number of instances to launch.
33 max_count: Count of maximum number of instances to launch.
Daryl Wallecke36d5002012-03-28 09:56:10 -050034 disk_config: Determines if user or admin controls disk configuration.
Daryl Wallecke5b83d42011-11-10 14:39:02 -060035 """
Daryl Wallecke5b83d42011-11-10 14:39:02 -060036 post_body = {
37 'name': name,
38 'imageRef': image_ref,
David Kranz28e79de2012-04-12 15:49:41 -040039 'flavorRef': flavor_ref
Daryl Wallecke5b83d42011-11-10 14:39:02 -060040 }
41
chris fattarsia6c2a732012-05-02 17:00:19 -070042 for option in ['personality', 'adminPass', 'key_name',
Zhongyue Luoe0884a32012-09-25 17:24:17 +080043 'security_groups', 'networks', 'user_data',
44 'availability_zone', 'accessIPv4', 'accessIPv6',
45 'min_count', 'max_count', ('metadata', 'meta'),
46 ('OS-DCF:diskConfig', 'disk_config')]:
David Kranz28e79de2012-04-12 15:49:41 -040047 if isinstance(option, tuple):
48 post_param = option[0]
49 key = option[1]
50 else:
51 post_param = option
52 key = option
53 value = kwargs.get(key)
Zhongyue Luoe471d6e2012-09-17 17:02:43 +080054 if value is not None:
David Kranz28e79de2012-04-12 15:49:41 -040055 post_body[post_param] = value
Daryl Wallecke5b83d42011-11-10 14:39:02 -060056 post_body = json.dumps({'server': post_body})
chris fattarsi5098fa22012-04-17 13:27:00 -070057 resp, body = self.post('servers', post_body, self.headers)
Jay Pipes5135bfc2012-01-05 15:46:49 -050058
Daryl Wallecke5b83d42011-11-10 14:39:02 -060059 body = json.loads(body)
60 return resp, body['server']
61
62 def update_server(self, server_id, name=None, meta=None, accessIPv4=None,
63 accessIPv6=None):
64 """
65 Updates the properties of an existing server.
66 server_id: The id of an existing server.
67 name: The name of the server.
68 personality: A list of files to be injected into the server.
69 accessIPv4: The IPv4 access address for the server.
70 accessIPv6: The IPv6 access address for the server.
71 """
72
73 post_body = {}
74
Zhongyue Luoe471d6e2012-09-17 17:02:43 +080075 if meta is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -060076 post_body['metadata'] = meta
77
Zhongyue Luoe471d6e2012-09-17 17:02:43 +080078 if name is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -060079 post_body['name'] = name
80
Zhongyue Luoe471d6e2012-09-17 17:02:43 +080081 if accessIPv4 is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -060082 post_body['accessIPv4'] = accessIPv4
83
Zhongyue Luoe471d6e2012-09-17 17:02:43 +080084 if accessIPv6 is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -060085 post_body['accessIPv6'] = accessIPv6
86
87 post_body = json.dumps({'server': post_body})
chris fattarsi5098fa22012-04-17 13:27:00 -070088 resp, body = self.put("servers/%s" % str(server_id),
Zhongyue Luoe0884a32012-09-25 17:24:17 +080089 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -060090 body = json.loads(body)
91 return resp, body['server']
92
93 def get_server(self, server_id):
94 """Returns the details of an existing server"""
chris fattarsi5098fa22012-04-17 13:27:00 -070095 resp, body = self.get("servers/%s" % str(server_id))
Daryl Wallecke5b83d42011-11-10 14:39:02 -060096 body = json.loads(body)
97 return resp, body['server']
98
99 def delete_server(self, server_id):
100 """Deletes the given server"""
chris fattarsi5098fa22012-04-17 13:27:00 -0700101 return self.delete("servers/%s" % str(server_id))
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600102
103 def list_servers(self, params=None):
104 """Lists all servers for a user"""
105
106 url = 'servers'
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800107 if params is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600108 param_list = []
109 for param, value in params.iteritems():
110 param_list.append("%s=%s&" % (param, value))
111
112 url = "servers?" + "".join(param_list)
113
chris fattarsi5098fa22012-04-17 13:27:00 -0700114 resp, body = self.get(url)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600115 body = json.loads(body)
116 return resp, body
117
118 def list_servers_with_detail(self, params=None):
119 """Lists all servers in detail for a user"""
120
121 url = 'servers/detail'
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800122 if params is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600123 param_list = []
124 for param, value in params.iteritems():
125 param_list.append("%s=%s&" % (param, value))
126
127 url = "servers/detail?" + "".join(param_list)
128
chris fattarsi5098fa22012-04-17 13:27:00 -0700129 resp, body = self.get(url)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600130 body = json.loads(body)
131 return resp, body
132
133 def wait_for_server_status(self, server_id, status):
134 """Waits for a server to reach a given status"""
135 resp, body = self.get_server(server_id)
136 server_status = body['status']
137 start = int(time.time())
138
139 while(server_status != status):
140 time.sleep(self.build_interval)
141 resp, body = self.get_server(server_id)
142 server_status = body['status']
143
Jay Pipes5135bfc2012-01-05 15:46:49 -0500144 if server_status == 'ERROR':
145 raise exceptions.BuildErrorException(server_id=server_id)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600146
Eoghan Glynnf72969c2012-03-05 12:33:10 +0000147 timed_out = int(time.time()) - start >= self.build_timeout
148
149 if server_status != status and timed_out:
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800150 message = ('Server %s failed to reach %s status within the '
151 'required time (%s s).' %
152 (server_id, status, self.build_timeout))
Eoghan Glynnf72969c2012-03-05 12:33:10 +0000153 message += ' Current status: %s.' % server_status
Daryl Walleckf0087032011-12-18 13:37:05 -0600154 raise exceptions.TimeoutException(message)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600155
Jay Pipes051075a2012-04-28 17:39:37 -0400156 def wait_for_server_termination(self, server_id, ignore_error=False):
Rohit Karajgi4a64d232012-05-17 07:44:37 -0700157 """Waits for server to reach termination"""
158 start_time = int(time.time())
159 while True:
160 try:
161 resp, body = self.get_server(server_id)
162 except exceptions.NotFound:
163 return
164
165 server_status = body['status']
Jay Pipes051075a2012-04-28 17:39:37 -0400166 if server_status == 'ERROR' and not ignore_error:
Rohit Karajgi4a64d232012-05-17 07:44:37 -0700167 raise exceptions.BuildErrorException
168
169 if int(time.time()) - start_time >= self.build_timeout:
170 raise exceptions.TimeoutException
171
172 time.sleep(self.build_interval)
173
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600174 def list_addresses(self, server_id):
175 """Lists all addresses for a server"""
chris fattarsi5098fa22012-04-17 13:27:00 -0700176 resp, body = self.get("servers/%s/ips" % str(server_id))
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600177 body = json.loads(body)
178 return resp, body['addresses']
179
180 def list_addresses_by_network(self, server_id, network_id):
181 """Lists all addresses of a specific network type for a server"""
chris fattarsi5098fa22012-04-17 13:27:00 -0700182 resp, body = self.get("servers/%s/ips/%s" %
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800183 (str(server_id), network_id))
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600184 body = json.loads(body)
185 return resp, body
186
187 def change_password(self, server_id, password):
188 """Changes the root password for the server"""
189 post_body = {
190 'changePassword': {
191 'adminPass': password,
192 }
193 }
194
195 post_body = json.dumps(post_body)
chris fattarsi5098fa22012-04-17 13:27:00 -0700196 return self.post('servers/%s/action' % str(server_id),
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800197 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600198
199 def reboot(self, server_id, reboot_type):
200 """Reboots a server"""
201 post_body = {
202 'reboot': {
203 'type': reboot_type,
204 }
205 }
206
207 post_body = json.dumps(post_body)
chris fattarsi5098fa22012-04-17 13:27:00 -0700208 return self.post('servers/%s/action' % str(server_id),
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800209 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600210
211 def rebuild(self, server_id, image_ref, name=None, meta=None,
Daryl Wallecke36d5002012-03-28 09:56:10 -0500212 personality=None, adminPass=None, disk_config=None):
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600213 """Rebuilds a server with a new image"""
214 post_body = {
215 'imageRef': image_ref,
216 }
217
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800218 if name is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600219 post_body['name'] = name
220
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800221 if adminPass is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600222 post_body['adminPass'] = adminPass
223
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800224 if meta is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600225 post_body['metadata'] = meta
226
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800227 if personality is not None:
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600228 post_body['personality'] = personality
229
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800230 if disk_config is not None:
Daryl Wallecke36d5002012-03-28 09:56:10 -0500231 post_body['OS-DCF:diskConfig'] = disk_config
232
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600233 post_body = json.dumps({'rebuild': post_body})
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800234 resp, body = self.post('servers/%s/action' % str(server_id),
235 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600236 body = json.loads(body)
Brian Waldon738cd632011-12-12 18:45:09 -0500237 return resp, body['server']
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600238
Daryl Wallecke36d5002012-03-28 09:56:10 -0500239 def resize(self, server_id, flavor_ref, disk_config=None):
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600240 """Changes the flavor of a server."""
241 post_body = {
242 'resize': {
243 'flavorRef': flavor_ref,
244 }
245 }
246
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800247 if disk_config is not None:
Daryl Wallecke36d5002012-03-28 09:56:10 -0500248 post_body['resize']['OS-DCF:diskConfig'] = disk_config
249
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600250 post_body = json.dumps(post_body)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800251 resp, body = self.post('servers/%s/action' % str(server_id),
252 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600253 return resp, body
254
255 def confirm_resize(self, server_id):
256 """Confirms the flavor change for a server"""
257 post_body = {
Brian Waldon3bde07f2011-12-13 15:11:22 -0500258 'confirmResize': None,
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600259 }
260
261 post_body = json.dumps(post_body)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800262 resp, body = self.post('servers/%s/action' % str(server_id),
263 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600264 return resp, body
265
266 def revert_resize(self, server_id):
267 """Reverts a server back to its original flavor"""
268 post_body = {
Brian Waldon3bde07f2011-12-13 15:11:22 -0500269 'revertResize': None,
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600270 }
271
272 post_body = json.dumps(post_body)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800273 resp, body = self.post('servers/%s/action' % str(server_id),
274 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600275 return resp, body
276
277 def create_image(self, server_id, image_name):
278 """Creates an image of the given server"""
279 post_body = {
280 'createImage': {
281 'name': image_name,
282 }
283 }
284
285 post_body = json.dumps(post_body)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800286 resp, body = self.post('servers/%s/action' % str(server_id),
287 post_body, self.headers)
Daryl Wallecke5b83d42011-11-10 14:39:02 -0600288 return resp, body
Daryl Walleck73a9e7a2011-11-15 17:43:31 -0600289
290 def list_server_metadata(self, server_id):
chris fattarsi5098fa22012-04-17 13:27:00 -0700291 resp, body = self.get("servers/%s/metadata" % str(server_id))
Daryl Walleck73a9e7a2011-11-15 17:43:31 -0600292 body = json.loads(body)
293 return resp, body['metadata']
294
295 def set_server_metadata(self, server_id, meta):
296 post_body = json.dumps({'metadata': meta})
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800297 resp, body = self.put('servers/%s/metadata' % str(server_id),
298 post_body, self.headers)
Daryl Walleck73a9e7a2011-11-15 17:43:31 -0600299 body = json.loads(body)
300 return resp, body['metadata']
301
302 def update_server_metadata(self, server_id, meta):
303 post_body = json.dumps({'metadata': meta})
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800304 resp, body = self.post('servers/%s/metadata' % str(server_id),
305 post_body, self.headers)
Daryl Walleck73a9e7a2011-11-15 17:43:31 -0600306 body = json.loads(body)
307 return resp, body['metadata']
308
309 def get_server_metadata_item(self, server_id, key):
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800310 resp, body = self.get("servers/%s/metadata/%s" % (str(server_id), key))
Daryl Walleck73a9e7a2011-11-15 17:43:31 -0600311 body = json.loads(body)
312 return resp, body['meta']
313
314 def set_server_metadata_item(self, server_id, key, meta):
315 post_body = json.dumps({'meta': meta})
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800316 resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
317 post_body, self.headers)
Daryl Walleck73a9e7a2011-11-15 17:43:31 -0600318 body = json.loads(body)
319 return resp, body['meta']
320
321 def delete_server_metadata_item(self, server_id, key):
chris fattarsi5098fa22012-04-17 13:27:00 -0700322 resp, body = self.delete("servers/%s/metadata/%s" %
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800323 (str(server_id), key))
Daryl Walleck73a9e7a2011-11-15 17:43:31 -0600324 return resp, body
Dan Smithc18d8c62012-07-02 08:09:26 -0700325
326 def stop(self, server_id):
327 post_body = json.dumps({'os-stop': None})
328 resp, body = self.post('servers/%s/action' % server_id,
329 post_body, self.headers)
330
331 def start(self, server_id):
332 post_body = json.dumps({'os-start': None})
333 resp, body = self.post('servers/%s/action' % server_id,
334 post_body, self.headers)
335
336 def attach_volume(self, server_id, volume_id, device='/dev/vdz'):
337 """Attaches a volume to a server instance"""
338 post_body = json.dumps(
339 {'volumeAttachment': {
340 'volumeId': volume_id,
341 'device': device,
342 }
343 })
344 resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
345 post_body, self.headers)
346 return resp, body
347
348 def detach_volume(self, server_id, volume_id):
349 """Detaches a volume from a server instance"""
350 resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
351 (server_id, volume_id))
352 return resp, body
sapan-kona775cf632012-06-22 00:32:36 +0530353
354 def add_security_group(self, server_id, security_group_name):
355 """Adds a security group to the server"""
356 post_body = {
357 'addSecurityGroup': {
358 'name': security_group_name
359 }
360 }
361 post_body = json.dumps(post_body)
362 return self.post('servers/%s/action' % server_id,
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800363 post_body, self.headers)
sapan-kona775cf632012-06-22 00:32:36 +0530364
365 def remove_security_group(self, server_id, security_group_name):
366 """Removes a security group from the server"""
367 post_body = {
368 'removeSecurityGroup': {
369 'name': security_group_name
370 }
371 }
372 post_body = json.dumps(post_body)
373 return self.post('servers/%s/action' % server_id,
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800374 post_body, self.headers)