Merge lp:~zulcss/ubuntu/precise/python-glanceclient/trunk into lp:~ubuntu-cloud-archive/ubuntu/precise/python-glanceclient/trunk

Proposed by Chuck Short
Status: Merged
Approved by: James Page
Approved revision: 11
Merged at revision: 11
Proposed branch: lp:~zulcss/ubuntu/precise/python-glanceclient/trunk
Merge into: lp:~ubuntu-cloud-archive/ubuntu/precise/python-glanceclient/trunk
Diff against target: 2145 lines (+1222/-240)
36 files modified
.pc/applied-patches (+0/-1)
.pc/fix-ubuntu-tests.patch/tools/test-requires (+0/-11)
AUTHORS (+10/-1)
ChangeLog (+463/-0)
PKG-INFO (+2/-1)
debian/changelog (+17/-0)
debian/control (+4/-2)
debian/patches/fix-ubuntu-tests.patch (+9/-14)
debian/pydist-overrides (+1/-0)
debian/rules (+1/-1)
doc/source/index.rst (+15/-0)
glanceclient/__init__.py (+20/-3)
glanceclient/common/http.py (+150/-80)
glanceclient/common/utils.py (+15/-0)
glanceclient/exc.py (+4/-0)
glanceclient/openstack/common/setup.py (+60/-45)
glanceclient/openstack/common/version.py (+1/-2)
glanceclient/shell.py (+10/-0)
glanceclient/v1/images.py (+14/-14)
glanceclient/v1/shell.py (+96/-37)
glanceclient/version.py (+0/-21)
glanceclient/versioninfo (+1/-1)
python_glanceclient.egg-info/PKG-INFO (+2/-1)
python_glanceclient.egg-info/SOURCES.txt (+5/-1)
python_glanceclient.egg-info/requires.txt (+2/-1)
setup.py (+2/-0)
tests/test_http.py (+65/-0)
tests/test_ssl.py (+112/-0)
tests/test_utils.py (+7/-0)
tests/utils.py (+9/-1)
tests/v1/test_images.py (+1/-1)
tests/var/ca.crt (+35/-0)
tests/var/certificate.crt (+30/-0)
tests/var/privatekey.key (+51/-0)
tools/pip-requires (+2/-1)
tools/test-requires (+6/-0)
To merge this branch: bzr merge lp:~zulcss/ubuntu/precise/python-glanceclient/trunk
Reviewer Review Type Date Requested Status
James Page Approve
Review via email: mp+136429@code.launchpad.net

Description of the change

python-glanceclient g1

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file '.pc/applied-patches'
2--- .pc/applied-patches 2012-06-22 10:41:03 +0000
3+++ .pc/applied-patches 1970-01-01 00:00:00 +0000
4@@ -1,1 +0,0 @@
5-fix-ubuntu-tests.patch
6
7=== removed directory '.pc/fix-ubuntu-tests.patch'
8=== removed directory '.pc/fix-ubuntu-tests.patch/tools'
9=== removed file '.pc/fix-ubuntu-tests.patch/tools/test-requires'
10--- .pc/fix-ubuntu-tests.patch/tools/test-requires 2012-09-07 12:23:37 +0000
11+++ .pc/fix-ubuntu-tests.patch/tools/test-requires 1970-01-01 00:00:00 +0000
12@@ -1,11 +0,0 @@
13-distribute>=0.6.24
14-
15-mox
16-nose
17-nose-exclude
18-nosexcover
19-openstack.nose_plugin
20-nosehtmloutput
21-pep8==1.2
22-setuptools-git>=0.4
23-sphinx>=1.1.2
24
25=== modified file 'AUTHORS'
26--- AUTHORS 2012-09-18 19:18:03 +0000
27+++ AUTHORS 2012-11-27 14:34:22 +0000
28@@ -1,13 +1,19 @@
29 Adam Gandelman <adamg@canonical.com>
30+Alessandro Pilotti <ap@pilotti.it>
31+Andre Naehring <naehring@b1-systems.de>
32+Andrew Laski <andrew.laski@rackspace.com>
33 Bhuvan Arumugam <bhuvan@apache.org>
34 Brian Lamar <brian.lamar@rackspace.com>
35 Brian Rosmaita <brian.rosmaita@rackspace.com>
36 Brian Waldon <bcwaldon@gmail.com>
37 Chris Behrens <cbehrens@codestud.com>
38+Christian Berendt <berendt@b1-systems.de>
39 Chuck Short <chuck.short@canonical.com>
40 Clark Boylan <clark.boylan@gmail.com>
41 Dan Prince <dprince@redhat.com>
42 Dean Troyer <dtroyer@gmail.com>
43+Diego Parrilla <diego.parrilla@stackops.com>
44+Doug Hellmann <doug.hellmann@dreamhost.com>
45 Gabriel Hurley <gabriel@strikeawe.com>
46 isethi <iccha.sethi@rackspace.com>
47 James E. Blair <jeblair@hp.com>
48@@ -20,5 +26,8 @@
49 Monty Taylor <mordred@inaugust.com>
50 Sascha Peilicke <saschpe@suse.de>
51 Stuart McLaren <stuart.mclaren@hp.com>
52+Sulochan Acharya <sulochan@gmail.com>
53 Thierry Carrez <thierry@openstack.org>
54-Unmesh Gurjar <unmesh.gurjar@vertex.co.in>
55\ No newline at end of file
56+Unmesh Gurjar <unmesh.gurjar@vertex.co.in>
57+Vincent Untz <vuntz@suse.com>
58+Vishvananda Ishaya <vishvananda@gmail.com>
59\ No newline at end of file
60
61=== modified file 'ChangeLog'
62--- ChangeLog 2012-09-18 19:18:03 +0000
63+++ ChangeLog 2012-11-27 14:34:22 +0000
64@@ -1,3 +1,466 @@
65+commit ae16750c48bc8d4b2b8d4de87913572a276f4f1e
66+Author: Brian Waldon <bcwaldon@gmail.com>
67+Date: Mon Nov 19 14:56:38 2012 -0800
68+
69+ Document bugs/features for v0.6.0
70+
71+ Change-Id: I8966d74eb86e52a222ddac5bc6d52d1dd699fb3d
72+
73+ doc/source/index.rst | 15 +++++++++++++++
74+ 1 file changed, 15 insertions(+)
75+
76+commit 046d34c89185c31b79f580dd2cafa9f790920426
77+Merge: e40580b fe17d35
78+Author: Jenkins <jenkins@review.openstack.org>
79+Date: Mon Nov 19 19:39:19 2012 +0000
80+
81+ Merge "Simplify human-readable size output"
82+
83+commit e40580b77a513cdfb89da15852ea73de0d9a6eea
84+Merge: 882c13a b24832c
85+Author: Jenkins <jenkins@review.openstack.org>
86+Date: Mon Nov 19 18:56:52 2012 +0000
87+
88+ Merge "Make image sizes more readable for humans"
89+
90+commit 882c13ab8cb4bd57a61ae3918f4c3ab1e3185a5d
91+Merge: 0192e14 0e90f8e
92+Author: Jenkins <jenkins@review.openstack.org>
93+Date: Mon Nov 19 18:52:55 2012 +0000
94+
95+ Merge "Set useful boolean flag metavars"
96+
97+commit 0192e14d5652500185aa9410fe6e39cd25c1ec6b
98+Merge: 1899ac2 e20ff23
99+Author: Jenkins <jenkins@review.openstack.org>
100+Date: Mon Nov 19 18:48:11 2012 +0000
101+
102+ Merge "added --version as new parameter"
103+
104+commit fe17d3517482525527d4af9b24df64e7651ebe3a
105+Author: Brian Waldon <bcwaldon@gmail.com>
106+Date: Mon Nov 19 10:35:04 2012 -0800
107+
108+ Simplify human-readable size output
109+
110+ * Limit human-readable sizes to a single decimal
111+ * Drop trailing zero
112+ * Step one suffix further in the case of a size being 1024
113+
114+ Change-Id: I2eb8ac0571d3d08b52f62155912863870573a37c
115+
116+ glanceclient/common/utils.py | 7 +++++--
117+ tests/test_utils.py | 8 +++++---
118+ 2 files changed, 10 insertions(+), 5 deletions(-)
119+
120+commit b24832c22aa44d2f8b5ecddaf12e7878653af28f
121+Author: Christian Berendt <berendt@b1-systems.de>
122+Date: Wed Nov 7 19:39:43 2012 +0100
123+
124+ Make image sizes more readable for humans
125+
126+ By introducing the parameter --human-readable for several functions
127+ (image-list, image-show, image-update, image-create) it's possible
128+ to convert the size in bytes to something more readable like
129+ 9.309MB or 1.375GB.
130+
131+ Change-Id: I4e2654994361dcf330ed6d681dbed73388f159cb
132+
133+ glanceclient/common/utils.py | 12 ++++++++++++
134+ glanceclient/v1/shell.py | 26 ++++++++++++++++++++++----
135+ tests/test_utils.py | 5 +++++
136+ 3 files changed, 39 insertions(+), 4 deletions(-)
137+
138+commit 0e90f8ef230eebd421175d637d7c9df7d149a155
139+Author: Brian Waldon <bcwaldon@gmail.com>
140+Date: Mon Nov 19 09:08:39 2012 -0800
141+
142+ Set useful boolean flag metavars
143+
144+ The boolean flags --is-protected and --is-public now
145+ communicate that they must be set to True or False.
146+
147+ Fixes bug 1056501.
148+
149+ Change-Id: I23094ea556eb71d6eb977a64c171119738ed792b
150+
151+ glanceclient/v1/shell.py | 8 ++++----
152+ 1 file changed, 4 insertions(+), 4 deletions(-)
153+
154+commit 1899ac29637923ad8ff18f863a9d7081a34593d0
155+Merge: c6b9712 8d81623
156+Author: Jenkins <jenkins@review.openstack.org>
157+Date: Sat Nov 17 00:57:15 2012 +0000
158+
159+ Merge "Unpin keystoneclient dependency"
160+
161+commit 8d81623d5f1891a19f58d66cc28f7d4033293354
162+Author: Vishvananda Ishaya <vishvananda@gmail.com>
163+Date: Fri Nov 16 16:29:43 2012 -0800
164+
165+ Unpin keystoneclient dependency
166+
167+ The new 0.2.0 keystoneclient was released with no api changes, and
168+ glanceclients dependency breaks gating so unpin it.
169+
170+ Change-Id: I9cbe2ebb462005ebfea3b7a0e68ca39069a0765f
171+
172+ tools/pip-requires | 2 +-
173+ 1 file changed, 1 insertion(+), 1 deletion(-)
174+
175+commit c6b9712482389a36102141aecb36a0199291092b
176+Merge: 00eff28 6c201e6
177+Author: Jenkins <jenkins@review.openstack.org>
178+Date: Thu Nov 15 21:18:44 2012 +0000
179+
180+ Merge "Fixes bug on Windows related to a wrong API url"
181+
182+commit 6c201e63ea76032cbdd65211382b6266e6a767de
183+Author: Alessandro Pilotti <ap@pilotti.it>
184+Date: Thu Nov 15 20:25:26 2012 +0200
185+
186+ Fixes bug on Windows related to a wrong API url
187+
188+ Fixes Bug #1079323
189+
190+ python-glanceclient (latest repository code) fails on Windows due to a
191+ malformed API url. This error is due to the usage of os.path.normpath(),
192+ which should not be used for URLs as it swaps "/" with "\" on Windows.
193+
194+ The fix consists in using posixpath.normpath().
195+ Please see also https://bugs.launchpad.net/nova/+bug/1077125 and related
196+ commit.
197+
198+ Change-Id: Iaa643bd579963ad9ffbf10674973cbca75d435ac
199+
200+ glanceclient/common/http.py | 4 ++--
201+ 1 file changed, 2 insertions(+), 2 deletions(-)
202+
203+commit 00eff28f28f57fa3f786629dbf20c19b558188ef
204+Author: Andre Naehring <naehring@b1-systems.de>
205+Date: Wed Nov 14 15:37:31 2012 +0100
206+
207+ Enhance --checksum help with algorithm
208+
209+ Fixes bug 1056499.
210+
211+ Added a line to the help text of --checksum which enhances the help text
212+ to show what checksum algorithm is expected.
213+
214+ Change-Id: Ie6604022dd9f398c639afe647b2d94b5179dbb61
215+
216+ glanceclient/v1/shell.py | 3 ++-
217+ 1 file changed, 2 insertions(+), 1 deletion(-)
218+
219+commit e20ff231587e9d3985602cf8df755e3f24459cda
220+Author: Christian Berendt <berendt@b1-systems.de>
221+Date: Tue Nov 13 11:59:17 2012 +0100
222+
223+ added --version as new parameter
224+
225+ fixes bug 1056504
226+ Change-Id: Ib28e3941006b46553001d7895d5ddf4b0f9c540d
227+
228+ glanceclient/__init__.py | 21 +++++++++++++++++++--
229+ glanceclient/shell.py | 4 ++++
230+ glanceclient/version.py | 21 ---------------------
231+ setup.py | 1 +
232+ 4 files changed, 24 insertions(+), 23 deletions(-)
233+
234+commit 16aafa728e4b8309b16bcc120b10bc20372883f4
235+Author: Alessandro Pilotti <ap@pilotti.it>
236+Date: Mon Nov 5 18:19:13 2012 +0200
237+
238+ Fixes setup compatibility issue on Windows
239+
240+ Fixes Bug #1052161
241+
242+ "python setup.py build" fails on Windows due to a hardcoded shell path:
243+ /bin/sh
244+
245+ setup.py updated using openstack-common/update.py
246+
247+ Change-Id: If0ae835aeada8769e46dddf4f3c2f2edfdfbc5fe
248+
249+ glanceclient/openstack/common/setup.py | 105 +++++++++++++++++-------------
250+ glanceclient/openstack/common/version.py | 3 +-
251+ 2 files changed, 61 insertions(+), 47 deletions(-)
252+
253+commit a8e88aa340c2d1ad9d937e0f5c2c60c65d7e5962
254+Merge: 256dcba 8b2c227
255+Author: Jenkins <jenkins@review.openstack.org>
256+Date: Thu Oct 25 02:23:49 2012 +0000
257+
258+ Merge "Allow deletion of multiple images through CLI"
259+
260+commit 8b2c227f27be649a4a3e371ad99157ee464ecc1d
261+Author: Sulochan Acharya <sulochan@gmail.com>
262+Date: Mon Oct 22 17:01:46 2012 -0500
263+
264+ Allow deletion of multiple images through CLI
265+
266+ Add nargs to argparse for image-delete command to
267+ allow muliple (optional) positional image-id arguments.
268+ For example:
269+ image-delete xxx aaa yyy will delete valid images
270+ xxx and yyy and print error message for invalid image
271+ aaa. Also with --verbose you can see some extra text
272+ on delete request for each image.
273+
274+ Fixes bug1056498.
275+
276+ Change-Id: I6e804700ed24d16f90ec92569c0893cad4aaa26f
277+
278+ glanceclient/v1/shell.py | 21 ++++++++++++++++++---
279+ 1 file changed, 18 insertions(+), 3 deletions(-)
280+
281+commit 256dcbae1c8565284cb54f5c5693b9cfd7103425
282+Merge: c420fa1 1e14e82
283+Author: Jenkins <jenkins@review.openstack.org>
284+Date: Wed Oct 24 02:53:46 2012 +0000
285+
286+ Merge "Fixes shell command for member-delete"
287+
288+commit 1e14e82c815b06dfd8a370f1f97c61a256b28a9a
289+Author: Sulochan Acharya <sulochan@gmail.com>
290+Date: Mon Oct 22 15:46:55 2012 -0500
291+
292+ Fixes shell command for member-delete
293+
294+ Fixes the member-delete cli command and string formatting for
295+ dry-run option.
296+ Fixes bug1064320
297+
298+ Change-Id: I338f03d53da5c9b7656ae4d1335de9623b774dd8
299+
300+ glanceclient/v1/shell.py | 10 +++++-----
301+ 1 file changed, 5 insertions(+), 5 deletions(-)
302+
303+commit c420fa10fe25fd671b2ca48dd86d80b499856ac6
304+Author: Doug Hellmann <doug.hellmann@dreamhost.com>
305+Date: Mon Oct 22 18:43:53 2012 -0400
306+
307+ Add OpenStack trove classifier for PyPI
308+
309+ Add trove classifier to have the client listed among the
310+ other OpenStack-related projets on PyPI.
311+
312+ Change-Id: I2bb290f529fd2cd08d0093f495074d8e1683d91f
313+ Signed-off-by: Doug Hellmann <doug.hellmann@dreamhost.com>
314+
315+ setup.py | 1 +
316+ 1 file changed, 1 insertion(+)
317+
318+commit 9004ee40df67705d6e0f07df65763e7ae2c44b13
319+Merge: 3576336 b5d46e2
320+Author: Jenkins <jenkins@review.openstack.org>
321+Date: Sat Oct 13 02:54:09 2012 +0000
322+
323+ Merge "Display acceptable disk/container formats in help text"
324+
325+commit 3576336cb993d88b32638ed1f2bcab5dc31653fe
326+Merge: 556082c 727aadb
327+Author: Jenkins <jenkins@review.openstack.org>
328+Date: Sat Oct 13 02:17:31 2012 +0000
329+
330+ Merge "Handle create/update of images with unknown size"
331+
332+commit 556082cd6632dbce52ccb67ace57410d61057d66
333+Author: Stuart McLaren <stuart.mclaren@hp.com>
334+Date: Fri Sep 21 14:18:22 2012 +0000
335+
336+ Implement blueprint ssl-connect-rework
337+
338+ Use pyOpenSSL for HTTPS connections.
339+
340+ This allows:
341+
342+ * Neater loading of system CA files
343+ * Optional disabling of SSL compression
344+
345+ The performance gain from disabling SSL compression is significant
346+ in cases where the image being uploaded/downloaded is in an already
347+ compressed format (eg qcow2).
348+
349+ Related to bp ssl-connect-rework.
350+
351+ Change-Id: I0568b6c95c5fc7b8eafdbd0284e24c453660a55a
352+
353+ glanceclient/common/http.py | 135 ++++++++++++++++++++++++++++---------------
354+ glanceclient/exc.py | 4 ++
355+ glanceclient/shell.py | 6 ++
356+ tests/test_ssl.py | 112 +++++++++++++++++++++++++++++++++++
357+ tests/var/ca.crt | 35 +++++++++++
358+ tests/var/certificate.crt | 30 ++++++++++
359+ tests/var/privatekey.key | 51 ++++++++++++++++
360+ tools/pip-requires | 1 +
361+ 8 files changed, 326 insertions(+), 48 deletions(-)
362+
363+commit 727aadbc257ec3c99dd1621202948d288d45c8cc
364+Author: Stuart McLaren <stuart.mclaren@hp.com>
365+Date: Wed Sep 26 12:56:51 2012 +0000
366+
367+ Handle create/update of images with unknown size
368+
369+ It may not be possible to know in advance the total
370+ size of image data which is to be uploaded, for example
371+ if the data is being piped to stdin.
372+
373+ To handle this we use HTTP Transfer-Encoding: chunked
374+ and do not set any image size headers.
375+
376+ Various subtly different cases needed to be handled for
377+ both image-create and image-update, including:
378+
379+ * input from named pipe
380+ * piped input of zero size
381+ * regular file of zero length
382+
383+ Fix for bug 1056220.
384+
385+ Change-Id: I0c7f0a64d883e058993b954a1c465c5b057f2bcf
386+
387+ glanceclient/common/http.py | 20 +++++++++++++++++++-
388+ glanceclient/v1/images.py | 20 ++++++++++----------
389+ glanceclient/v1/shell.py | 17 ++++++++++++++---
390+ tests/v1/test_images.py | 2 +-
391+ 4 files changed, 44 insertions(+), 15 deletions(-)
392+
393+commit b5d46e2e0d1e5df7f953787987b88880eb844b9d
394+Author: Brian Waldon <bcwaldon@gmail.com>
395+Date: Wed Oct 3 13:52:55 2012 -0700
396+
397+ Display acceptable disk/container formats in help text
398+
399+ Fixes bug #1056497
400+
401+ This patch provides more information in the help text. Originally the text
402+ provided the trivial definitions of the arguments disk_format and
403+ container_format. This patch updates the text to display the acceptable
404+ formats.
405+
406+ Change-Id: I893b52c9f72a34c75e8bea522820863592300302
407+
408+ glanceclient/v1/shell.py | 18 ++++++++++++------
409+ 1 file changed, 12 insertions(+), 6 deletions(-)
410+
411+commit cdc06d9fdb15cd19bd5d26304dfebf092c6c8df8
412+Author: Brian Waldon <bcwaldon@gmail.com>
413+Date: Wed Oct 3 13:52:55 2012 -0700
414+
415+ Simplify http(s) connection instantiation
416+
417+ The endpoint parsing and connection instantiation code was too
418+ complicated and easily broken. This assigns human-readable names to
419+ instance variables and breaks up the parsing into more understandable
420+ chunks.
421+
422+ Fixes bug 1060316.
423+
424+ Change-Id: I5c5236f90d88b9e797cf0a476aabe8cc7cfa1cc9
425+
426+ glanceclient/common/http.py | 48 ++++++++++++++++++++++++++-----------------
427+ 1 file changed, 29 insertions(+), 19 deletions(-)
428+
429+commit 11e6aadf190122d6a4776fc00e660b71a89dffb3
430+Author: Brian Waldon <bcwaldon@gmail.com>
431+Date: Tue Sep 11 16:22:56 2012 -0700
432+
433+ Add happy path tests for ResponseBodyIterator
434+
435+ Change-Id: I5e971b57a0591752e7fca76d0df78ce139308db5
436+
437+ tests/test_http.py | 10 ++++++++++
438+ tests/utils.py | 10 +++++++++-
439+ 2 files changed, 19 insertions(+), 1 deletion(-)
440+
441+commit ff3060c067bb2a860642a1c2e01ef151df8c5243
442+Author: Diego Parrilla <diego.parrilla@stackops.com>
443+Date: Fri Sep 21 00:51:20 2012 +0200
444+
445+ Use full URI path from Glance endpoint in HTTP requests
446+
447+ Fixes bug 1052846
448+
449+ Now the connection uses host, port and path to connect to Glance. So proxied connections to Glance are allowed.
450+
451+ Change-Id: I53a890e6532adb8168961d1d09f938bf439e895c
452+
453+ glanceclient/common/http.py | 6 ++++--
454+ 1 file changed, 4 insertions(+), 2 deletions(-)
455+
456+commit e140dbb0c779de74e4ae063971660ac2d234365a
457+Merge: cdc94af 91896ff
458+Author: Jenkins <jenkins@review.openstack.org>
459+Date: Tue Sep 18 20:44:58 2012 +0000
460+
461+ Merge "Fixes glance add / update / image-create / image-update on Windows"
462+
463+commit cdc94af297fe56341dfe0484d62f2e69d9aa5e9b
464+Author: Stuart McLaren <stuart.mclaren@hp.com>
465+Date: Mon Sep 17 13:39:33 2012 +0000
466+
467+ Typo in image-create help page
468+
469+ The image-create help page reversed the DISK_FORMAT
470+ and CONTAINER_FORMAT metavars.
471+
472+ Fixes bug 1051968.
473+
474+ Change-Id: I385cb0912ad87a62fd10742b5da23a5ea8bc9bb8
475+
476+ glanceclient/v1/shell.py | 4 ++--
477+ 1 file changed, 2 insertions(+), 2 deletions(-)
478+
479+commit 91896ff51861e8d90bdf0f7c54cab0f2b3e3c277
480+Author: Alessandro Pilotti <ap@pilotti.it>
481+Date: Thu Sep 13 14:02:20 2012 +0300
482+
483+ Fixes glance add / update / image-create / image-update on Windows
484+
485+ Fixes Bug #1050345
486+
487+ The image upload hangs if the file contains a byte with value 0x1A (EOF), due to
488+ the fact that the file or stdin streams are treated as text and not
489+ binary streams. This fix sets the proper binary mode.
490+
491+ Change-Id: I3425cb9729a8da4d1b73fbfba06fd6f2c7e8833e
492+
493+ glanceclient/v1/shell.py | 28 ++++++++++++++++++----------
494+ 1 file changed, 18 insertions(+), 10 deletions(-)
495+
496+commit 902bff79bbe52e831da947bb5ac5fce2330d810e
497+Author: Vincent Untz <vuntz@suse.com>
498+Date: Thu Sep 13 11:12:00 2012 +0200
499+
500+ Fix weird "None" displayed on some errors
501+
502+ logging.exception() should only be called from an exception handler,
503+ which is not the case here.
504+
505+ Part of bug 1050260.
506+
507+ Change-Id: I591a68c458cd733c04cea7d2d640afdbb7dd19f6
508+
509+ glanceclient/common/http.py | 2 +-
510+ 1 file changed, 1 insertion(+), 1 deletion(-)
511+
512+commit 8cee48b1ddf480d182bbc33ec684dcfd195b038c
513+Author: Andrew Laski <andrew.laski@rackspace.com>
514+Date: Wed Sep 12 09:40:04 2012 -0400
515+
516+ Make ConnectionRefused error more informative.
517+
518+ When the server refuses the connection the error message displayed now
519+ lists the endpoint that refused the connection.
520+
521+ Fixes: bug 1043067
522+ Change-Id: I62797106732bbb6eec8c99e491fd38850ad58ff8
523+
524+ glanceclient/common/http.py | 3 ++-
525+ tests/test_http.py | 55 +++++++++++++++++++++++++++++++++++++++++++
526+ 2 files changed, 57 insertions(+), 1 deletion(-)
527+
528 commit 3f67c461da236bf603cf4812f81f51200573f51f
529 Author: Brian Waldon <bcwaldon@gmail.com>
530 Date: Mon Sep 10 18:25:20 2012 -0700
531
532=== modified file 'PKG-INFO'
533--- PKG-INFO 2012-09-18 19:18:03 +0000
534+++ PKG-INFO 2012-11-27 14:34:22 +0000
535@@ -1,6 +1,6 @@
536 Metadata-Version: 1.1
537 Name: python-glanceclient
538-Version: 0.5.1
539+Version: 0.6.0
540 Summary: Client library for OpenStack Image API
541 Home-page: https://github.com/openstack/python-glanceclient
542 Author: OpenStack Glance Contributors
543@@ -18,6 +18,7 @@
544 Platform: UNKNOWN
545 Classifier: Development Status :: 4 - Beta
546 Classifier: Environment :: Console
547+Classifier: Environment :: OpenStack
548 Classifier: Intended Audience :: Developers
549 Classifier: Intended Audience :: Information Technology
550 Classifier: License :: OSI Approved :: Apache Software License
551
552=== modified file 'debian/changelog'
553--- debian/changelog 2012-10-19 23:15:22 +0000
554+++ debian/changelog 2012-11-27 14:34:22 +0000
555@@ -1,3 +1,20 @@
556+python-glanceclient (1:0.6.0-0ubuntu1~cloud0) precise-grizzly; urgency=low
557+
558+ * New upstream release for the Ubuntu Cloud Archive.
559+
560+ -- Chuck Short <zulcss@ubuntu.com> Tue, 27 Nov 2012 08:30:11 -0600
561+
562+python-glanceclient (1:0.6.0-0ubuntu1) raring; urgency=low
563+
564+ [ Adam Gandelman ]
565+ * Ensure python-prettytable >= 0.6. (LP: #1073275)
566+ * debian/control, pydist-overrides: Add python-openssl override.
567+
568+ [ Chuck Short ]
569+ * New usptream release.
570+
571+ -- Chuck Short <zulcss@ubuntu.com> Fri, 23 Nov 2012 10:22:06 -0600
572+
573 python-glanceclient (1:0.5.1-0ubuntu1~cloud0) precise-folsom; urgency=low
574
575 * New release candidate for the Ubuntu Cloud Archive.
576
577=== modified file 'debian/control'
578--- debian/control 2012-08-16 12:24:18 +0000
579+++ debian/control 2012-11-27 14:34:22 +0000
580@@ -9,7 +9,8 @@
581 python-keystoneclient,
582 python-mox,
583 python-nose,
584- python-prettytable,
585+ python-openssl,
586+ python-prettytable ( >= 0.6 ),
587 python-setuptools,
588 python-setuptools-git,
589 python-sphinx,
590@@ -21,7 +22,8 @@
591 Architecture: all
592 Depends: python-httplib2,
593 python-keystoneclient,
594- python-prettytable,
595+ python-openssl,
596+ python-prettytable ( >= 0.6 ),
597 python-setuptools,
598 python-keystoneclient,
599 python-warlock,
600
601=== modified file 'debian/patches/fix-ubuntu-tests.patch'
602--- debian/patches/fix-ubuntu-tests.patch 2012-09-07 12:23:37 +0000
603+++ debian/patches/fix-ubuntu-tests.patch 2012-11-27 14:34:22 +0000
604@@ -1,16 +1,11 @@
605-Index: python-glanceclient-0.5.0.1.3e6c0b6/tools/test-requires
606-===================================================================
607---- python-glanceclient-0.5.0.1.3e6c0b6.orig/tools/test-requires 2012-08-22 23:39:33.000000000 -0700
608-+++ python-glanceclient-0.5.0.1.3e6c0b6/tools/test-requires 2012-08-22 23:42:41.298507894 -0700
609-@@ -1,11 +1,5 @@
610--distribute>=0.6.24
611--
612- mox
613- nose
614--nose-exclude
615--nosexcover
616--openstack.nose_plugin
617--nosehtmloutput
618- pep8==1.2
619+diff -Naupr python-glanceclient-0.6.0.orig/tools/test-requires python-glanceclient-0.6.0/tools/test-requires
620+--- python-glanceclient-0.6.0.orig/tools/test-requires 2012-11-19 23:42:45.000000000 -0600
621++++ python-glanceclient-0.6.0/tools/test-requires 2012-11-23 10:16:05.853179881 -0600
622+@@ -6,6 +6,6 @@ nose-exclude
623+ nosexcover
624+ openstack.nose_plugin
625+ nosehtmloutput
626+-pep8==1.2
627++pep8==1.3.3
628 setuptools-git>=0.4
629 sphinx>=1.1.2
630
631=== modified file 'debian/pydist-overrides'
632--- debian/pydist-overrides 2012-08-16 12:24:18 +0000
633+++ debian/pydist-overrides 2012-11-27 14:34:22 +0000
634@@ -2,3 +2,4 @@
635 python-keystoneclient
636 setuptools-git
637 warlock
638+pyOpenSSL
639
640=== modified file 'debian/rules'
641--- debian/rules 2012-07-06 11:31:31 +0000
642+++ debian/rules 2012-11-27 14:34:22 +0000
643@@ -7,7 +7,7 @@
644 dh $@ --with python2
645
646 override_dh_auto_test:
647- set -e ; for pyversion in $(shell pyversions -r); do $$pyversion setup.py test; done
648+# set -e ; for pyversion in $(shell pyversions -r); do $$pyversion setup.py test; done
649
650 get-orig-source:
651 uscan --verbose --force-download --rename --destdir=../build-area
652
653=== modified file 'doc/source/index.rst'
654--- doc/source/index.rst 2012-09-18 19:18:03 +0000
655+++ doc/source/index.rst 2012-11-27 14:34:22 +0000
656@@ -36,6 +36,21 @@
657 Release Notes
658 =============
659
660+0.6.0
661+-----
662+
663+* Multiple image ID can be passed to ``glance image-delete``
664+* ``glance --version`` and glanceclient.__version__ expose the current library version
665+* Use ``--human-readable`` with ``image-list`` and ``image-show`` to display image sizes in human-friendly formats
666+* Use OpenSSL for HTTPS connections
667+* 1056220_: Always use 'Transfer-Encoding: chunked' when transferring image data
668+* 1052846_: Padded endpoints enabled (e.g. glance.example.com/padding/v1)
669+* 1050345_: ``glance image-create`` and ``glance image-update`` now work on Windows
670+
671+.. _1056220: http://bugs.launchpad.net/python-glanceclient/+bug/1056220
672+.. _1052846: http://bugs.launchpad.net/python-glanceclient/+bug/1052846
673+.. _1050345: http://bugs.launchpad.net/python-glanceclient/+bug/1050345
674+
675 0.5.1
676 ----
677 * 1045824_: Always send Content-Length when updating image with image data
678
679=== modified file 'glanceclient/__init__.py'
680--- glanceclient/__init__.py 2012-08-16 12:24:18 +0000
681+++ glanceclient/__init__.py 2012-11-27 14:34:22 +0000
682@@ -23,6 +23,23 @@
683 import warnings
684 warnings.warn("Could not import glanceclient.client", ImportWarning)
685
686-import glanceclient.version
687-
688-__version__ = glanceclient.version.version_info.deferred_version_string()
689+import os
690+import inspect
691+
692+
693+def _get_client_version():
694+ """Read version from versioninfo file."""
695+ mod_abspath = inspect.getabsfile(inspect.currentframe())
696+ client_path = os.path.dirname(mod_abspath)
697+ version_path = os.path.join(client_path, 'versioninfo')
698+
699+ if os.path.exists(version_path):
700+ version = open(version_path).read().strip()
701+ else:
702+ version = "Unknown, couldn't find versioninfo file at %s"\
703+ % version_path
704+
705+ return version
706+
707+
708+__version__ = _get_client_version()
709
710=== modified file 'glanceclient/common/http.py'
711--- glanceclient/common/http.py 2012-09-18 19:18:03 +0000
712+++ glanceclient/common/http.py 2012-11-27 14:34:22 +0000
713@@ -16,18 +16,13 @@
714 import copy
715 import httplib
716 import logging
717-import os
718+import posixpath
719 import socket
720 import StringIO
721+import struct
722 import urlparse
723
724 try:
725- import ssl
726-except ImportError:
727- #TODO(bcwaldon): Handle this failure more gracefully
728- pass
729-
730-try:
731 import json
732 except ImportError:
733 import simplejson as json
734@@ -37,6 +32,7 @@
735 import cgi
736 urlparse.parse_qsl = cgi.parse_qsl
737
738+import OpenSSL
739
740 from glanceclient import exc
741
742@@ -50,35 +46,47 @@
743
744 def __init__(self, endpoint, **kwargs):
745 self.endpoint = endpoint
746+ endpoint_parts = self.parse_endpoint(self.endpoint)
747+ self.endpoint_scheme = endpoint_parts.scheme
748+ self.endpoint_hostname = endpoint_parts.hostname
749+ self.endpoint_port = endpoint_parts.port
750+ self.endpoint_path = endpoint_parts.path
751+
752+ self.connection_class = self.get_connection_class(self.endpoint_scheme)
753+ self.connection_kwargs = self.get_connection_kwargs(
754+ self.endpoint_scheme, **kwargs)
755+
756 self.auth_token = kwargs.get('token')
757- self.connection_params = self.get_connection_params(endpoint, **kwargs)
758-
759- @staticmethod
760- def get_connection_params(endpoint, **kwargs):
761- parts = urlparse.urlparse(endpoint)
762-
763- _args = (parts.hostname, parts.port)
764+
765+ @staticmethod
766+ def parse_endpoint(endpoint):
767+ return urlparse.urlparse(endpoint)
768+
769+ @staticmethod
770+ def get_connection_class(scheme):
771+ if scheme == 'https':
772+ return VerifiedHTTPSConnection
773+ else:
774+ return httplib.HTTPConnection
775+
776+ @staticmethod
777+ def get_connection_kwargs(scheme, **kwargs):
778 _kwargs = {'timeout': float(kwargs.get('timeout', 600))}
779
780- if parts.scheme == 'https':
781- _class = VerifiedHTTPSConnection
782+ if scheme == 'https':
783 _kwargs['ca_file'] = kwargs.get('ca_file', None)
784 _kwargs['cert_file'] = kwargs.get('cert_file', None)
785 _kwargs['key_file'] = kwargs.get('key_file', None)
786 _kwargs['insecure'] = kwargs.get('insecure', False)
787- elif parts.scheme == 'http':
788- _class = httplib.HTTPConnection
789- else:
790- msg = 'Unsupported scheme: %s' % parts.scheme
791- raise exc.InvalidEndpoint(msg)
792+ _kwargs['ssl_compression'] = kwargs.get('ssl_compression', True)
793
794- return (_class, _args, _kwargs)
795+ return _kwargs
796
797 def get_connection(self):
798- _class = self.connection_params[0]
799+ _class = self.connection_class
800 try:
801- return _class(*self.connection_params[1],
802- **self.connection_params[2])
803+ return _class(self.endpoint_hostname, self.endpoint_port,
804+ **self.connection_kwargs)
805 except httplib.InvalidURL:
806 raise exc.InvalidEndpoint()
807
808@@ -95,11 +103,11 @@
809 ('ca_file', '--cacert %s'),
810 ]
811 for (key, fmt) in conn_params_fmt:
812- value = self.connection_params[2].get(key)
813+ value = self.connection_kwargs.get(key)
814 if value:
815 curl.append(fmt % value)
816
817- if self.connection_params[2].get('insecure'):
818+ if self.connection_kwargs.get('insecure'):
819 curl.append('-k')
820
821 if 'body' in kwargs:
822@@ -134,13 +142,27 @@
823 conn = self.get_connection()
824
825 try:
826- conn.request(method, url, **kwargs)
827+ conn_url = posixpath.normpath('%s/%s' % (self.endpoint_path, url))
828+ if kwargs['headers'].get('Transfer-Encoding') == 'chunked':
829+ conn.putrequest(method, conn_url)
830+ for header, value in kwargs['headers'].items():
831+ conn.putheader(header, value)
832+ conn.endheaders()
833+ chunk = kwargs['body'].read(CHUNKSIZE)
834+ # Chunk it, baby...
835+ while chunk:
836+ conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
837+ chunk = kwargs['body'].read(CHUNKSIZE)
838+ conn.send('0\r\n\r\n')
839+ else:
840+ conn.request(method, conn_url, **kwargs)
841 resp = conn.getresponse()
842 except socket.gaierror as e:
843 message = "Error finding address for %(url)s: %(e)s" % locals()
844 raise exc.InvalidEndpoint(message=message)
845 except (socket.error, socket.timeout) as e:
846- message = "Error communicating with %(url)s: %(e)s" % locals()
847+ endpoint = self.endpoint
848+ message = "Error communicating with %(endpoint)s %(e)s" % locals()
849 raise exc.CommunicationError(message=message)
850
851 body_iter = ResponseBodyIterator(resp)
852@@ -154,7 +176,7 @@
853 self.log_http_response(resp)
854
855 if 400 <= resp.status < 600:
856- LOG.exception("Request returned failure status.")
857+ LOG.error("Request returned failure status.")
858 raise exc.from_response(resp)
859 elif resp.status in (301, 302, 305):
860 # Redirected. Reissue the request to the new location.
861@@ -188,70 +210,118 @@
862 kwargs.setdefault('headers', {})
863 kwargs['headers'].setdefault('Content-Type',
864 'application/octet-stream')
865+ if 'body' in kwargs:
866+ if (hasattr(kwargs['body'], 'read')
867+ and method.lower() in ('post', 'put')):
868+ # We use 'Transfer-Encoding: chunked' because
869+ # body size may not always be known in advance.
870+ kwargs['headers']['Transfer-Encoding'] = 'chunked'
871 return self._http_request(url, method, **kwargs)
872
873
874+class OpenSSLConnectionDelegator(object):
875+ """
876+ An OpenSSL.SSL.Connection delegator.
877+
878+ Supplies an additional 'makefile' method which httplib requires
879+ and is not present in OpenSSL.SSL.Connection.
880+
881+ Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
882+ a delegator must be used.
883+ """
884+ def __init__(self, *args, **kwargs):
885+ self.connection = OpenSSL.SSL.Connection(*args, **kwargs)
886+
887+ def __getattr__(self, name):
888+ return getattr(self.connection, name)
889+
890+ def makefile(self, *args, **kwargs):
891+ return socket._fileobject(self.connection, *args, **kwargs)
892+
893+
894 class VerifiedHTTPSConnection(httplib.HTTPSConnection):
895- """httplib-compatibile connection using client-side SSL authentication
896-
897- :see http://code.activestate.com/recipes/
898- 577548-https-httplib-client-connection-with-certificate-v/
899- """
900-
901+ """
902+ Extended HTTPSConnection which uses the OpenSSL library
903+ for enhanced SSL support.
904+ """
905 def __init__(self, host, port, key_file=None, cert_file=None,
906- ca_file=None, timeout=None, insecure=False):
907- httplib.HTTPSConnection.__init__(self, host, port, key_file=key_file,
908+ ca_file=None, timeout=None, insecure=False,
909+ ssl_compression=True):
910+ httplib.HTTPSConnection.__init__(self, host, port,
911+ key_file=key_file,
912 cert_file=cert_file)
913 self.key_file = key_file
914 self.cert_file = cert_file
915- if ca_file is not None:
916- self.ca_file = ca_file
917- else:
918- self.ca_file = self.get_system_ca_file()
919 self.timeout = timeout
920 self.insecure = insecure
921+ self.ssl_compression = ssl_compression
922+ self.ca_file = ca_file
923+ self.setcontext()
924+
925+ @staticmethod
926+ def verify_callback(connection, x509, errnum, errdepth, preverify_ok):
927+ # Pass through OpenSSL's default result
928+ return preverify_ok
929+
930+ def setcontext(self):
931+ """
932+ Set up the OpenSSL context.
933+ """
934+ self.context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
935+
936+ if self.ssl_compression is False:
937+ self.context.set_options(0x20000) # SSL_OP_NO_COMPRESSION
938+
939+ if self.insecure is not True:
940+ self.context.set_verify(OpenSSL.SSL.VERIFY_PEER,
941+ self.verify_callback)
942+ else:
943+ self.context.set_verify(OpenSSL.SSL.VERIFY_NONE,
944+ self.verify_callback)
945+
946+ if self.cert_file:
947+ try:
948+ self.context.use_certificate_file(self.cert_file)
949+ except Exception, e:
950+ msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e)
951+ raise exc.SSLConfigurationError(msg)
952+ if self.key_file is None:
953+ # We support having key and cert in same file
954+ try:
955+ self.context.use_privatekey_file(self.cert_file)
956+ except Exception, e:
957+ msg = ('No key file specified and unable to load key '
958+ 'from "%s" %s' % (self.cert_file, e))
959+ raise exc.SSLConfigurationError(msg)
960+
961+ if self.key_file:
962+ try:
963+ self.context.use_privatekey_file(self.key_file)
964+ except Exception, e:
965+ msg = 'Unable to load key from "%s" %s' % (self.key_file, e)
966+ raise exc.SSLConfigurationError(msg)
967+
968+ if self.ca_file:
969+ try:
970+ self.context.load_verify_locations(self.ca_file)
971+ except Exception, e:
972+ msg = 'Unable to load CA from "%s"' % (self.ca_file, e)
973+ raise exc.SSLConfigurationError(msg)
974+ else:
975+ self.context.set_default_verify_paths()
976
977 def connect(self):
978 """
979- Connect to a host on a given (SSL) port.
980- If ca_file is pointing somewhere, use it to check Server Certificate.
981-
982- Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
983- This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to
984- ssl.wrap_socket(), which forces SSL to check server certificate against
985- our client certificate.
986+ Connect to an SSL port using the OpenSSL library and apply
987+ per-connection parameters.
988 """
989- sock = socket.create_connection((self.host, self.port), self.timeout)
990-
991- if self._tunnel_host:
992- self.sock = sock
993- self._tunnel()
994-
995- if self.insecure is True:
996- kwargs = {'cert_reqs': ssl.CERT_NONE}
997- else:
998- kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.ca_file}
999-
1000- if self.cert_file:
1001- kwargs['certfile'] = self.cert_file
1002- if self.key_file:
1003- kwargs['keyfile'] = self.key_file
1004-
1005- self.sock = ssl.wrap_socket(sock, **kwargs)
1006-
1007- @staticmethod
1008- def get_system_ca_file():
1009- """"Return path to system default CA file"""
1010- # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora,
1011- # Suse, FreeBSD/OpenBSD
1012- ca_path = ['/etc/ssl/certs/ca-certificates.crt',
1013- '/etc/pki/tls/certs/ca-bundle.crt',
1014- '/etc/ssl/ca-bundle.pem',
1015- '/etc/ssl/cert.pem']
1016- for ca in ca_path:
1017- if os.path.exists(ca):
1018- return ca
1019- return None
1020+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1021+ if self.timeout is not None:
1022+ # '0' microseconds
1023+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
1024+ struct.pack('LL', self.timeout, 0))
1025+ self.sock = OpenSSLConnectionDelegator(self.context, sock)
1026+ self.sock.connect((self.host, self.port))
1027
1028
1029 class ResponseBodyIterator(object):
1030
1031=== modified file 'glanceclient/common/utils.py'
1032--- glanceclient/common/utils.py 2012-08-16 12:24:18 +0000
1033+++ glanceclient/common/utils.py 2012-11-27 14:34:22 +0000
1034@@ -168,3 +168,18 @@
1035 raise IOError(errno.EPIPE,
1036 'Corrupt image download. Checksum was %s expected %s' %
1037 (md5sum, checksum))
1038+
1039+
1040+def make_size_human_readable(size):
1041+ suffix = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB']
1042+ base = 1024.0
1043+
1044+ index = 0
1045+ while size >= base:
1046+ index = index + 1
1047+ size = size / base
1048+
1049+ padded = '%.1f' % size
1050+ stripped = padded.rstrip('0').rstrip('.')
1051+
1052+ return '%s%s' % (stripped, suffix[index])
1053
1054=== modified file 'glanceclient/exc.py'
1055--- glanceclient/exc.py 2012-09-18 19:18:03 +0000
1056+++ glanceclient/exc.py 2012-11-27 14:34:22 +0000
1057@@ -164,3 +164,7 @@
1058 class EndpointNotFound(Exception):
1059 """DEPRECATED"""
1060 pass
1061+
1062+
1063+class SSLConfigurationError(BaseException):
1064+ pass
1065
1066=== modified file 'glanceclient/openstack/common/setup.py'
1067--- glanceclient/openstack/common/setup.py 2012-08-16 12:24:18 +0000
1068+++ glanceclient/openstack/common/setup.py 2012-11-27 14:34:22 +0000
1069@@ -31,13 +31,13 @@
1070 def parse_mailmap(mailmap='.mailmap'):
1071 mapping = {}
1072 if os.path.exists(mailmap):
1073- fp = open(mailmap, 'r')
1074- for l in fp:
1075- l = l.strip()
1076- if not l.startswith('#') and ' ' in l:
1077- canonical_email, alias = [x for x in l.split(' ')
1078- if x.startswith('<')]
1079- mapping[alias] = canonical_email
1080+ with open(mailmap, 'r') as fp:
1081+ for l in fp:
1082+ l = l.strip()
1083+ if not l.startswith('#') and ' ' in l:
1084+ canonical_email, alias = [x for x in l.split(' ')
1085+ if x.startswith('<')]
1086+ mapping[alias] = canonical_email
1087 return mapping
1088
1089
1090@@ -52,10 +52,10 @@
1091
1092 # Get requirements from the first file that exists
1093 def get_reqs_from_files(requirements_files):
1094- reqs_in = []
1095 for requirements_file in requirements_files:
1096 if os.path.exists(requirements_file):
1097- return open(requirements_file, 'r').read().split('\n')
1098+ with open(requirements_file, 'r') as fil:
1099+ return fil.read().split('\n')
1100 return []
1101
1102
1103@@ -117,8 +117,12 @@
1104
1105
1106 def _run_shell_command(cmd):
1107- output = subprocess.Popen(["/bin/sh", "-c", cmd],
1108- stdout=subprocess.PIPE)
1109+ if os.name == 'nt':
1110+ output = subprocess.Popen(["cmd.exe", "/C", cmd],
1111+ stdout=subprocess.PIPE)
1112+ else:
1113+ output = subprocess.Popen(["/bin/sh", "-c", cmd],
1114+ stdout=subprocess.PIPE)
1115 out = output.communicate()
1116 if len(out) == 0:
1117 return None
1118@@ -127,13 +131,6 @@
1119 return out[0].strip()
1120
1121
1122-def _get_git_branch_name():
1123- branch_ref = _run_shell_command("git symbolic-ref -q HEAD")
1124- if branch_ref is None:
1125- return "HEAD"
1126- return branch_ref[len("refs/heads/"):]
1127-
1128-
1129 def _get_git_next_version_suffix(branch_name):
1130 datestamp = datetime.datetime.now().strftime('%Y%m%d')
1131 if branch_name == 'milestone-proposed':
1132@@ -143,16 +140,18 @@
1133 _run_shell_command("git fetch origin +refs/meta/*:refs/remotes/meta/*")
1134 milestone_cmd = "git show meta/openstack/release:%s" % branch_name
1135 milestonever = _run_shell_command(milestone_cmd)
1136- if not milestonever:
1137- milestonever = branch_name
1138+ if milestonever:
1139+ first_half = "%s~%s" % (milestonever, datestamp)
1140+ else:
1141+ first_half = datestamp
1142+
1143 post_version = _get_git_post_version()
1144 # post version should look like:
1145- # 0.1.1.4.cc9e28a
1146+ # 0.1.1.4.gcc9e28a
1147 # where the bit after the last . is the short sha, and the bit between
1148 # the last and second to last is the revno count
1149 (revno, sha) = post_version.split(".")[-2:]
1150- first_half = "%(milestonever)s~%(datestamp)s" % locals()
1151- second_half = "%(revno_prefix)s%(revno)s.%(sha)s" % locals()
1152+ second_half = "%s%s.%s" % (revno_prefix, revno, sha)
1153 return ".".join((first_half, second_half))
1154
1155
1156@@ -180,37 +179,43 @@
1157 tag_infos = tag_info.split("-")
1158 base_version = "-".join(tag_infos[:-2])
1159 (revno, sha) = tag_infos[-2:]
1160- # git describe prefixes the sha with a g
1161- sha = sha[1:]
1162 return "%s.%s.%s" % (base_version, revno, sha)
1163
1164
1165 def write_git_changelog():
1166 """Write a changelog based on the git changelog."""
1167- if os.path.isdir('.git'):
1168- git_log_cmd = 'git log --stat'
1169- changelog = _run_shell_command(git_log_cmd)
1170- mailmap = parse_mailmap()
1171- with open("ChangeLog", "w") as changelog_file:
1172- changelog_file.write(canonicalize_emails(changelog, mailmap))
1173+ new_changelog = 'ChangeLog'
1174+ if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'):
1175+ if os.path.isdir('.git'):
1176+ git_log_cmd = 'git log --stat'
1177+ changelog = _run_shell_command(git_log_cmd)
1178+ mailmap = parse_mailmap()
1179+ with open(new_changelog, "w") as changelog_file:
1180+ changelog_file.write(canonicalize_emails(changelog, mailmap))
1181+ else:
1182+ open(new_changelog, 'w').close()
1183
1184
1185 def generate_authors():
1186 """Create AUTHORS file using git commits."""
1187- jenkins_email = 'jenkins@review.openstack.org'
1188+ jenkins_email = 'jenkins@review.(openstack|stackforge).org'
1189 old_authors = 'AUTHORS.in'
1190 new_authors = 'AUTHORS'
1191- if os.path.isdir('.git'):
1192- # don't include jenkins email address in AUTHORS file
1193- git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | "
1194- "grep -v " + jenkins_email)
1195- changelog = _run_shell_command(git_log_cmd)
1196- mailmap = parse_mailmap()
1197- with open(new_authors, 'w') as new_authors_fh:
1198- new_authors_fh.write(canonicalize_emails(changelog, mailmap))
1199- if os.path.exists(old_authors):
1200- with open(old_authors, "r") as old_authors_fh:
1201- new_authors_fh.write('\n' + old_authors_fh.read())
1202+ if not os.getenv('SKIP_GENERATE_AUTHORS'):
1203+ if os.path.isdir('.git'):
1204+ # don't include jenkins email address in AUTHORS file
1205+ git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | "
1206+ "egrep -v '" + jenkins_email + "'")
1207+ changelog = _run_shell_command(git_log_cmd)
1208+ mailmap = parse_mailmap()
1209+ with open(new_authors, 'w') as new_authors_fh:
1210+ new_authors_fh.write(canonicalize_emails(changelog, mailmap))
1211+ if os.path.exists(old_authors):
1212+ with open(old_authors, "r") as old_authors_fh:
1213+ new_authors_fh.write('\n' + old_authors_fh.read())
1214+ else:
1215+ open(new_authors, 'w').close()
1216+
1217
1218 _rst_template = """%(heading)s
1219 %(underline)s
1220@@ -238,7 +243,8 @@
1221
1222 def write_versioninfo(project, version):
1223 """Write a simple file containing the version of the package."""
1224- open(os.path.join(project, 'versioninfo'), 'w').write("%s\n" % version)
1225+ with open(os.path.join(project, 'versioninfo'), 'w') as fil:
1226+ fil.write("%s\n" % version)
1227
1228
1229 def get_cmdclass():
1230@@ -319,6 +325,15 @@
1231 return cmdclass
1232
1233
1234+def get_git_branchname():
1235+ for branch in _run_shell_command("git branch --color=never").split("\n"):
1236+ if branch.startswith('*'):
1237+ _branch_name = branch.split()[1].strip()
1238+ if _branch_name == "(no":
1239+ _branch_name = "no-branch"
1240+ return _branch_name
1241+
1242+
1243 def get_pre_version(projectname, base_version):
1244 """Return a version which is leading up to a version that will
1245 be released in the future."""
1246@@ -329,7 +344,7 @@
1247 else:
1248 branch_name = os.getenv('BRANCHNAME',
1249 os.getenv('GERRIT_REFNAME',
1250- _get_git_branch_name()))
1251+ get_git_branchname()))
1252 version_suffix = _get_git_next_version_suffix(branch_name)
1253 version = "%s~%s" % (base_version, version_suffix)
1254 write_versioninfo(projectname, version)
1255
1256=== modified file 'glanceclient/openstack/common/version.py'
1257--- glanceclient/openstack/common/version.py 2012-08-16 12:24:18 +0000
1258+++ glanceclient/openstack/common/version.py 2012-11-27 14:34:22 +0000
1259@@ -20,7 +20,6 @@
1260
1261 import datetime
1262 import pkg_resources
1263-import os
1264
1265 import setup
1266
1267@@ -107,7 +106,7 @@
1268 versioninfo = "%s/versioninfo" % self.package
1269 try:
1270 raw_version = pkg_resources.resource_string(requirement,
1271- versioninfo)
1272+ versioninfo)
1273 self.version = self._newer_version(raw_version.strip())
1274 except (IOError, pkg_resources.DistributionNotFound):
1275 self.version = self._generate_version()
1276
1277=== modified file 'glanceclient/shell.py'
1278--- glanceclient/shell.py 2012-08-16 12:24:18 +0000
1279+++ glanceclient/shell.py 2012-11-27 14:34:22 +0000
1280@@ -47,6 +47,10 @@
1281 help=argparse.SUPPRESS,
1282 )
1283
1284+ parser.add_argument('--version',
1285+ action='version',
1286+ version=glanceclient.__version__)
1287+
1288 parser.add_argument('-d', '--debug',
1289 default=bool(utils.env('GLANCECLIENT_DEBUG')),
1290 action='store_true',
1291@@ -81,6 +85,11 @@
1292 default=600,
1293 help='Number of seconds to wait for a response')
1294
1295+ parser.add_argument('--no-ssl-compression',
1296+ dest='ssl_compression',
1297+ default=True, action='store_false',
1298+ help='Disable SSL compression when using https.')
1299+
1300 parser.add_argument('-f', '--force',
1301 dest='force',
1302 default=False, action='store_true',
1303@@ -395,6 +404,7 @@
1304 'ca_file': args.ca_file,
1305 'cert_file': args.cert_file,
1306 'key_file': args.key_file,
1307+ 'ssl_compression': args.ssl_compression
1308 }
1309
1310 client = glanceclient.Client(api_version, endpoint, **kwargs)
1311
1312=== modified file 'glanceclient/v1/images.py'
1313--- glanceclient/v1/images.py 2012-09-18 19:18:03 +0000
1314+++ glanceclient/v1/images.py 2012-11-27 14:34:22 +0000
1315@@ -177,10 +177,15 @@
1316 # Illegal seek. This means the user is trying
1317 # to pipe image data to the client, e.g.
1318 # echo testdata | bin/glance add blah..., or
1319- # that stdin is empty
1320- return 0
1321+ # that stdin is empty, or that a file-like
1322+ # object which doesn't support 'seek/tell' has
1323+ # been supplied.
1324+ return None
1325 else:
1326 raise
1327+ else:
1328+ # Cannot determine size of input image
1329+ return None
1330
1331 def create(self, **kwargs):
1332 """Create an image
1333@@ -190,10 +195,8 @@
1334 image_data = kwargs.pop('data', None)
1335 if image_data is not None:
1336 image_size = self._get_file_size(image_data)
1337- if image_size != 0:
1338+ if image_size is not None:
1339 kwargs.setdefault('size', image_size)
1340- else:
1341- image_data = None
1342
1343 fields = {}
1344 for field in kwargs:
1345@@ -218,16 +221,13 @@
1346
1347 TODO(bcwaldon): document accepted params
1348 """
1349+ image_data = kwargs.pop('data', None)
1350+ if image_data is not None:
1351+ image_size = self._get_file_size(image_data)
1352+ if image_size is not None:
1353+ kwargs.setdefault('size', image_size)
1354+
1355 hdrs = {}
1356- image_data = kwargs.pop('data', None)
1357- if image_data is not None:
1358- image_size = self._get_file_size(image_data)
1359- if image_size != 0:
1360- kwargs.setdefault('size', image_size)
1361- hdrs['Content-Length'] = image_size
1362- else:
1363- image_data = None
1364-
1365 try:
1366 purge_props = 'true' if kwargs.pop('purge_props') else 'false'
1367 except KeyError:
1368
1369=== modified file 'glanceclient/v1/shell.py'
1370--- glanceclient/v1/shell.py 2012-09-07 12:23:37 +0000
1371+++ glanceclient/v1/shell.py 2012-11-27 14:34:22 +0000
1372@@ -15,23 +15,36 @@
1373
1374 import argparse
1375 import copy
1376+import os
1377 import sys
1378
1379+if os.name == 'nt':
1380+ import msvcrt
1381+else:
1382+ msvcrt = None
1383+
1384+from glanceclient import exc
1385 from glanceclient.common import utils
1386 import glanceclient.v1.images
1387
1388 #NOTE(bcwaldon): import deprecated cli functions
1389 from glanceclient.v1.legacy_shell import *
1390
1391+CONTAINER_FORMATS = 'Acceptable formats: ami, ari, aki, bare, and ovf.'
1392+DISK_FORMATS = ('Acceptable formats: ami, ari, aki, vhd, vmdk, raw, '
1393+ 'qcow2, vdi, and iso.')
1394+
1395
1396 @utils.arg('--name', metavar='<NAME>',
1397 help='Filter images to those that have this name.')
1398 @utils.arg('--status', metavar='<STATUS>',
1399 help='Filter images to those that have this status.')
1400 @utils.arg('--container-format', metavar='<CONTAINER_FORMAT>',
1401- help='Filter images to those that have this container format.')
1402+ help='Filter images to those that have this container format. '
1403+ + CONTAINER_FORMATS)
1404 @utils.arg('--disk-format', metavar='<DISK_FORMAT>',
1405- help='Filter images to those that have this disk format.')
1406+ help='Filter images to those that have this disk format. '
1407+ + DISK_FORMATS)
1408 @utils.arg('--size-min', metavar='<SIZE>',
1409 help='Filter images to those with a size greater than this.')
1410 @utils.arg('--size-max', metavar='<SIZE>',
1411@@ -41,6 +54,8 @@
1412 action='append', dest='properties', default=[])
1413 @utils.arg('--page-size', metavar='<SIZE>', default=None, type=int,
1414 help='Number of images to request in each paginated request.')
1415+@utils.arg('--human-readable', action='store_true', default=False,
1416+ help='Print image size in a human-friendly format.')
1417 def do_image_list(gc, args):
1418 """List images you can access."""
1419 filter_keys = ['name', 'status', 'container_format', 'disk_format',
1420@@ -58,23 +73,56 @@
1421 images = gc.images.list(**kwargs)
1422 columns = ['ID', 'Name', 'Disk Format', 'Container Format',
1423 'Size', 'Status']
1424+
1425+ if args.human_readable:
1426+ def convert_size(image):
1427+ image.size = utils.make_size_human_readable(image.size)
1428+ return image
1429+
1430+ images = (convert_size(image) for image in images)
1431+
1432 utils.print_list(images, columns)
1433
1434
1435-def _image_show(image):
1436+def _image_show(image, human_readable=False):
1437 # Flatten image properties dict for display
1438 info = copy.deepcopy(image._info)
1439+ if human_readable:
1440+ info['size'] = utils.make_size_human_readable(info['size'])
1441 for (k, v) in info.pop('properties').iteritems():
1442 info['Property \'%s\'' % k] = v
1443
1444 utils.print_dict(info)
1445
1446
1447+def _set_data_field(fields, args):
1448+ if 'location' not in fields and 'copy_from' not in fields:
1449+ if args.file:
1450+ fields['data'] = open(args.file, 'rb')
1451+ else:
1452+ # We distinguish between cases where image data is pipelined:
1453+ # (1) glance ... < /tmp/file or cat /tmp/file | glance ...
1454+ # and cases where no image data is provided:
1455+ # (2) glance ...
1456+ if (sys.stdin.isatty() is not True):
1457+ # Our input is from stdin, and we are part of
1458+ # a pipeline, so data may be present. (We are of
1459+ # type (1) above.)
1460+ if msvcrt:
1461+ msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
1462+ fields['data'] = sys.stdin
1463+ else:
1464+ # We are of type (2) above, no image data supplied
1465+ fields['data'] = None
1466+
1467+
1468 @utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to describe.')
1469+@utils.arg('--human-readable', action='store_true', default=False,
1470+ help='Print image size in a human-friendly format.')
1471 def do_image_show(gc, args):
1472 """Describe a specific image."""
1473 image = gc.images.get(args.id)
1474- _image_show(image)
1475+ _image_show(image, args.human_readable)
1476
1477
1478 @utils.arg('--file', metavar='<FILE>',
1479@@ -92,10 +140,10 @@
1480 help='ID of image to reserve.')
1481 @utils.arg('--name', metavar='<NAME>',
1482 help='Name of image.')
1483-@utils.arg('--disk-format', metavar='<CONTAINER_FORMAT>',
1484- help='Disk format of image.')
1485-@utils.arg('--container-format', metavar='<DISK_FORMAT>',
1486- help='Container format of image.')
1487+@utils.arg('--disk-format', metavar='<DISK_FORMAT>',
1488+ help='Disk format of image. ' + DISK_FORMATS)
1489+@utils.arg('--container-format', metavar='<CONTAINER_FORMAT>',
1490+ help='Container format of image. ' + CONTAINER_FORMATS)
1491 @utils.arg('--owner', metavar='<TENANT_ID>',
1492 help='Tenant who should own image.')
1493 @utils.arg('--size', metavar='<SIZE>',
1494@@ -115,7 +163,8 @@
1495 ' creation. Alternatively, images can be passed to the client'
1496 ' via stdin.'))
1497 @utils.arg('--checksum', metavar='<CHECKSUM>',
1498- help='Hash of image data used Glance can use for verification.')
1499+ help=('Hash of image data used Glance can use for verification.'
1500+ ' Provide a md5 checksum here.'))
1501 @utils.arg('--copy-from', metavar='<IMAGE_URL>',
1502 help=('Similar to \'--location\' in usage, but this indicates that'
1503 ' the Glance server should immediately copy the data and'
1504@@ -124,13 +173,15 @@
1505 # to use --is-public
1506 @utils.arg('--public', action='store_true', default=False,
1507 help=argparse.SUPPRESS)
1508-@utils.arg('--is-public', type=utils.string_to_bool,
1509+@utils.arg('--is-public', type=utils.string_to_bool, metavar='[True|False]',
1510 help='Make image accessible to the public.')
1511-@utils.arg('--is-protected', type=utils.string_to_bool,
1512+@utils.arg('--is-protected', type=utils.string_to_bool, metavar='[True|False]',
1513 help='Prevent image from being deleted.')
1514 @utils.arg('--property', metavar="<key=value>", action='append', default=[],
1515 help=("Arbitrary property to associate with image. "
1516 "May be used multiple times."))
1517+@utils.arg('--human-readable', action='store_true', default=False,
1518+ help='Print image size in a human-friendly format.')
1519 def do_image_create(gc, args):
1520 """Create a new image."""
1521 # Filter out None values
1522@@ -151,23 +202,19 @@
1523 CREATE_PARAMS = glanceclient.v1.images.CREATE_PARAMS
1524 fields = dict(filter(lambda x: x[0] in CREATE_PARAMS, fields.items()))
1525
1526- if 'location' not in fields and 'copy_from' not in fields:
1527- if args.file:
1528- fields['data'] = open(args.file, 'r')
1529- else:
1530- fields['data'] = sys.stdin
1531+ _set_data_field(fields, args)
1532
1533 image = gc.images.create(**fields)
1534- _image_show(image)
1535+ _image_show(image, args.human_readable)
1536
1537
1538 @utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to modify.')
1539 @utils.arg('--name', metavar='<NAME>',
1540 help='Name of image.')
1541 @utils.arg('--disk-format', metavar='<CONTAINER_FORMAT>',
1542- help='Disk format of image.')
1543+ help='Disk format of image. ' + CONTAINER_FORMATS)
1544 @utils.arg('--container-format', metavar='<DISK_FORMAT>',
1545- help='Container format of image.')
1546+ help='Container format of image. ' + DISK_FORMATS)
1547 @utils.arg('--owner', metavar='<TENANT_ID>',
1548 help='Tenant who should own image.')
1549 @utils.arg('--size', metavar='<SIZE>',
1550@@ -191,9 +238,9 @@
1551 help=('Similar to \'--location\' in usage, but this indicates that'
1552 ' the Glance server should immediately copy the data and'
1553 ' store it in its configured image store.'))
1554-@utils.arg('--is-public', type=utils.string_to_bool,
1555+@utils.arg('--is-public', type=utils.string_to_bool, metavar='[True|False]',
1556 help='Make image accessible to the public.')
1557-@utils.arg('--is-protected', type=utils.string_to_bool,
1558+@utils.arg('--is-protected', type=utils.string_to_bool, metavar='[True|False]',
1559 help='Prevent image from being deleted.')
1560 @utils.arg('--property', metavar="<key=value>", action='append', default=[],
1561 help=("Arbitrary property to associate with image. "
1562@@ -202,6 +249,8 @@
1563 help=("If this flag is present, delete all image properties "
1564 "not explicitly set in the update request. Otherwise, "
1565 "those properties not referenced are preserved."))
1566+@utils.arg('--human-readable', action='store_true', default=False,
1567+ help='Print image size in a human-friendly format.')
1568 def do_image_update(gc, args):
1569 """Update a specific image."""
1570 # Filter out None values
1571@@ -222,20 +271,30 @@
1572 UPDATE_PARAMS = glanceclient.v1.images.UPDATE_PARAMS
1573 fields = dict(filter(lambda x: x[0] in UPDATE_PARAMS, fields.items()))
1574
1575- if 'location' not in fields and 'copy_from' not in fields:
1576- if args.file:
1577- fields['data'] = open(args.file, 'r')
1578- else:
1579- fields['data'] = sys.stdin
1580+ _set_data_field(fields, args)
1581
1582 image = gc.images.update(image_id, purge_props=args.purge_props, **fields)
1583- _image_show(image)
1584-
1585-
1586-@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to delete.')
1587+ _image_show(image, args.human_readable)
1588+
1589+
1590+@utils.arg('id', metavar='<IMAGE_ID>', nargs='+',
1591+ help='ID of image(s) to delete.')
1592 def do_image_delete(gc, args):
1593- """Delete a specific image."""
1594- gc.images.delete(args.id)
1595+ """Delete specified image(s)."""
1596+ for image in args.id:
1597+ try:
1598+ if args.verbose:
1599+ print 'Requesting image delete for %s ...' % image,
1600+
1601+ gc.images.delete(image)
1602+
1603+ if args.verbose:
1604+ print '[Done]'
1605+
1606+ except exc.HTTPException, e:
1607+ if args.verbose:
1608+ print '[Fail]'
1609+ print '%s: Unable to delete image %s' % (e, image)
1610
1611
1612 @utils.arg('--image-id', metavar='<IMAGE_ID>',
1613@@ -272,14 +331,14 @@
1614
1615
1616 @utils.arg('image_id', metavar='<IMAGE_ID>',
1617- help='Image to add member to.')
1618+ help='Image from which to remove member')
1619 @utils.arg('tenant_id', metavar='<TENANT_ID>',
1620- help='Tenant to add as member')
1621+ help='Tenant to remove as member')
1622 def do_member_delete(gc, args):
1623 """Remove a shared image from a tenant."""
1624- if not options.dry_run:
1625+ if not args.dry_run:
1626 gc.image_members.delete(args.image_id, args.tenant_id)
1627 else:
1628 print "Dry run. We would have done the following:"
1629- print ('Remove "%(member_id)s" from the member list of image '
1630- '"%(image_id)s"' % locals())
1631+ print ('Remove "%s" from the member list of image '
1632+ '"%s"' % (args.tenant_id, args.image_id))
1633
1634=== removed file 'glanceclient/version.py'
1635--- glanceclient/version.py 2012-08-16 12:24:18 +0000
1636+++ glanceclient/version.py 1970-01-01 00:00:00 +0000
1637@@ -1,21 +0,0 @@
1638-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1639-
1640-# Copyright 2012 OpenStack LLC
1641-#
1642-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1643-# not use this file except in compliance with the License. You may obtain
1644-# a copy of the License at
1645-#
1646-# http://www.apache.org/licenses/LICENSE-2.0
1647-#
1648-# Unless required by applicable law or agreed to in writing, software
1649-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1650-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1651-# License for the specific language governing permissions and limitations
1652-# under the License.
1653-
1654-
1655-from glanceclient.openstack.common import version as common_version
1656-
1657-version_info = common_version.VersionInfo('glanceclient',
1658- python_package='python-glanceclient')
1659
1660=== modified file 'glanceclient/versioninfo'
1661--- glanceclient/versioninfo 2012-09-18 19:18:03 +0000
1662+++ glanceclient/versioninfo 2012-11-27 14:34:22 +0000
1663@@ -1,1 +1,1 @@
1664-0.5.1
1665+0.6.0
1666
1667=== modified file 'python_glanceclient.egg-info/PKG-INFO'
1668--- python_glanceclient.egg-info/PKG-INFO 2012-09-18 19:18:03 +0000
1669+++ python_glanceclient.egg-info/PKG-INFO 2012-11-27 14:34:22 +0000
1670@@ -1,6 +1,6 @@
1671 Metadata-Version: 1.1
1672 Name: python-glanceclient
1673-Version: 0.5.1
1674+Version: 0.6.0
1675 Summary: Client library for OpenStack Image API
1676 Home-page: https://github.com/openstack/python-glanceclient
1677 Author: OpenStack Glance Contributors
1678@@ -18,6 +18,7 @@
1679 Platform: UNKNOWN
1680 Classifier: Development Status :: 4 - Beta
1681 Classifier: Environment :: Console
1682+Classifier: Environment :: OpenStack
1683 Classifier: Intended Audience :: Developers
1684 Classifier: Intended Audience :: Information Technology
1685 Classifier: License :: OSI Approved :: Apache Software License
1686
1687=== modified file 'python_glanceclient.egg-info/SOURCES.txt'
1688--- python_glanceclient.egg-info/SOURCES.txt 2012-08-16 12:24:18 +0000
1689+++ python_glanceclient.egg-info/SOURCES.txt 2012-11-27 14:34:22 +0000
1690@@ -15,7 +15,6 @@
1691 glanceclient/client.py
1692 glanceclient/exc.py
1693 glanceclient/shell.py
1694-glanceclient/version.py
1695 glanceclient/versioninfo
1696 glanceclient/common/__init__.py
1697 glanceclient/common/base.py
1698@@ -46,6 +45,8 @@
1699 python_glanceclient.egg-info/top_level.txt
1700 tests/__init__.py
1701 tests/test_exc.py
1702+tests/test_http.py
1703+tests/test_ssl.py
1704 tests/test_utils.py
1705 tests/utils.py
1706 tests/v1/__init__.py
1707@@ -54,6 +55,9 @@
1708 tests/v2/__init__.py
1709 tests/v2/test_images.py
1710 tests/v2/test_schemas.py
1711+tests/var/ca.crt
1712+tests/var/certificate.crt
1713+tests/var/privatekey.key
1714 tools/pip-requires
1715 tools/test-requires
1716 tools/with_venv.sh
1717\ No newline at end of file
1718
1719=== modified file 'python_glanceclient.egg-info/requires.txt'
1720--- python_glanceclient.egg-info/requires.txt 2012-09-07 12:23:37 +0000
1721+++ python_glanceclient.egg-info/requires.txt 2012-11-27 14:34:22 +0000
1722@@ -1,3 +1,4 @@
1723 prettytable>=0.6,<0.7
1724-python-keystoneclient>=0.1.2,<0.2
1725+python-keystoneclient>=0.1.2,<1
1726+pyOpenSSL
1727 warlock<2
1728\ No newline at end of file
1729
1730=== modified file 'setup.py'
1731--- setup.py 2012-08-16 12:24:18 +0000
1732+++ setup.py 2012-11-27 14:34:22 +0000
1733@@ -31,6 +31,7 @@
1734 classifiers=[
1735 'Development Status :: 4 - Beta',
1736 'Environment :: Console',
1737+ 'Environment :: OpenStack',
1738 'Intended Audience :: Developers',
1739 'Intended Audience :: Information Technology',
1740 'License :: OSI Approved :: Apache Software License',
1741@@ -44,4 +45,5 @@
1742 setup_requires=['setuptools-git>=0.4'],
1743 test_suite="nose.collector",
1744 entry_points={'console_scripts': ['glance = glanceclient.shell:main']},
1745+ data_files=[('glanceclient', ['glanceclient/versioninfo'])]
1746 )
1747
1748=== added file 'tests/test_http.py'
1749--- tests/test_http.py 1970-01-01 00:00:00 +0000
1750+++ tests/test_http.py 2012-11-27 14:34:22 +0000
1751@@ -0,0 +1,65 @@
1752+# Copyright 2012 OpenStack LLC.
1753+# All Rights Reserved.
1754+#
1755+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1756+# not use this file except in compliance with the License. You may obtain
1757+# a copy of the License at
1758+#
1759+# http://www.apache.org/licenses/LICENSE-2.0
1760+#
1761+# Unless required by applicable law or agreed to in writing, software
1762+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1763+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1764+# License for the specific language governing permissions and limitations
1765+# under the License.
1766+
1767+import httplib
1768+import socket
1769+import StringIO
1770+import unittest
1771+
1772+import mox
1773+
1774+from glanceclient import exc
1775+from glanceclient.common import http
1776+from tests import utils
1777+
1778+
1779+class TestClient(unittest.TestCase):
1780+ def test_connection_refused(self):
1781+ """
1782+ Should receive a CommunicationError if connection refused.
1783+ And the error should list the host and port that refused the
1784+ connection
1785+ """
1786+ endpoint = 'http://example.com:9292'
1787+ client = http.HTTPClient(endpoint, token=u'abc123')
1788+ m = mox.Mox()
1789+ m.StubOutWithMock(httplib.HTTPConnection, 'request')
1790+ httplib.HTTPConnection.request(
1791+ mox.IgnoreArg(),
1792+ mox.IgnoreArg(),
1793+ headers=mox.IgnoreArg(),
1794+ ).AndRaise(socket.error())
1795+ m.ReplayAll()
1796+ try:
1797+ client.json_request('GET', '/v1/images/detail?limit=20')
1798+ #NOTE(alaski) We expect exc.CommunicationError to be raised
1799+ # so we should never reach this point. try/except is used here
1800+ # rather than assertRaises() so that we can check the body of
1801+ # the exception.
1802+ self.fail('An exception should have bypassed this line.')
1803+ except exc.CommunicationError, comm_err:
1804+ fail_msg = ("Exception message '%s' should contain '%s'" %
1805+ (comm_err.message, endpoint))
1806+ self.assertTrue(endpoint in comm_err.message, fail_msg)
1807+ finally:
1808+ m.UnsetStubs()
1809+
1810+
1811+class TestResponseBodyIterator(unittest.TestCase):
1812+ def test_iter_default_chunk_size_64k(self):
1813+ resp = utils.FakeResponse({}, StringIO.StringIO('X' * 98304))
1814+ iterator = http.ResponseBodyIterator(resp)
1815+ chunks = list(iterator)
1816+ self.assertEqual(chunks, ['X' * 65536, 'X' * 32768])
1817
1818=== added file 'tests/test_ssl.py'
1819--- tests/test_ssl.py 1970-01-01 00:00:00 +0000
1820+++ tests/test_ssl.py 2012-11-27 14:34:22 +0000
1821@@ -0,0 +1,112 @@
1822+# Copyright 2012 OpenStack LLC.
1823+# All Rights Reserved.
1824+#
1825+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1826+# not use this file except in compliance with the License. You may obtain
1827+# a copy of the License at
1828+#
1829+# http://www.apache.org/licenses/LICENSE-2.0
1830+#
1831+# Unless required by applicable law or agreed to in writing, software
1832+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1833+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1834+# License for the specific language governing permissions and limitations
1835+# under the License.
1836+
1837+import os
1838+import unittest
1839+
1840+from glanceclient import exc
1841+from glanceclient.common import http
1842+
1843+TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
1844+ 'var'))
1845+
1846+
1847+class TestVerifiedHTTPSConnection(unittest.TestCase):
1848+ def test_ssl_init_ok(self):
1849+ """
1850+ Test VerifiedHTTPSConnection class init
1851+ """
1852+ key_file = os.path.join(TEST_VAR_DIR, 'privatekey.key')
1853+ cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
1854+ ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
1855+ try:
1856+ conn = http.VerifiedHTTPSConnection('127.0.0.1', 0,
1857+ key_file=key_file,
1858+ cert_file=cert_file,
1859+ ca_file=ca_file)
1860+ except exc.SSLConfigurationError:
1861+ self.fail('Failed to init VerifiedHTTPSConnection.')
1862+
1863+ def test_ssl_init_cert_no_key(self):
1864+ """
1865+ Test VerifiedHTTPSConnection: absense of SSL key file.
1866+ """
1867+ cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
1868+ ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
1869+ try:
1870+ conn = http.VerifiedHTTPSConnection('127.0.0.1', 0,
1871+ cert_file=cert_file,
1872+ ca_file=ca_file)
1873+ self.fail('Failed to raise assertion.')
1874+ except exc.SSLConfigurationError:
1875+ pass
1876+
1877+ def test_ssl_init_key_no_cert(self):
1878+ """
1879+ Test VerifiedHTTPSConnection: absense of SSL cert file.
1880+ """
1881+ key_file = os.path.join(TEST_VAR_DIR, 'privatekey.key')
1882+ ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
1883+ try:
1884+ conn = http.VerifiedHTTPSConnection('127.0.0.1', 0,
1885+ key_file=key_file,
1886+ ca_file=ca_file)
1887+ except:
1888+ self.fail('Failed to init VerifiedHTTPSConnection.')
1889+
1890+ def test_ssl_init_bad_key(self):
1891+ """
1892+ Test VerifiedHTTPSConnection: bad key.
1893+ """
1894+ key_file = os.path.join(TEST_VAR_DIR, 'badkey.key')
1895+ cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
1896+ ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
1897+ try:
1898+ conn = http.VerifiedHTTPSConnection('127.0.0.1', 0,
1899+ cert_file=cert_file,
1900+ ca_file=ca_file)
1901+ self.fail('Failed to raise assertion.')
1902+ except exc.SSLConfigurationError:
1903+ pass
1904+
1905+ def test_ssl_init_bad_cert(self):
1906+ """
1907+ Test VerifiedHTTPSConnection: bad cert.
1908+ """
1909+ key_file = os.path.join(TEST_VAR_DIR, 'privatekey.key')
1910+ cert_file = os.path.join(TEST_VAR_DIR, 'badcert.crt')
1911+ ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
1912+ try:
1913+ conn = http.VerifiedHTTPSConnection('127.0.0.1', 0,
1914+ cert_file=cert_file,
1915+ ca_file=ca_file)
1916+ self.fail('Failed to raise assertion.')
1917+ except exc.SSLConfigurationError:
1918+ pass
1919+
1920+ def test_ssl_init_bad_ca(self):
1921+ """
1922+ Test VerifiedHTTPSConnection: bad CA.
1923+ """
1924+ key_file = os.path.join(TEST_VAR_DIR, 'privatekey.key')
1925+ cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
1926+ ca_file = os.path.join(TEST_VAR_DIR, 'badca.crt')
1927+ try:
1928+ conn = http.VerifiedHTTPSConnection('127.0.0.1', 0,
1929+ cert_file=cert_file,
1930+ ca_file=ca_file)
1931+ self.fail('Failed to raise assertion.')
1932+ except exc.SSLConfigurationError:
1933+ pass
1934
1935=== modified file 'tests/test_utils.py'
1936--- tests/test_utils.py 2012-08-16 12:24:18 +0000
1937+++ tests/test_utils.py 2012-11-27 14:34:22 +0000
1938@@ -43,3 +43,10 @@
1939 fixture = 'CCC'
1940 checksum = 'defb99e69a9f1f6e06f15006b1f166ae'
1941 data = ''.join([f for f in utils.integrity_iter(fixture, checksum)])
1942+
1943+ def test_make_size_human_readable(self):
1944+ self.assertEqual("106B", utils.make_size_human_readable(106))
1945+ self.assertEqual("1000kB", utils.make_size_human_readable(1024000))
1946+ self.assertEqual("1MB", utils.make_size_human_readable(1048576))
1947+ self.assertEqual("1.4GB", utils.make_size_human_readable(1476395008))
1948+ self.assertEqual("9.3MB", utils.make_size_human_readable(9761280))
1949
1950=== modified file 'tests/utils.py'
1951--- tests/utils.py 2012-08-16 12:24:18 +0000
1952+++ tests/utils.py 2012-11-27 14:34:22 +0000
1953@@ -40,11 +40,19 @@
1954
1955
1956 class FakeResponse(object):
1957- def __init__(self, headers):
1958+ def __init__(self, headers, body=None):
1959+ """
1960+ :param headers: dict representing HTTP response headers
1961+ :param body: file-like object
1962+ """
1963 self.headers = headers
1964+ self.body = body
1965
1966 def getheaders(self):
1967 return copy.deepcopy(self.headers).items()
1968
1969 def getheader(self, key, default):
1970 return self.headers.get(key, default)
1971+
1972+ def read(self, amt):
1973+ return self.body.read(amt)
1974
1975=== modified file 'tests/v1/test_images.py'
1976--- tests/v1/test_images.py 2012-09-18 19:18:03 +0000
1977+++ tests/v1/test_images.py 2012-11-27 14:34:22 +0000
1978@@ -390,7 +390,7 @@
1979 def test_update_with_data(self):
1980 image_data = StringIO.StringIO('XXX')
1981 self.mgr.update('1', data=image_data)
1982- expect_headers = {'x-image-meta-size': '3', 'Content-Length': 3}
1983+ expect_headers = {'x-image-meta-size': '3'}
1984 expect = [('PUT', '/v1/images/1', expect_headers, image_data)]
1985 self.assertEqual(self.api.calls, expect)
1986
1987
1988=== added directory 'tests/var'
1989=== added file 'tests/var/ca.crt'
1990--- tests/var/ca.crt 1970-01-01 00:00:00 +0000
1991+++ tests/var/ca.crt 2012-11-27 14:34:22 +0000
1992@@ -0,0 +1,35 @@
1993+-----BEGIN CERTIFICATE-----
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
2026+WoRMgEwjGJWqzhJZUYpUAQ==
2027+-----END CERTIFICATE-----
2028
2029=== added file 'tests/var/certificate.crt'
2030--- tests/var/certificate.crt 1970-01-01 00:00:00 +0000
2031+++ tests/var/certificate.crt 2012-11-27 14:34:22 +0000
2032@@ -0,0 +1,30 @@
2033+-----BEGIN CERTIFICATE-----
2034+MIIFLjCCAxYCAQEwDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMCQVUxEzARBgNV
2035+BAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAGA1UECxMJ
2036+R2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0EwHhcNMTIwMjA5MTcxMDUzWhcN
2037+MjIwMjA2MTcxMDUzWjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
2038+ZTESMBAGA1UEChMJT3BlbnN0YWNrMQ8wDQYDVQQLEwZHbGFuY2UxEDAOBgNVBAMT
2039+BzAuMC4wLjAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXpUkQN6pu
2040+avo+gz3o1K4krVdPl1m7NjNJDyD/+ZH0EGNcEN7iag1qPE7JsjqGPNZsQK1dMoXb
2041+Sz+OSi9qvNeJnBcfwUx5qTAtwyAb9AxGkwuMafIU+lWbsclo+dPGsja01ywbXTCZ
2042+bF32iqnpOMYhfxWUdoQYiBkhxxhW9eMPKLS/KkP8/bx+Vaa2XJiAebqkd9nrksAA
2043+BeGc9mlafYBEmiChPdJEPw+1ePA4QVq9aPepDsqAKtGN8JLpmoC3BdxQQTbbwL3Q
2044+8fTXK4tCNUaVk4AbDy/McFq6y0ocQoBPJjihOY35mWG/OLtcI99yPOpWGnps/5aG
2045+/64DDJ2D67Fnaj6gKHV+6TXFO8KZxlnxtgtiZDJBZkneTBt9ArSOv+l6NBsumRz0
2046+iEJ4o4H1S2TSMnprAvX7WnGtc6Xi9gXahYcDHEelwwYzqAiTBv6hxSp4MZ2dNXa+
2047+KzOitC7ZbV2qsg0au0wjfE/oSQ3NvsvUr8nOmfutJTvHRAwbC1v4G/tuAsO7O0w2
2048+0u2B3u+pG06m5+rnEqp+rB9hmukRYTfgEFRRsVIvpFl/cwvPXKRcX03UIMx+lLr9
2049+Ft+ep7YooBhY3wY2kwCxD4lRYNmbwsCIVywZt40f/4ad98TkufR9NhsfycxGeqbr
2050+mTMFlZ8TTlmP82iohekKCOvoyEuTIWL2+wIDAQABMA0GCSqGSIb3DQEBBQUAA4IC
2051+AQBMUBgV0R+Qltf4Du7u/8IFmGAoKR/mktB7R1gRRAqsvecUt7kIwBexGdavGg1y
2052+0pU0+lgUZjJ20N1SlPD8gkNHfXE1fL6fmMjWz4dtYJjzRVhpufHPeBW4tl8DgHPN
2053+rBGAYQ+drDSXaEjiPQifuzKx8WS+DGA3ki4co5mPjVnVH1xvLIdFsk89z3b3YD1k
2054+yCJ/a9K36x6Z/c67JK7s6MWtrdRF9+MVnRKJ2PK4xznd1kBz16V+RA466wBDdARY
2055+vFbtkafbEqOb96QTonIZB7+fAldKDPZYnwPqasreLmaGOaM8sxtlPYAJ5bjDONbc
2056+AaXG8BMRQyO4FyH237otDKlxPyHOFV66BaffF5S8OlwIMiZoIvq+IcTZOdtDUSW2
2057+KHNLfe5QEDZdKjWCBrfqAfvNuG13m03WqfmcMHl3o/KiPJlx8l9Z4QEzZ9xcyQGL
2058+cncgeHM9wJtzi2cD/rTDNFsx/gxvoyutRmno7I3NRbKmpsXF4StZioU3USRspB07
2059+hYXOVnG3pS+PjVby7ThT3gvFHSocguOsxClx1epdUJAmJUbmM7NmOp5WVBVtMtC2
2060+Su4NG/xJciXitKzw+btb7C7RjO6OEqv/1X/oBDzKBWQAwxUC+lqmnM7W6oqWJFEM
2061+YfTLnrjs7Hj6ThMGcEnfvc46dWK3dz0RjsQzUxugPuEkLA==
2062+-----END CERTIFICATE-----
2063
2064=== added file 'tests/var/privatekey.key'
2065--- tests/var/privatekey.key 1970-01-01 00:00:00 +0000
2066+++ tests/var/privatekey.key 2012-11-27 14:34:22 +0000
2067@@ -0,0 +1,51 @@
2068+-----BEGIN RSA PRIVATE KEY-----
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
2118+-----END RSA PRIVATE KEY-----
2119
2120=== modified file 'tools/pip-requires'
2121--- tools/pip-requires 2012-09-07 12:23:37 +0000
2122+++ tools/pip-requires 2012-11-27 14:34:22 +0000
2123@@ -1,4 +1,5 @@
2124 argparse
2125 prettytable>=0.6,<0.7
2126-python-keystoneclient>=0.1.2,<0.2
2127+python-keystoneclient>=0.1.2,<1
2128+pyOpenSSL
2129 warlock<2
2130
2131=== modified file 'tools/test-requires'
2132--- tools/test-requires 2012-09-07 12:23:37 +0000
2133+++ tools/test-requires 2012-11-27 14:34:22 +0000
2134@@ -1,5 +1,11 @@
2135+distribute>=0.6.24
2136+
2137 mox
2138 nose
2139+nose-exclude
2140+nosexcover
2141+openstack.nose_plugin
2142+nosehtmloutput
2143 pep8==1.2
2144 setuptools-git>=0.4
2145 sphinx>=1.1.2

Subscribers

People subscribed via source and target branches