Skip to content
This repository was archived by the owner on Nov 29, 2023. It is now read-only.

Commit 9deb35f

Browse files
committed
merge fixup
2 parents 2acc75b + 6b56f3b commit 9deb35f

19 files changed

Lines changed: 331 additions & 214 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@
44
cover
55
*.pyc
66
.idea
7+
*.swp
8+
*~
9+
build
10+
dist
11+
python_novaclient.egg-info

novaclient/base.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ def _list(self, url, response_key, obj_class=None, body=None):
6767

6868
if obj_class is None:
6969
obj_class = self.resource_class
70+
7071
data = body[response_key]
7172
# NOTE(ja): keystone returns values as list as {'values': [ ... ]}
7273
# unlike other services which just return the list...
7374
if type(data) is dict:
7475
data = data['values']
75-
return [obj_class(self, res) for res in data if res]
76+
return [obj_class(self, res, loaded=True) for res in data if res]
7677

7778
def _get(self, url, response_key):
7879
resp, body = self.api.client.get(url)
@@ -203,19 +204,28 @@ class Resource(object):
203204
"""
204205
A resource represents a particular instance of an object (server, flavor,
205206
etc). This is pretty much just a bag for attributes.
207+
208+
:param manager: Manager object
209+
:param info: dictionary representing resource attributes
210+
:param loaded: prevent lazy-loading if set to True
206211
"""
207-
def __init__(self, manager, info):
212+
def __init__(self, manager, info, loaded=False):
208213
self.manager = manager
209214
self._info = info
210215
self._add_details(info)
216+
self._loaded = loaded
211217

212218
def _add_details(self, info):
213219
for (k, v) in info.iteritems():
214220
setattr(self, k, v)
215221

216222
def __getattr__(self, k):
217-
self.get()
218223
if k not in self.__dict__:
224+
#NOTE(bcwaldon): disallow lazy-loading if already loaded once
225+
if not self.is_loaded():
226+
self.get()
227+
return self.__getattr__(k)
228+
219229
raise AttributeError(k)
220230
else:
221231
return self.__dict__[k]
@@ -227,8 +237,11 @@ def __repr__(self):
227237
return "<%s %s>" % (self.__class__.__name__, info)
228238

229239
def get(self):
240+
# set_loaded() first ... so if we have to bail, we know we tried.
241+
self.set_loaded(True)
230242
if not hasattr(self.manager, 'get'):
231243
return
244+
232245
new = self.manager.get(self.id)
233246
if new:
234247
self._add_details(new._info)
@@ -239,3 +252,9 @@ def __eq__(self, other):
239252
if hasattr(self, 'id') and hasattr(other, 'id'):
240253
return self.id == other.id
241254
return self._info == other._info
255+
256+
def is_loaded(self):
257+
return self._loaded
258+
259+
def set_loaded(self, val):
260+
self._loaded = val

novaclient/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ def http_log(self, args, kwargs, resp, body):
6666
string_parts.append(header)
6767

6868
_logger.debug("REQ: %s\n" % "".join(string_parts))
69+
if 'body' in kwargs:
70+
_logger.debug("REQ BODY: %s\n" % (kwargs['body']))
6971
_logger.debug("RESP:%s %s\n", resp, body)
7072

7173
def request(self, *args, **kwargs):

novaclient/shell.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ def main(self, argv):
163163
raise exc.CommandError("You must provide an API key, either"
164164
"via --apikey or via"
165165
"env[NOVA_API_KEY]")
166+
if options.version and options.version != '1.0':
167+
if not projectid:
168+
raise exc.CommandError("You must provide an projectid, either"
169+
"via --projectid or via"
170+
"env[NOVA_PROJECT_ID")
171+
172+
if not url:
173+
raise exc.CommandError("You must provide a auth url, either"
174+
"via --url or via"
175+
"env[NOVA_URL")
166176

167177
self.cs = self.get_api_class(options.version) \
168178
(user, apikey, projectid, url,

novaclient/v1_0/zones.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222

2323

2424
class Weighting(base.Resource):
25-
def __init__(self, manager, info):
25+
def __init__(self, manager, info, loaded=False):
2626
self.name = "n/a"
27-
super(Weighting, self).__init__(manager, info)
27+
super(Weighting, self).__init__(manager, info, loaded)
2828

2929
def __repr__(self):
3030
return "<Weighting: %s>" % self.name
@@ -35,11 +35,11 @@ def to_dict(self):
3535

3636

3737
class Zone(base.Resource):
38-
def __init__(self, manager, info):
38+
def __init__(self, manager, info, loaded=False):
3939
self.name = "n/a"
4040
self.is_active = "n/a"
4141
self.capabilities = "n/a"
42-
super(Zone, self).__init__(manager, info)
42+
super(Zone, self).__init__(manager, info, loaded)
4343

4444
def __repr__(self):
4545
return "<Zone: %s>" % self.api_url

novaclient/v1_1/base.py

Lines changed: 4 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -15,117 +15,11 @@
1515
# License for the specific language governing permissions and limitations
1616
# under the License.
1717

18-
"""
19-
Base utilities to build API operation managers and objects on top of.
20-
"""
21-
18+
from novaclient import base
2219
from novaclient import exceptions
2320

24-
# Python 2.4 compat
25-
try:
26-
all
27-
except NameError:
28-
def all(iterable):
29-
return True not in (not x for x in iterable)
30-
31-
32-
def getid(obj):
33-
"""
34-
Abstracts the common pattern of allowing both an object or an object's ID
35-
(UUID) as a parameter when dealing with relationships.
36-
"""
37-
38-
# Try to return the object's UUID first, if we have a UUID.
39-
try:
40-
if obj.uuid:
41-
return obj.uuid
42-
except AttributeError:
43-
pass
44-
try:
45-
return obj.id
46-
except AttributeError:
47-
return obj
48-
49-
50-
class Manager(object):
51-
"""
52-
Managers interact with a particular type of API (servers, flavors, images,
53-
etc.) and provide CRUD operations for them.
54-
"""
55-
resource_class = None
56-
57-
def __init__(self, api):
58-
self.api = api
59-
60-
def _list(self, url, response_key, obj_class=None, body=None):
61-
resp = None
62-
if body:
63-
resp, body = self.api.client.post(url, body=body)
64-
else:
65-
resp, body = self.api.client.get(url)
66-
67-
if obj_class is None:
68-
obj_class = self.resource_class
69-
return [obj_class(self, res)
70-
for res in body[response_key] if res]
71-
72-
def _get(self, url, response_key):
73-
resp, body = self.api.client.get(url)
74-
return self.resource_class(self, body[response_key])
75-
76-
def _create(self, url, body, response_key, return_raw=False):
77-
resp, body = self.api.client.post(url, body=body)
78-
if return_raw:
79-
return body[response_key]
80-
return self.resource_class(self, body[response_key])
81-
82-
def _delete(self, url):
83-
resp, body = self.api.client.delete(url)
84-
85-
def _update(self, url, body):
86-
resp, body = self.api.client.put(url, body=body)
87-
88-
89-
class ManagerWithFind(Manager):
90-
"""
91-
Like a `Manager`, but with additional `find()`/`findall()` methods.
92-
"""
93-
def find(self, **kwargs):
94-
"""
95-
Find a single item with attributes matching ``**kwargs``.
96-
97-
This isn't very efficient: it loads the entire list then filters on
98-
the Python side.
99-
"""
100-
rl = self.findall(**kwargs)
101-
try:
102-
return rl[0]
103-
except IndexError:
104-
raise exceptions.NotFound(404, "No %s matching %s." %
105-
(self.resource_class.__name__, kwargs))
106-
107-
def findall(self, **kwargs):
108-
"""
109-
Find all items with attributes matching ``**kwargs``.
110-
111-
This isn't very efficient: it loads the entire list then filters on
112-
the Python side.
113-
"""
114-
found = []
115-
searches = kwargs.items()
116-
117-
for obj in self.list():
118-
try:
119-
if all(getattr(obj, attr) == value
120-
for (attr, value) in searches):
121-
found.append(obj)
122-
except AttributeError:
123-
continue
124-
125-
return found
12621

127-
128-
class BootingManagerWithFind(ManagerWithFind):
22+
class BootingManagerWithFind(base.ManagerWithFind):
12923
"""Like a `ManagerWithFind`, but has the ability to boot servers."""
13024
def _boot(self, resource_url, response_key, name, image, flavor,
13125
meta=None, files=None, zone_blob=None,
@@ -155,8 +49,8 @@ def _boot(self, resource_url, response_key, name, image, flavor,
15549
"""
15650
body = {"server": {
15751
"name": name,
158-
"imageRef": getid(image),
159-
"flavorRef": getid(flavor),
52+
"imageRef": base.getid(image),
53+
"flavorRef": base.getid(flavor),
16054
}}
16155
if meta:
16256
body["server"]["metadata"] = meta
@@ -194,43 +88,3 @@ def _boot(self, resource_url, response_key, name, image, flavor,
19488

19589
return self._create(resource_url, body, response_key,
19690
return_raw=return_raw)
197-
198-
199-
class Resource(object):
200-
"""
201-
A resource represents a particular instance of an object (server, flavor,
202-
etc). This is pretty much just a bag for attributes.
203-
"""
204-
def __init__(self, manager, info):
205-
self.manager = manager
206-
self._info = info
207-
self._add_details(info)
208-
209-
def _add_details(self, info):
210-
for (k, v) in info.iteritems():
211-
setattr(self, k, v)
212-
213-
def __getattr__(self, k):
214-
self.get()
215-
if k not in self.__dict__:
216-
raise AttributeError(k)
217-
else:
218-
return self.__dict__[k]
219-
220-
def __repr__(self):
221-
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
222-
k != 'manager')
223-
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
224-
return "<%s %s>" % (self.__class__.__name__, info)
225-
226-
def get(self):
227-
new = self.manager.get(self.id)
228-
if new:
229-
self._add_details(new._info)
230-
231-
def __eq__(self, other):
232-
if not isinstance(other, self.__class__):
233-
return False
234-
if hasattr(self, 'id') and hasattr(other, 'id'):
235-
return self.id == other.id
236-
return self._info == other._info

novaclient/v1_1/floating_ips.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515

16-
from novaclient.v1_1 import base
16+
from novaclient import base
1717

1818

1919
class FloatingIP(base.Resource):

novaclient/v1_1/images.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,24 @@ def delete(self, image):
5656
:param image: The :class:`Image` (or its ID) to delete.
5757
"""
5858
self._delete("/images/%s" % base.getid(image))
59+
60+
def set_meta(self, image, metadata):
61+
"""
62+
Set an images metadata
63+
64+
:param image: The :class:`Image` to add metadata to
65+
:param metadata: A dict of metadata to add to the image
66+
"""
67+
body = {'metadata': metadata}
68+
return self._create("/images/%s/metadata" % base.getid(image), body,
69+
"metadata")
70+
71+
def delete_meta(self, image, keys):
72+
"""
73+
Delete metadata from an image
74+
75+
:param image: The :class:`Image` to add metadata to
76+
:param keys: A list of metadata keys to delete from the image
77+
"""
78+
for k in keys:
79+
self._delete("/images/%s/metadata/%s" % (base.getid(image), k))

novaclient/v1_1/servers.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,25 @@ def create_image(self, server, image_name, metadata=None):
461461
self._action('createImage', server,
462462
{'name': image_name, 'metadata': metadata or {}})
463463

464+
def set_meta(self, server, metadata):
465+
"""
466+
Set a servers metadata
467+
:param server: The :class:`Server` to add metadata to
468+
:param metadata: A dict of metadata to add to the server
469+
"""
470+
body = {'metadata': metadata}
471+
return self._create("/servers/%s/metadata" % base.getid(server),
472+
body, "metadata")
473+
474+
def delete_meta(self, server, keys):
475+
"""
476+
Delete metadata from an server
477+
:param server: The :class:`Server` to add metadata to
478+
:param keys: A list of metadata keys to delete from the server
479+
"""
480+
for k in keys:
481+
self._delete("/servers/%s/metadata/%s" % (base.getid(server), k))
482+
464483
def _action(self, action, server, info=None):
465484
"""
466485
Perform a server "action" -- reboot/rebuild/resize/etc.

0 commit comments

Comments
 (0)