41 |
41 |
"""OpenStack Compute API 1.1 client"""
|
42 |
42 |
|
43 |
43 |
def list_servers(self, detail=False):
|
44 |
|
"""List servers, returned detailed output if detailed is True"""
|
|
44 |
"""
|
|
45 |
:param detail: if true, append full server details to each item
|
|
46 |
|
|
47 |
:returns: list of server ids and names
|
|
48 |
"""
|
45 |
49 |
detail = 'detail' if detail else ''
|
46 |
50 |
r = self.servers_get(command=detail)
|
47 |
51 |
return r.json['servers']['values']
|
48 |
52 |
|
49 |
53 |
def get_server_details(self, server_id, **kwargs):
|
50 |
|
"""Return detailed output on a server specified by its id"""
|
|
54 |
"""Return detailed info for a server
|
|
55 |
|
|
56 |
:param server_id: integer (int or str)
|
|
57 |
|
|
58 |
:returns: dict with server details
|
|
59 |
"""
|
51 |
60 |
r = self.servers_get(server_id, **kwargs)
|
52 |
61 |
return r.json['server']
|
53 |
62 |
|
54 |
63 |
def create_server(self, name, flavor_id, image_id, personality=None):
|
55 |
64 |
"""Submit request to create a new server
|
56 |
65 |
|
57 |
|
The flavor_id specifies the hardware configuration to use,
|
58 |
|
the image_id specifies the OS Image to be deployed inside the new
|
59 |
|
server.
|
|
66 |
:param name: (str)
|
|
67 |
|
|
68 |
:param flavor_id: integer id denoting a preset hardware configuration
|
|
69 |
|
|
70 |
:param image_id: (str) id denoting the OS image to run on the VM
|
60 |
71 |
|
61 |
|
The personality argument is a list of (file path, file contents)
|
62 |
|
tuples, describing files to be injected into the server upon creation.
|
|
72 |
:param personality: a list of (file path, file contents) tuples,
|
|
73 |
describing files to be injected into VM upon creation.
|
63 |
74 |
|
64 |
|
The call returns a dictionary describing the newly created server.
|
|
75 |
:returns: a dict with the new VMs details
|
|
76 |
|
|
77 |
:raises ClientError: wraps request errors
|
65 |
78 |
"""
|
66 |
79 |
req = {'server': {'name': name,
|
67 |
80 |
'flavorRef': flavor_id,
|
... | ... | |
94 |
107 |
return r.json['server']
|
95 |
108 |
|
96 |
109 |
def update_server_name(self, server_id, new_name):
|
97 |
|
"""Update the name of the server as reported by the API.
|
|
110 |
"""Update the name of the server as reported by the API (does not
|
|
111 |
modify the hostname used inside the VM)
|
|
112 |
|
|
113 |
:param server_id: integer (str or int)
|
98 |
114 |
|
99 |
|
This call does not modify the hostname actually used by the server
|
100 |
|
internally.
|
|
115 |
:param new_name: (str)
|
101 |
116 |
"""
|
102 |
117 |
req = {'server': {'name': new_name}}
|
103 |
118 |
r = self.servers_put(server_id, json_data=req)
|
104 |
119 |
r.release()
|
105 |
120 |
|
106 |
121 |
def delete_server(self, server_id):
|
107 |
|
"""Submit a deletion request for a server specified by id"""
|
|
122 |
"""Submit a deletion request for a server specified by id
|
|
123 |
|
|
124 |
:param server_id: integer (str or int)
|
|
125 |
"""
|
108 |
126 |
r = self.servers_delete(server_id)
|
109 |
127 |
r.release()
|
110 |
128 |
|
111 |
129 |
def reboot_server(self, server_id, hard=False):
|
112 |
|
"""Submit a reboot request for a server specified by id"""
|
|
130 |
"""
|
|
131 |
:param server_id: integer (str or int)
|
|
132 |
|
|
133 |
:param hard: perform a hard reboot if true, soft reboot otherwise
|
|
134 |
"""
|
113 |
135 |
type = 'HARD' if hard else 'SOFT'
|
114 |
136 |
req = {'reboot': {'type': type}}
|
115 |
137 |
r = self.servers_post(server_id, 'action', json_data=req)
|
116 |
138 |
r.release()
|
117 |
139 |
|
118 |
140 |
def get_server_metadata(self, server_id, key=''):
|
|
141 |
"""
|
|
142 |
:param server_id: integer (str or int)
|
|
143 |
|
|
144 |
:param key: (str) the metadatum key (all metadata if not given)
|
|
145 |
|
|
146 |
:returns: a key:val dict of requests metadata
|
|
147 |
"""
|
119 |
148 |
command = path4url('meta', key)
|
120 |
149 |
r = self.servers_get(server_id, command)
|
121 |
150 |
return r.json['meta'] if key != '' else r.json['metadata']['values']
|
122 |
151 |
|
123 |
152 |
def create_server_metadata(self, server_id, key, val):
|
|
153 |
"""
|
|
154 |
:param server_id: integer (str or int)
|
|
155 |
|
|
156 |
:param key: (str)
|
|
157 |
|
|
158 |
:param val: (str)
|
|
159 |
|
|
160 |
:returns: dict of updated key:val metadata
|
|
161 |
"""
|
124 |
162 |
req = {'meta': {key: val}}
|
125 |
163 |
r = self.servers_put(server_id,
|
126 |
164 |
'meta/' + key,
|
... | ... | |
129 |
167 |
return r.json['meta']
|
130 |
168 |
|
131 |
169 |
def update_server_metadata(self, server_id, **metadata):
|
|
170 |
"""
|
|
171 |
:param server_id: integer (str or int)
|
|
172 |
|
|
173 |
:param metadata: dict of key:val metadata
|
|
174 |
|
|
175 |
:returns: dict of updated key:val metadata
|
|
176 |
"""
|
132 |
177 |
req = {'metadata': metadata}
|
133 |
178 |
r = self.servers_post(server_id, 'meta', json_data=req, success=201)
|
134 |
179 |
return r.json['metadata']
|
135 |
180 |
|
136 |
181 |
def delete_server_metadata(self, server_id, key):
|
|
182 |
"""
|
|
183 |
:param server_id: integer (str or int)
|
|
184 |
|
|
185 |
:param key: (str) the meta key
|
|
186 |
"""
|
137 |
187 |
r = self.servers_delete(server_id, 'meta/' + key)
|
138 |
188 |
r.release()
|
139 |
189 |
|
140 |
|
def flavors_get(self, flavor_id='', command='', **kwargs):
|
141 |
|
"""GET base_url[/flavor_id][/command]
|
142 |
|
@param flavor_id
|
143 |
|
@param command
|
|
190 |
def list_flavors(self, detail=False):
|
144 |
191 |
"""
|
145 |
|
path = path4url('flavors', flavor_id, command)
|
146 |
|
success = kwargs.pop('success', 200)
|
147 |
|
return self.get(path, success=success, **kwargs)
|
|
192 |
:param detail: (bool) detailed flavor info if set, short if not
|
148 |
193 |
|
149 |
|
def list_flavors(self, detail=False):
|
|
194 |
:returns: (dict) flavor info
|
|
195 |
"""
|
150 |
196 |
detail = 'detail' if detail else ''
|
151 |
197 |
r = self.flavors_get(command='detail')
|
152 |
198 |
return r.json['flavors']['values']
|
153 |
199 |
|
154 |
200 |
def get_flavor_details(self, flavor_id):
|
|
201 |
"""
|
|
202 |
:param flavor_id: integer (str or int)
|
|
203 |
|
|
204 |
:returns: dict
|
|
205 |
"""
|
155 |
206 |
r = self.flavors_get(flavor_id)
|
156 |
207 |
return r.json['flavor']
|
157 |
208 |
|
158 |
|
def images_get(self, image_id='', command='', **kwargs):
|
159 |
|
"""GET base_url[/image_id][/command]
|
160 |
|
@param image_id
|
161 |
|
@param command
|
162 |
|
"""
|
163 |
|
path = path4url('images', image_id, command)
|
164 |
|
success = kwargs.pop('success', 200)
|
165 |
|
return self.get(path, success=success, **kwargs)
|
166 |
|
|
167 |
|
def images_delete(self, image_id='', command='', **kwargs):
|
168 |
|
"""DEL ETE base_url[/image_id][/command]
|
169 |
|
@param image_id
|
170 |
|
@param command
|
171 |
|
"""
|
172 |
|
path = path4url('images', image_id, command)
|
173 |
|
success = kwargs.pop('success', 204)
|
174 |
|
return self.delete(path, success=success, **kwargs)
|
175 |
|
|
176 |
|
def images_post(self, image_id='', command='', json_data=None, **kwargs):
|
177 |
|
"""POST base_url/images[/image_id]/[command] request
|
178 |
|
@param image_id or ''
|
179 |
|
@param command: can be 'action' or ''
|
180 |
|
@param json_data: a json valid dict that will be send as data
|
181 |
|
"""
|
182 |
|
data = json_data
|
183 |
|
if json_data is not None:
|
184 |
|
data = json.dumps(json_data)
|
185 |
|
self.set_header('Content-Type', 'application/json')
|
186 |
|
self.set_header('Content-Length', len(data))
|
187 |
|
|
188 |
|
path = path4url('images', image_id, command)
|
189 |
|
success = kwargs.pop('success', 201)
|
190 |
|
return self.post(path, data=data, success=success, **kwargs)
|
191 |
|
|
192 |
|
def images_put(self, image_id='', command='', json_data=None, **kwargs):
|
193 |
|
"""PUT base_url/images[/image_id]/[command] request
|
194 |
|
@param image_id or ''
|
195 |
|
@param command: can be 'action' or ''
|
196 |
|
@param json_data: a json valid dict that will be send as data
|
197 |
|
"""
|
198 |
|
data = json_data
|
199 |
|
if json_data is not None:
|
200 |
|
data = json.dumps(json_data)
|
201 |
|
self.set_header('Content-Type', 'application/json')
|
202 |
|
self.set_header('Content-Length', len(data))
|
203 |
|
|
204 |
|
path = path4url('images', image_id, command)
|
205 |
|
success = kwargs.pop('success', 201)
|
206 |
|
return self.put(path, data=data, success=success, **kwargs)
|
207 |
|
|
208 |
209 |
def list_images(self, detail=False):
|
|
210 |
"""
|
|
211 |
:param detail: (bool) detailed info if set, short if not
|
|
212 |
|
|
213 |
:returns: dict id,name + full info if detail
|
|
214 |
"""
|
209 |
215 |
detail = 'detail' if detail else ''
|
210 |
216 |
r = self.images_get(command=detail)
|
211 |
217 |
return r.json['images']['values']
|
212 |
218 |
|
213 |
219 |
def get_image_details(self, image_id, **kwargs):
|
|
220 |
"""
|
|
221 |
:param image_id: integer (str or int)
|
|
222 |
|
|
223 |
:returns: dict
|
|
224 |
|
|
225 |
:raises ClientError: 404 if image not available
|
|
226 |
"""
|
214 |
227 |
r = self.images_get(image_id, **kwargs)
|
215 |
228 |
try:
|
216 |
229 |
return r.json['image']
|
... | ... | |
219 |
232 |
details='Image %d not found or not accessible')
|
220 |
233 |
|
221 |
234 |
def delete_image(self, image_id):
|
|
235 |
"""
|
|
236 |
:param image_id: (str)
|
|
237 |
"""
|
222 |
238 |
r = self.images_delete(image_id)
|
223 |
239 |
r.release()
|
224 |
240 |
|
225 |
241 |
def get_image_metadata(self, image_id, key=''):
|
|
242 |
"""
|
|
243 |
:param image_id: (str)
|
|
244 |
|
|
245 |
:param key: (str) the metadatum key
|
|
246 |
|
|
247 |
:returns (dict) metadata if key not set, specific metadatum otherwise
|
|
248 |
"""
|
226 |
249 |
command = path4url('meta', key)
|
227 |
250 |
r = self.images_get(image_id, command)
|
228 |
|
return r.json['meta'] if key != '' else r.json['metadata']['values']
|
|
251 |
return r.json['meta'] if key else r.json['metadata']['values']
|
229 |
252 |
|
230 |
253 |
def create_image_metadata(self, image_id, key, val):
|
|
254 |
"""
|
|
255 |
:param image_id: integer (str or int)
|
|
256 |
|
|
257 |
:param key: (str) metadatum key
|
|
258 |
|
|
259 |
:param val: (str) metadatum value
|
|
260 |
|
|
261 |
:returns: (dict) updated metadata
|
|
262 |
"""
|
231 |
263 |
req = {'meta': {key: val}}
|
232 |
264 |
r = self.images_put(image_id, 'meta/' + key, json_data=req)
|
233 |
265 |
return r.json['meta']
|
234 |
266 |
|
235 |
267 |
def update_image_metadata(self, image_id, **metadata):
|
|
268 |
"""
|
|
269 |
:param image_id: (str)
|
|
270 |
|
|
271 |
:param metadata: dict
|
|
272 |
|
|
273 |
:returns: updated metadata
|
|
274 |
"""
|
236 |
275 |
req = {'metadata': metadata}
|
237 |
276 |
r = self.images_post(image_id, 'meta', json_data=req)
|
238 |
277 |
return r.json['metadata']
|
239 |
278 |
|
240 |
279 |
def delete_image_metadata(self, image_id, key):
|
|
280 |
"""
|
|
281 |
:param image_id: (str)
|
|
282 |
|
|
283 |
:param key: (str) metadatum key
|
|
284 |
"""
|
241 |
285 |
command = path4url('meta', key)
|
242 |
286 |
r = self.images_delete(image_id, command)
|
243 |
287 |
r.release()
|