Merge lp:~devcamcar/horizon/combine-django-nova into lp:~hudson-openstack/horizon/trunk

Proposed by Devin Carlen
Status: Merged
Approved by: Devin Carlen
Approved revision: 18
Merged at revision: 19
Proposed branch: lp:~devcamcar/horizon/combine-django-nova
Merge into: lp:~hudson-openstack/horizon/trunk
Diff against target: 7481 lines (+6861/-42)
95 files modified
.bzrignore (+10/-3)
README (+14/-0)
django-nova/LICENSE (+176/-0)
django-nova/README (+42/-0)
django-nova/bootstrap.py (+260/-0)
django-nova/buildout.cfg (+19/-0)
django-nova/setup.py (+29/-0)
django-nova/src/django_nova/adminclient.py (+498/-0)
django-nova/src/django_nova/connection.py (+38/-0)
django-nova/src/django_nova/exceptions.py (+95/-0)
django-nova/src/django_nova/forms.py (+262/-0)
django-nova/src/django_nova/management/commands/createnovausers.py (+37/-0)
django-nova/src/django_nova/manager.py (+340/-0)
django-nova/src/django_nova/models.py (+121/-0)
django-nova/src/django_nova/shortcuts.py (+131/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/add_project.html (+45/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/add_project_user.html (+69/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/base_projects.html (+16/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/change_list.html (+3/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/delete_project.html (+25/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/delete_project_user.html (+25/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/edit_project.html (+95/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/global_edit_user.html (+71/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/project_list.html (+42/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/project_user.html (+76/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/send_credentials.html (+87/-0)
django-nova/src/django_nova/templates/admin/django_nova/project/user_list.html (+39/-0)
django-nova/src/django_nova/templates/django_nova/_messages.html (+41/-0)
django-nova/src/django_nova/templates/django_nova/base.html (+85/-0)
django-nova/src/django_nova/templates/django_nova/credentials/expired.html (+17/-0)
django-nova/src/django_nova/templates/django_nova/images/_launch_form.html (+7/-0)
django-nova/src/django_nova/templates/django_nova/images/_list.html (+112/-0)
django-nova/src/django_nova/templates/django_nova/images/base.html (+7/-0)
django-nova/src/django_nova/templates/django_nova/images/detail_list.html (+207/-0)
django-nova/src/django_nova/templates/django_nova/images/edit.html (+35/-0)
django-nova/src/django_nova/templates/django_nova/images/index.html (+70/-0)
django-nova/src/django_nova/templates/django_nova/images/launch.html (+32/-0)
django-nova/src/django_nova/templates/django_nova/instances/_instances_list.html (+104/-0)
django-nova/src/django_nova/templates/django_nova/instances/base.html (+7/-0)
django-nova/src/django_nova/templates/django_nova/instances/detail_list.html (+33/-0)
django-nova/src/django_nova/templates/django_nova/instances/edit.html (+34/-0)
django-nova/src/django_nova/templates/django_nova/instances/index.html (+98/-0)
django-nova/src/django_nova/templates/django_nova/instances/performance.html (+58/-0)
django-nova/src/django_nova/templates/django_nova/keypairs/_create_form.html (+5/-0)
django-nova/src/django_nova/templates/django_nova/keypairs/_list.html (+31/-0)
django-nova/src/django_nova/templates/django_nova/keypairs/base.html (+7/-0)
django-nova/src/django_nova/templates/django_nova/keypairs/index.html (+77/-0)
django-nova/src/django_nova/templates/django_nova/projects/edit_user.html (+72/-0)
django-nova/src/django_nova/templates/django_nova/projects/index.html (+26/-0)
django-nova/src/django_nova/templates/django_nova/projects/manage.html (+45/-0)
django-nova/src/django_nova/templates/django_nova/securitygroups/_authorize_form.html (+5/-0)
django-nova/src/django_nova/templates/django_nova/securitygroups/_create_form.html (+5/-0)
django-nova/src/django_nova/templates/django_nova/securitygroups/_revoke_form.html (+3/-0)
django-nova/src/django_nova/templates/django_nova/securitygroups/base.html (+7/-0)
django-nova/src/django_nova/templates/django_nova/securitygroups/detail.html (+62/-0)
django-nova/src/django_nova/templates/django_nova/securitygroups/index.html (+59/-0)
django-nova/src/django_nova/templates/django_nova/volumes/_attach_form.html (+5/-0)
django-nova/src/django_nova/templates/django_nova/volumes/_create_form.html (+5/-0)
django-nova/src/django_nova/templates/django_nova/volumes/base.html (+7/-0)
django-nova/src/django_nova/templates/django_nova/volumes/index.html (+84/-0)
django-nova/src/django_nova/templatetags/admin_extras.py (+50/-0)
django-nova/src/django_nova/templatetags/django_nova_tags.py (+37/-0)
django-nova/src/django_nova/templatetags/project_tags.py (+39/-0)
django-nova/src/django_nova/templatetags/region_tags.py (+40/-0)
django-nova/src/django_nova/templatetags/sidebar_tags.py (+46/-0)
django-nova/src/django_nova/templatetags/truncate_filter.py (+31/-0)
django-nova/src/django_nova/tests/__init__.py (+1/-0)
django-nova/src/django_nova/tests/urls.py (+36/-0)
django-nova/src/django_nova/tests/view_tests/__init__.py (+7/-0)
django-nova/src/django_nova/tests/view_tests/base.py (+90/-0)
django-nova/src/django_nova/tests/view_tests/credential_tests.py (+70/-0)
django-nova/src/django_nova/tests/view_tests/image_tests.py (+232/-0)
django-nova/src/django_nova/tests/view_tests/instance_tests.py (+67/-0)
django-nova/src/django_nova/tests/view_tests/keypair_tests.py (+93/-0)
django-nova/src/django_nova/tests/view_tests/region_tests.py (+43/-0)
django-nova/src/django_nova/tests/view_tests/volume_tests.py (+170/-0)
django-nova/src/django_nova/testsettings.py (+21/-0)
django-nova/src/django_nova/urls/admin_project.py (+55/-0)
django-nova/src/django_nova/urls/admin_roles.py (+32/-0)
django-nova/src/django_nova/urls/project.py (+129/-0)
django-nova/src/django_nova/urls/region.py (+29/-0)
django-nova/src/django_nova/views/admin.py (+326/-0)
django-nova/src/django_nova/views/credentials.py (+46/-0)
django-nova/src/django_nova/views/images.py (+229/-0)
django-nova/src/django_nova/views/instances.py (+203/-0)
django-nova/src/django_nova/views/keypairs.py (+122/-0)
django-nova/src/django_nova/views/projects.py (+107/-0)
django-nova/src/django_nova/views/regions.py (+36/-0)
django-nova/src/django_nova/views/securitygroups.py (+180/-0)
django-nova/src/django_nova/views/volumes.py (+151/-0)
openstack-dashboard/README (+4/-16)
openstack-dashboard/dashboard/templates/index.html (+2/-2)
openstack-dashboard/local/local_settings.py.example (+6/-0)
openstack-dashboard/tools/install_venv.py (+6/-18)
run_tests.sh (+8/-3)
To merge this branch: bzr merge lp:~devcamcar/horizon/combine-django-nova
Reviewer Review Type Date Requested Status
Devin Carlen Pending
Review via email: mp+52172@code.launchpad.net

Description of the change

The django-nova project has been moved into the openstack-dashboard repo to simplify development efforts.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-03-01 21:20:32 +0000
3+++ .bzrignore 2011-03-04 07:54:59 +0000
4@@ -1,3 +1,10 @@
5-.dashboard-venv
6-local/dashboard_openstack.sqlite3
7-local/local_settings.py
8+django-nova/.installed.cfg
9+django-nova/bin
10+django-nova/develop-eggs/
11+django-nova/downloads/
12+django-nova/eggs/
13+django-nova/parts/
14+django-nova/src/django_nova.egg-info
15+openstack-dashboard/.dashboard-venv
16+openstack-dashboard/local/dashboard_openstack.sqlite3
17+openstack-dashboard/local/local_settings.py
18
19=== added file 'README'
20--- README 1970-01-01 00:00:00 +0000
21+++ README 2011-03-04 07:54:59 +0000
22@@ -0,0 +1,14 @@
23+OpenStack Dashboard
24+-------------------
25+
26+The OpenStack Dashboard is a Django based reference implementation of a web
27+based managent interface for OpenStack Nova.
28+
29+It is based on django-nova, which is designed to be a generic Django module
30+that can be re-used in other sites.
31+
32+For more information about how to get started with the OpenStack Dashboard,
33+view the README file in the openstack-dashboard folder.
34+
35+For more information about working directly with django-nova, see the
36+README file in the django-nova folder.
37
38=== added directory 'django-nova'
39=== added file 'django-nova/LICENSE'
40--- django-nova/LICENSE 1970-01-01 00:00:00 +0000
41+++ django-nova/LICENSE 2011-03-04 07:54:59 +0000
42@@ -0,0 +1,176 @@
43+
44+ Apache License
45+ Version 2.0, January 2004
46+ http://www.apache.org/licenses/
47+
48+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
49+
50+ 1. Definitions.
51+
52+ "License" shall mean the terms and conditions for use, reproduction,
53+ and distribution as defined by Sections 1 through 9 of this document.
54+
55+ "Licensor" shall mean the copyright owner or entity authorized by
56+ the copyright owner that is granting the License.
57+
58+ "Legal Entity" shall mean the union of the acting entity and all
59+ other entities that control, are controlled by, or are under common
60+ control with that entity. For the purposes of this definition,
61+ "control" means (i) the power, direct or indirect, to cause the
62+ direction or management of such entity, whether by contract or
63+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
64+ outstanding shares, or (iii) beneficial ownership of such entity.
65+
66+ "You" (or "Your") shall mean an individual or Legal Entity
67+ exercising permissions granted by this License.
68+
69+ "Source" form shall mean the preferred form for making modifications,
70+ including but not limited to software source code, documentation
71+ source, and configuration files.
72+
73+ "Object" form shall mean any form resulting from mechanical
74+ transformation or translation of a Source form, including but
75+ not limited to compiled object code, generated documentation,
76+ and conversions to other media types.
77+
78+ "Work" shall mean the work of authorship, whether in Source or
79+ Object form, made available under the License, as indicated by a
80+ copyright notice that is included in or attached to the work
81+ (an example is provided in the Appendix below).
82+
83+ "Derivative Works" shall mean any work, whether in Source or Object
84+ form, that is based on (or derived from) the Work and for which the
85+ editorial revisions, annotations, elaborations, or other modifications
86+ represent, as a whole, an original work of authorship. For the purposes
87+ of this License, Derivative Works shall not include works that remain
88+ separable from, or merely link (or bind by name) to the interfaces of,
89+ the Work and Derivative Works thereof.
90+
91+ "Contribution" shall mean any work of authorship, including
92+ the original version of the Work and any modifications or additions
93+ to that Work or Derivative Works thereof, that is intentionally
94+ submitted to Licensor for inclusion in the Work by the copyright owner
95+ or by an individual or Legal Entity authorized to submit on behalf of
96+ the copyright owner. For the purposes of this definition, "submitted"
97+ means any form of electronic, verbal, or written communication sent
98+ to the Licensor or its representatives, including but not limited to
99+ communication on electronic mailing lists, source code control systems,
100+ and issue tracking systems that are managed by, or on behalf of, the
101+ Licensor for the purpose of discussing and improving the Work, but
102+ excluding communication that is conspicuously marked or otherwise
103+ designated in writing by the copyright owner as "Not a Contribution."
104+
105+ "Contributor" shall mean Licensor and any individual or Legal Entity
106+ on behalf of whom a Contribution has been received by Licensor and
107+ subsequently incorporated within the Work.
108+
109+ 2. Grant of Copyright License. Subject to the terms and conditions of
110+ this License, each Contributor hereby grants to You a perpetual,
111+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
112+ copyright license to reproduce, prepare Derivative Works of,
113+ publicly display, publicly perform, sublicense, and distribute the
114+ Work and such Derivative Works in Source or Object form.
115+
116+ 3. Grant of Patent License. Subject to the terms and conditions of
117+ this License, each Contributor hereby grants to You a perpetual,
118+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
119+ (except as stated in this section) patent license to make, have made,
120+ use, offer to sell, sell, import, and otherwise transfer the Work,
121+ where such license applies only to those patent claims licensable
122+ by such Contributor that are necessarily infringed by their
123+ Contribution(s) alone or by combination of their Contribution(s)
124+ with the Work to which such Contribution(s) was submitted. If You
125+ institute patent litigation against any entity (including a
126+ cross-claim or counterclaim in a lawsuit) alleging that the Work
127+ or a Contribution incorporated within the Work constitutes direct
128+ or contributory patent infringement, then any patent licenses
129+ granted to You under this License for that Work shall terminate
130+ as of the date such litigation is filed.
131+
132+ 4. Redistribution. You may reproduce and distribute copies of the
133+ Work or Derivative Works thereof in any medium, with or without
134+ modifications, and in Source or Object form, provided that You
135+ meet the following conditions:
136+
137+ (a) You must give any other recipients of the Work or
138+ Derivative Works a copy of this License; and
139+
140+ (b) You must cause any modified files to carry prominent notices
141+ stating that You changed the files; and
142+
143+ (c) You must retain, in the Source form of any Derivative Works
144+ that You distribute, all copyright, patent, trademark, and
145+ attribution notices from the Source form of the Work,
146+ excluding those notices that do not pertain to any part of
147+ the Derivative Works; and
148+
149+ (d) If the Work includes a "NOTICE" text file as part of its
150+ distribution, then any Derivative Works that You distribute must
151+ include a readable copy of the attribution notices contained
152+ within such NOTICE file, excluding those notices that do not
153+ pertain to any part of the Derivative Works, in at least one
154+ of the following places: within a NOTICE text file distributed
155+ as part of the Derivative Works; within the Source form or
156+ documentation, if provided along with the Derivative Works; or,
157+ within a display generated by the Derivative Works, if and
158+ wherever such third-party notices normally appear. The contents
159+ of the NOTICE file are for informational purposes only and
160+ do not modify the License. You may add Your own attribution
161+ notices within Derivative Works that You distribute, alongside
162+ or as an addendum to the NOTICE text from the Work, provided
163+ that such additional attribution notices cannot be construed
164+ as modifying the License.
165+
166+ You may add Your own copyright statement to Your modifications and
167+ may provide additional or different license terms and conditions
168+ for use, reproduction, or distribution of Your modifications, or
169+ for any such Derivative Works as a whole, provided Your use,
170+ reproduction, and distribution of the Work otherwise complies with
171+ the conditions stated in this License.
172+
173+ 5. Submission of Contributions. Unless You explicitly state otherwise,
174+ any Contribution intentionally submitted for inclusion in the Work
175+ by You to the Licensor shall be under the terms and conditions of
176+ this License, without any additional terms or conditions.
177+ Notwithstanding the above, nothing herein shall supersede or modify
178+ the terms of any separate license agreement you may have executed
179+ with Licensor regarding such Contributions.
180+
181+ 6. Trademarks. This License does not grant permission to use the trade
182+ names, trademarks, service marks, or product names of the Licensor,
183+ except as required for reasonable and customary use in describing the
184+ origin of the Work and reproducing the content of the NOTICE file.
185+
186+ 7. Disclaimer of Warranty. Unless required by applicable law or
187+ agreed to in writing, Licensor provides the Work (and each
188+ Contributor provides its Contributions) on an "AS IS" BASIS,
189+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
190+ implied, including, without limitation, any warranties or conditions
191+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
192+ PARTICULAR PURPOSE. You are solely responsible for determining the
193+ appropriateness of using or redistributing the Work and assume any
194+ risks associated with Your exercise of permissions under this License.
195+
196+ 8. Limitation of Liability. In no event and under no legal theory,
197+ whether in tort (including negligence), contract, or otherwise,
198+ unless required by applicable law (such as deliberate and grossly
199+ negligent acts) or agreed to in writing, shall any Contributor be
200+ liable to You for damages, including any direct, indirect, special,
201+ incidental, or consequential damages of any character arising as a
202+ result of this License or out of the use or inability to use the
203+ Work (including but not limited to damages for loss of goodwill,
204+ work stoppage, computer failure or malfunction, or any and all
205+ other commercial damages or losses), even if such Contributor
206+ has been advised of the possibility of such damages.
207+
208+ 9. Accepting Warranty or Additional Liability. While redistributing
209+ the Work or Derivative Works thereof, You may choose to offer,
210+ and charge a fee for, acceptance of support, warranty, indemnity,
211+ or other liability obligations and/or rights consistent with this
212+ License. However, in accepting such obligations, You may act only
213+ on Your own behalf and on Your sole responsibility, not on behalf
214+ of any other Contributor, and only if You agree to indemnify,
215+ defend, and hold each Contributor harmless for any liability
216+ incurred by, or claims asserted against, such Contributor by reason
217+ of your accepting any such warranty or additional liability.
218+
219
220=== added file 'django-nova/README'
221--- django-nova/README 1970-01-01 00:00:00 +0000
222+++ django-nova/README 2011-03-04 07:54:59 +0000
223@@ -0,0 +1,42 @@
224+OpenStack Django-Nova
225+---------------------
226+
227+The Django-Nova project is a Django module that is used to provide web based
228+interactions with the OpenStack Nova cloud controller.
229+
230+There is a reference implementation that uses this module located at:
231+
232+ http://launchpad.net/openstack-dashboard
233+
234+It is highly recommended that you make use of this reference implementation
235+so that changes you make can be visualized effectively and are consistent.
236+Using this reference implementation as a development environment will greatly
237+simplify development of the django-nova module.
238+
239+Of course, if you are developing your own Django site using django-nova, then
240+you can disregard this advice.
241+
242+
243+
244+Getting Started
245+---------------
246+
247+Django-Nova uses Buildout (http://www.buildout.org/) to manage local
248+development. To configure your local Buildout environment:
249+
250+ $ python bootstrap.py
251+ $ bin/buildout
252+
253+This will install all the dependencies of django-nova and provide some useful
254+scripts in the bin/ directory:
255+
256+ bin/python provides a python shell for the current buildout.
257+ bin/django provides django functions for the current buildout.
258+
259+
260+You should now be able to run unit tests as follows:
261+
262+ $ bin/django test
263+
264+
265+
266
267=== added file 'django-nova/bootstrap.py'
268--- django-nova/bootstrap.py 1970-01-01 00:00:00 +0000
269+++ django-nova/bootstrap.py 2011-03-04 07:54:59 +0000
270@@ -0,0 +1,260 @@
271+##############################################################################
272+#
273+# Copyright (c) 2006 Zope Foundation and Contributors.
274+# All Rights Reserved.
275+#
276+# This software is subject to the provisions of the Zope Public License,
277+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
278+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
279+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
280+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
281+# FOR A PARTICULAR PURPOSE.
282+#
283+##############################################################################
284+"""Bootstrap a buildout-based project
285+
286+Simply run this script in a directory containing a buildout.cfg.
287+The script accepts buildout command-line options, so you can
288+use the -c option to specify an alternate configuration file.
289+"""
290+
291+import os, shutil, sys, tempfile, textwrap, urllib, urllib2, subprocess
292+from optparse import OptionParser
293+
294+if sys.platform == 'win32':
295+ def quote(c):
296+ if ' ' in c:
297+ return '"%s"' % c # work around spawn lamosity on windows
298+ else:
299+ return c
300+else:
301+ quote = str
302+
303+# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
304+stdout, stderr = subprocess.Popen(
305+ [sys.executable, '-Sc',
306+ 'try:\n'
307+ ' import ConfigParser\n'
308+ 'except ImportError:\n'
309+ ' print 1\n'
310+ 'else:\n'
311+ ' print 0\n'],
312+ stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
313+has_broken_dash_S = bool(int(stdout.strip()))
314+
315+# In order to be more robust in the face of system Pythons, we want to
316+# run without site-packages loaded. This is somewhat tricky, in
317+# particular because Python 2.6's distutils imports site, so starting
318+# with the -S flag is not sufficient. However, we'll start with that:
319+if not has_broken_dash_S and 'site' in sys.modules:
320+ # We will restart with python -S.
321+ args = sys.argv[:]
322+ args[0:0] = [sys.executable, '-S']
323+ args = map(quote, args)
324+ os.execv(sys.executable, args)
325+# Now we are running with -S. We'll get the clean sys.path, import site
326+# because distutils will do it later, and then reset the path and clean
327+# out any namespace packages from site-packages that might have been
328+# loaded by .pth files.
329+clean_path = sys.path[:]
330+import site
331+sys.path[:] = clean_path
332+for k, v in sys.modules.items():
333+ if k in ('setuptools', 'pkg_resources') or (
334+ hasattr(v, '__path__') and
335+ len(v.__path__)==1 and
336+ not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
337+ # This is a namespace package. Remove it.
338+ sys.modules.pop(k)
339+
340+is_jython = sys.platform.startswith('java')
341+
342+setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
343+distribute_source = 'http://python-distribute.org/distribute_setup.py'
344+
345+# parsing arguments
346+def normalize_to_url(option, opt_str, value, parser):
347+ if value:
348+ if '://' not in value: # It doesn't smell like a URL.
349+ value = 'file://%s' % (
350+ urllib.pathname2url(
351+ os.path.abspath(os.path.expanduser(value))),)
352+ if opt_str == '--download-base' and not value.endswith('/'):
353+ # Download base needs a trailing slash to make the world happy.
354+ value += '/'
355+ else:
356+ value = None
357+ name = opt_str[2:].replace('-', '_')
358+ setattr(parser.values, name, value)
359+
360+usage = '''\
361+[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
362+
363+Bootstraps a buildout-based project.
364+
365+Simply run this script in a directory containing a buildout.cfg, using the
366+Python that you want bin/buildout to use.
367+
368+Note that by using --setup-source and --download-base to point to
369+local resources, you can keep this script from going over the network.
370+'''
371+
372+parser = OptionParser(usage=usage)
373+parser.add_option("-v", "--version", dest="version",
374+ help="use a specific zc.buildout version")
375+parser.add_option("-d", "--distribute",
376+ action="store_true", dest="use_distribute", default=False,
377+ help="Use Distribute rather than Setuptools.")
378+parser.add_option("--setup-source", action="callback", dest="setup_source",
379+ callback=normalize_to_url, nargs=1, type="string",
380+ help=("Specify a URL or file location for the setup file. "
381+ "If you use Setuptools, this will default to " +
382+ setuptools_source + "; if you use Distribute, this "
383+ "will default to " + distribute_source +"."))
384+parser.add_option("--download-base", action="callback", dest="download_base",
385+ callback=normalize_to_url, nargs=1, type="string",
386+ help=("Specify a URL or directory for downloading "
387+ "zc.buildout and either Setuptools or Distribute. "
388+ "Defaults to PyPI."))
389+parser.add_option("--eggs",
390+ help=("Specify a directory for storing eggs. Defaults to "
391+ "a temporary directory that is deleted when the "
392+ "bootstrap script completes."))
393+parser.add_option("-t", "--accept-buildout-test-releases",
394+ dest='accept_buildout_test_releases',
395+ action="store_true", default=False,
396+ help=("Normally, if you do not specify a --version, the "
397+ "bootstrap script and buildout gets the newest "
398+ "*final* versions of zc.buildout and its recipes and "
399+ "extensions for you. If you use this flag, "
400+ "bootstrap and buildout will get the newest releases "
401+ "even if they are alphas or betas."))
402+parser.add_option("-c", None, action="store", dest="config_file",
403+ help=("Specify the path to the buildout configuration "
404+ "file to be used."))
405+
406+options, args = parser.parse_args()
407+
408+# if -c was provided, we push it back into args for buildout's main function
409+if options.config_file is not None:
410+ args += ['-c', options.config_file]
411+
412+if options.eggs:
413+ eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
414+else:
415+ eggs_dir = tempfile.mkdtemp()
416+
417+if options.setup_source is None:
418+ if options.use_distribute:
419+ options.setup_source = distribute_source
420+ else:
421+ options.setup_source = setuptools_source
422+
423+if options.accept_buildout_test_releases:
424+ args.append('buildout:accept-buildout-test-releases=true')
425+args.append('bootstrap')
426+
427+try:
428+ import pkg_resources
429+ import setuptools # A flag. Sometimes pkg_resources is installed alone.
430+ if not hasattr(pkg_resources, '_distribute'):
431+ raise ImportError
432+except ImportError:
433+ ez_code = urllib2.urlopen(
434+ options.setup_source).read().replace('\r\n', '\n')
435+ ez = {}
436+ exec ez_code in ez
437+ setup_args = dict(to_dir=eggs_dir, download_delay=0)
438+ if options.download_base:
439+ setup_args['download_base'] = options.download_base
440+ if options.use_distribute:
441+ setup_args['no_fake'] = True
442+ ez['use_setuptools'](**setup_args)
443+ if 'pkg_resources' in sys.modules:
444+ reload(sys.modules['pkg_resources'])
445+ import pkg_resources
446+ # This does not (always?) update the default working set. We will
447+ # do it.
448+ for path in sys.path:
449+ if path not in pkg_resources.working_set.entries:
450+ pkg_resources.working_set.add_entry(path)
451+
452+cmd = [quote(sys.executable),
453+ '-c',
454+ quote('from setuptools.command.easy_install import main; main()'),
455+ '-mqNxd',
456+ quote(eggs_dir)]
457+
458+if not has_broken_dash_S:
459+ cmd.insert(1, '-S')
460+
461+find_links = options.download_base
462+if not find_links:
463+ find_links = os.environ.get('bootstrap-testing-find-links')
464+if find_links:
465+ cmd.extend(['-f', quote(find_links)])
466+
467+if options.use_distribute:
468+ setup_requirement = 'distribute'
469+else:
470+ setup_requirement = 'setuptools'
471+ws = pkg_resources.working_set
472+setup_requirement_path = ws.find(
473+ pkg_resources.Requirement.parse(setup_requirement)).location
474+env = dict(
475+ os.environ,
476+ PYTHONPATH=setup_requirement_path)
477+
478+requirement = 'zc.buildout'
479+version = options.version
480+if version is None and not options.accept_buildout_test_releases:
481+ # Figure out the most recent final version of zc.buildout.
482+ import setuptools.package_index
483+ _final_parts = '*final-', '*final'
484+ def _final_version(parsed_version):
485+ for part in parsed_version:
486+ if (part[:1] == '*') and (part not in _final_parts):
487+ return False
488+ return True
489+ index = setuptools.package_index.PackageIndex(
490+ search_path=[setup_requirement_path])
491+ if find_links:
492+ index.add_find_links((find_links,))
493+ req = pkg_resources.Requirement.parse(requirement)
494+ if index.obtain(req) is not None:
495+ best = []
496+ bestv = None
497+ for dist in index[req.project_name]:
498+ distv = dist.parsed_version
499+ if _final_version(distv):
500+ if bestv is None or distv > bestv:
501+ best = [dist]
502+ bestv = distv
503+ elif distv == bestv:
504+ best.append(dist)
505+ if best:
506+ best.sort()
507+ version = best[-1].version
508+if version:
509+ requirement = '=='.join((requirement, version))
510+cmd.append(requirement)
511+
512+if is_jython:
513+ import subprocess
514+ exitcode = subprocess.Popen(cmd, env=env).wait()
515+else: # Windows prefers this, apparently; otherwise we would prefer subprocess
516+ exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
517+if exitcode != 0:
518+ sys.stdout.flush()
519+ sys.stderr.flush()
520+ print ("An error occurred when trying to install zc.buildout. "
521+ "Look above this message for any errors that "
522+ "were output by easy_install.")
523+ sys.exit(exitcode)
524+
525+ws.add_entry(eggs_dir)
526+ws.require(requirement)
527+import zc.buildout.buildout
528+zc.buildout.buildout.main(args)
529+if not options.eggs: # clean up temporary egg directory
530+ shutil.rmtree(eggs_dir)
531
532=== added file 'django-nova/buildout.cfg'
533--- django-nova/buildout.cfg 1970-01-01 00:00:00 +0000
534+++ django-nova/buildout.cfg 2011-03-04 07:54:59 +0000
535@@ -0,0 +1,19 @@
536+[buildout]
537+parts = python django
538+develop = .
539+eggs = django-nova
540+
541+[python]
542+recipe = zc.recipe.egg
543+interpreter = python
544+eggs = ${buildout:eggs}
545+
546+[django]
547+recipe = djangorecipe
548+version = 1.2.4
549+project = django_nova
550+projectegg = django_nova
551+settings = testsettings
552+test = django_nova
553+eggs = ${buildout:eggs}
554+
555
556=== added file 'django-nova/setup.py'
557--- django-nova/setup.py 1970-01-01 00:00:00 +0000
558+++ django-nova/setup.py 2011-03-04 07:54:59 +0000
559@@ -0,0 +1,29 @@
560+import os
561+from setuptools import setup, find_packages
562+
563+def read(fname):
564+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
565+
566+setup(
567+ name = "django-nova",
568+ version = "0.1",
569+ url = 'https://launchpad.net/django-nova/',
570+ license = 'Apache 2.0',
571+ description = "A Django interface for OpenStack Nova.",
572+ long_description = read('README'),
573+ author = 'Devin Carlen',
574+ author_email = 'devin.carlen@gmail.com',
575+ packages = find_packages('src'),
576+ package_dir = {'': 'src'},
577+ install_requires = ['setuptools', 'boto==1.9b', 'mox>=0.5.0'],
578+ classifiers = [
579+ 'Development Status :: 4 - Beta',
580+ 'Framework :: Django',
581+ 'Intended Audience :: Developers',
582+ 'License :: OSI Approved :: Apache License',
583+ 'Operating System :: OS Independent',
584+ 'Programming Language :: Python',
585+ 'Topic :: Internet :: WWW/HTTP',
586+ ]
587+)
588+
589
590=== added directory 'django-nova/src'
591=== added directory 'django-nova/src/django_nova'
592=== added file 'django-nova/src/django_nova/__init__.py'
593=== added file 'django-nova/src/django_nova/adminclient.py'
594--- django-nova/src/django_nova/adminclient.py 1970-01-01 00:00:00 +0000
595+++ django-nova/src/django_nova/adminclient.py 2011-03-04 07:54:59 +0000
596@@ -0,0 +1,498 @@
597+# vim: tabstop=4 shiftwidth=4 softtabstop=4
598+
599+# Copyright 2010 United States Government as represented by the
600+# Administrator of the National Aeronautics and Space Administration.
601+# All Rights Reserved.
602+#
603+# Licensed under the Apache License, Version 2.0 (the "License"); you may
604+# not use this file except in compliance with the License. You may obtain
605+# a copy of the License at
606+#
607+# http://www.apache.org/licenses/LICENSE-2.0
608+#
609+# Unless required by applicable law or agreed to in writing, software
610+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
611+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
612+# License for the specific language governing permissions and limitations
613+# under the License.
614+"""
615+Nova User API client library.
616+"""
617+
618+import base64
619+import boto
620+import boto.exception
621+import httplib
622+import re
623+import string
624+from boto.ec2.regioninfo import RegionInfo
625+
626+
627+DEFAULT_CLC_URL='http://127.0.0.1:8773'
628+DEFAULT_REGION='nova'
629+DEFAULT_ACCESS_KEY='admin'
630+DEFAULT_SECRET_KEY='admin'
631+
632+
633+class UserInfo(object):
634+ """
635+ Information about a Nova user, as parsed through SAX
636+ fields include:
637+ username
638+ accesskey
639+ secretkey
640+
641+ and an optional field containing a zip with X509 cert & rc
642+ file
643+ """
644+
645+ def __init__(self, connection=None, username=None, endpoint=None):
646+ self.connection = connection
647+ self.username = username
648+ self.endpoint = endpoint
649+
650+ def __repr__(self):
651+ return 'UserInfo:%s' % self.username
652+
653+ def startElement(self, name, attrs, connection):
654+ return None
655+
656+ def endElement(self, name, value, connection):
657+ if name == 'username':
658+ self.username = str(value)
659+ elif name == 'file':
660+ self.file = base64.b64decode(str(value))
661+ elif name == 'accesskey':
662+ self.accesskey = str(value)
663+ elif name == 'secretkey':
664+ self.secretkey = str(value)
665+
666+
667+class UserRole(object):
668+ """
669+ Information about a Nova user's role, as parsed through SAX.
670+ Fields include:
671+ role
672+ """
673+ def __init__(self, connection=None):
674+ self.connection = connection
675+ self.role = None
676+
677+ def __repr__(self):
678+ return 'UserRole:%s' % self.role
679+
680+ def startElement(self, name, attrs, connection):
681+ return None
682+
683+ def endElement(self, name, value, connection):
684+ if name == 'role':
685+ self.role = value
686+ else:
687+ setattr(self, name, str(value))
688+
689+
690+class ProjectInfo(object):
691+ """
692+ Information about a Nova project, as parsed through SAX
693+ Fields include:
694+ projectname
695+ description
696+ projectManagerId
697+ memberIds
698+ """
699+
700+ def __init__(self, connection=None):
701+ self.connection = connection
702+ self.projectname = None
703+ self.description = None
704+ self.projectManagerId = None
705+ self.memberIds = []
706+
707+ def __repr__(self):
708+ return 'ProjectInfo:%s' % self.projectname
709+
710+ def startElement(self, name, attrs, connection):
711+ return None
712+
713+ def endElement(self, name, value, connection):
714+ if name == 'projectname':
715+ self.projectname = value
716+ elif name == 'description':
717+ self.description = value
718+ elif name == 'projectManagerId':
719+ self.projectManagerId = value
720+ elif name == 'memberId':
721+ self.memberIds.append(value)
722+ else:
723+ setattr(self, name, str(value))
724+
725+
726+class ProjectMember(object):
727+ """
728+ Information about a Nova project member, as parsed through SAX.
729+ Fields include:
730+ memberId
731+ """
732+
733+ def __init__(self, connection=None):
734+ self.connection = connection
735+ self.memberId = None
736+
737+ def __repr__(self):
738+ return 'ProjectMember:%s' % self.memberId
739+
740+ def startElement(self, name, attrs, connection):
741+ return None
742+
743+ def endElement(self, name, value, connection):
744+ if name == 'member':
745+ self.memberId = value
746+ else:
747+ setattr(self, name, str(value))
748+
749+
750+class HostInfo(object):
751+ """
752+ Information about a Nova Host, as parsed through SAX:
753+ Hostname
754+ Compute Service Status
755+ Volume Service Status
756+ """
757+
758+ def __init__(self, connection=None):
759+ self.connection = connection
760+ self.hostname = None
761+ self.compute = None
762+ self.volume = None
763+ self.instance_count = 0
764+ self.volume_count = 0
765+
766+ def __repr__(self):
767+ return 'Host:%s' % self.hostname
768+
769+ # this is needed by the sax parser, so ignore the ugly name
770+ def startElement(self, name, attrs, connection):
771+ return None
772+
773+ # this is needed by the sax parser, so ignore the ugly name
774+ def endElement(self, name, value, connection):
775+ fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name))
776+ setattr(self, fixed_name, value)
777+
778+
779+class Vpn(object):
780+ """
781+ Information about a Vpn, as parsed through SAX
782+ fields include:
783+ instance_id
784+ project_id
785+ public_ip
786+ public_port
787+ created_at
788+ internal_ip
789+ state
790+ """
791+
792+ def __init__(self, connection=None):
793+ self.connection = connection
794+ self.instance_id = None
795+ self.project_id = None
796+
797+ def __repr__(self):
798+ return 'Vpn:%s:%s' % (self.project_id, self.instance_id)
799+
800+ def startElement(self, name, attrs, connection):
801+ return None
802+
803+ def endElement(self, name, value, connection):
804+ if name == 'instanceId':
805+ self.instance_id = str(value)
806+ elif name == 'projectId':
807+ self.project_id = str(value)
808+ elif name == 'publicIp':
809+ self.public_ip = str(value)
810+ elif name == 'publicPort':
811+ self.public_port = str(value)
812+ elif name == 'createdAt':
813+ self.created_at = str(value)
814+ elif name == 'internalIp':
815+ self.internal_ip = str(value)
816+ else:
817+ setattr(self, name, str(value))
818+
819+
820+class InstanceType(object):
821+ """
822+ Information about a Nova instance type, as parsed through SAX.
823+
824+ **Fields include**
825+
826+ * name
827+ * vcpus
828+ * disk_gb
829+ * memory_mb
830+ * flavor_id
831+
832+ """
833+
834+ def __init__(self, connection=None):
835+ self.connection = connection
836+ self.name = None
837+ self.vcpus = None
838+ self.disk_gb = None
839+ self.memory_mb = None
840+ self.flavor_id = None
841+
842+ def __repr__(self):
843+ return 'InstanceType:%s' % self.name
844+
845+ def startElement(self, name, attrs, connection):
846+ return None
847+
848+ def endElement(self, name, value, connection):
849+ if name == "memoryMb":
850+ self.memory_mb = str(value)
851+ elif name == "flavorId":
852+ self.flavor_id = str(value)
853+ elif name == "diskGb":
854+ self.disk_gb = str(value)
855+ else:
856+ setattr(self, name, str(value))
857+
858+
859+class StatusResponse(object):
860+ def __init__(self, connection=None):
861+ self.connection = connection
862+ self.status = None
863+
864+ def __repr__(self):
865+ return 'Status:%s' % self.status
866+
867+ def startElement(self, name, attrs, connection):
868+ return None
869+
870+ def endElement(self, name, value, connection):
871+ setattr(self, name, str(value))
872+
873+
874+class NovaAdminClient(object):
875+ def __init__(self, clc_url=DEFAULT_CLC_URL, region=DEFAULT_REGION,
876+ access_key=DEFAULT_ACCESS_KEY, secret_key=DEFAULT_SECRET_KEY,
877+ **kwargs):
878+ parts = self.split_clc_url(clc_url)
879+
880+ self.clc_url = clc_url
881+ self.region = region
882+ self.access = access_key
883+ self.secret = secret_key
884+ self.apiconn = boto.connect_ec2(aws_access_key_id=access_key,
885+ aws_secret_access_key=secret_key,
886+ is_secure=parts['is_secure'],
887+ region=RegionInfo(None,
888+ region,
889+ parts['ip']),
890+ port=parts['port'],
891+ path='/services/Admin',
892+ **kwargs)
893+ self.apiconn.APIVersion = 'nova'
894+
895+ def connection_for(self, username, project, clc_url=None, region=None,
896+ **kwargs):
897+ """
898+ Returns a boto ec2 connection for the given username.
899+ """
900+ if not clc_url:
901+ clc_url = self.clc_url
902+ if not region:
903+ region = self.region
904+ parts = self.split_clc_url(clc_url)
905+ user = self.get_user(username)
906+ access_key = '%s:%s' % (user.accesskey, project)
907+ return boto.connect_ec2(aws_access_key_id=access_key,
908+ aws_secret_access_key=user.secretkey,
909+ is_secure=parts['is_secure'],
910+ region=RegionInfo(None,
911+ self.region,
912+ parts['ip']),
913+ port=parts['port'],
914+ path='/services/Cloud',
915+ **kwargs)
916+
917+ def split_clc_url(self, clc_url):
918+ """
919+ Splits a cloud controller endpoint url.
920+ """
921+ parts = httplib.urlsplit(clc_url)
922+ is_secure = parts.scheme == 'https'
923+ ip, port = parts.netloc.split(':')
924+ return {'ip': ip, 'port': int(port), 'is_secure': is_secure}
925+
926+ def get_users(self):
927+ """ grabs the list of all users """
928+ return self.apiconn.get_list('DescribeUsers', {}, [('item', UserInfo)])
929+
930+ def get_user(self, name):
931+ """ grab a single user by name """
932+ try:
933+ return self.apiconn.get_object('DescribeUser', {'Name': name}, UserInfo)
934+ except boto.exception.BotoServerError, e:
935+ if e.status == 400 and e.error_code == 'NotFound':
936+ return None
937+ raise
938+
939+ def has_user(self, username):
940+ """ determine if user exists """
941+ return self.get_user(username) != None
942+
943+ def create_user(self, username):
944+ """ creates a new user, returning the userinfo object with access/secret """
945+ return self.apiconn.get_object('RegisterUser', {'Name': username}, UserInfo)
946+
947+ def delete_user(self, username):
948+ """ deletes a user """
949+ return self.apiconn.get_object('DeregisterUser', {'Name': username}, UserInfo)
950+
951+ def get_roles(self, project_roles=True):
952+ """Returns a list of available roles."""
953+ return self.apiconn.get_list('DescribeRoles',
954+ {'ProjectRoles': project_roles},
955+ [('item', UserRole)])
956+
957+ def get_user_roles(self, user, project=None):
958+ """Returns a list of roles for the given user.
959+ Omitting project will return any global roles that the user has.
960+ Specifying project will return only project specific roles.
961+ """
962+ params = {'User':user}
963+ if project:
964+ params['Project'] = project
965+ return self.apiconn.get_list('DescribeUserRoles',
966+ params,
967+ [('item', UserRole)])
968+
969+ def add_user_role(self, user, role, project=None):
970+ """
971+ Add a role to a user either globally or for a specific project.
972+ """
973+ return self.modify_user_role(user, role, project=project,
974+ operation='add')
975+
976+ def remove_user_role(self, user, role, project=None):
977+ """
978+ Remove a role from a user either globally or for a specific project.
979+ """
980+ return self.modify_user_role(user, role, project=project,
981+ operation='remove')
982+
983+ def modify_user_role(self, user, role, project=None, operation='add',
984+ **kwargs):
985+ """
986+ Add or remove a role for a user and project.
987+ """
988+ params = {'User': user,
989+ 'Role': role,
990+ 'Project': project,
991+ 'Operation': operation}
992+ return self.apiconn.get_status('ModifyUserRole', params)
993+
994+ def get_projects(self, user=None):
995+ """
996+ Returns a list of all projects.
997+ """
998+ if user:
999+ params = {'User': user}
1000+ else:
1001+ params = {}
1002+ return self.apiconn.get_list('DescribeProjects',
1003+ params,
1004+ [('item', ProjectInfo)])
1005+
1006+ def get_project(self, name):
1007+ """
1008+ Returns a single project with the specified name.
1009+ """
1010+ project = self.apiconn.get_object('DescribeProject',
1011+ {'Name': name},
1012+ ProjectInfo)
1013+
1014+ if project.projectname != None:
1015+ return project
1016+
1017+ def create_project(self, projectname, manager_user, description=None,
1018+ member_users=None):
1019+ """
1020+ Creates a new project.
1021+ """
1022+ params = {'Name': projectname,
1023+ 'ManagerUser': manager_user,
1024+ 'Description': description,
1025+ 'MemberUsers': member_users}
1026+ return self.apiconn.get_object('RegisterProject', params, ProjectInfo)
1027+
1028+ def delete_project(self, projectname):
1029+ """
1030+ Permanently deletes the specified project.
1031+ """
1032+ return self.apiconn.get_object('DeregisterProject',
1033+ {'Name': projectname},
1034+ ProjectInfo)
1035+
1036+ def get_project_members(self, name):
1037+ """
1038+ Returns a list of members of a project.
1039+ """
1040+ return self.apiconn.get_list('DescribeProjectMembers',
1041+ {'Name': name},
1042+ [('item', ProjectMember)])
1043+
1044+ def add_project_member(self, user, project):
1045+ """
1046+ Adds a user to a project.
1047+ """
1048+ return self.modify_project_member(user, project, operation='add')
1049+
1050+ def remove_project_member(self, user, project):
1051+ """
1052+ Removes a user from a project.
1053+ """
1054+ return self.modify_project_member(user, project, operation='remove')
1055+
1056+ def modify_project_member(self, user, project, operation='add'):
1057+ """
1058+ Adds or removes a user from a project.
1059+ """
1060+ params = {'User': user,
1061+ 'Project': project,
1062+ 'Operation': operation}
1063+ return self.apiconn.get_status('ModifyProjectMember', params)
1064+
1065+ def get_zip(self, user, project):
1066+ """
1067+ Returns the content of a zip file containing novarc and access credentials.
1068+ """
1069+ params = {'Name': user, 'Project': project}
1070+ zip = self.apiconn.get_object('GenerateX509ForUser', params, UserInfo)
1071+ return zip.file
1072+
1073+ def start_vpn(self, project):
1074+ """
1075+ Starts the vpn for a user
1076+ """
1077+ return self.apiconn.get_object('StartVpn', {'Project': project}, Vpn)
1078+
1079+ def get_vpns(self):
1080+ """Return a list of vpn with project name"""
1081+ return self.apiconn.get_list('DescribeVpns', {}, [('item', Vpn)])
1082+
1083+ def get_hosts(self):
1084+ return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)])
1085+
1086+ def get_instance_types(self):
1087+ """Grabs the list of all users."""
1088+ return self.apiconn.get_list('DescribeInstanceTypes', {},
1089+ [('item', InstanceType)])
1090+
1091+ def disable_project_credentials(self, project):
1092+ """Revoke project credentials and kill the cloudpipe/vpn instance"""
1093+ return self.apiconn.get_object('DisableProjectCredentials',
1094+ {'Project': project}, StatusResponse)
1095
1096=== added file 'django-nova/src/django_nova/connection.py'
1097--- django-nova/src/django_nova/connection.py 1970-01-01 00:00:00 +0000
1098+++ django-nova/src/django_nova/connection.py 2011-03-04 07:54:59 +0000
1099@@ -0,0 +1,38 @@
1100+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1101+
1102+# Copyright 2010 United States Government as represented by the
1103+# Administrator of the National Aeronautics and Space Administration.
1104+# All Rights Reserved.
1105+#
1106+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1107+# not use this file except in compliance with the License. You may obtain
1108+# a copy of the License at
1109+#
1110+# http://www.apache.org/licenses/LICENSE-2.0
1111+#
1112+# Unless required by applicable law or agreed to in writing, software
1113+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1114+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1115+# License for the specific language governing permissions and limitations
1116+# under the License.
1117+"""
1118+Manage connections to Nova's admin API.
1119+"""
1120+
1121+from django.conf import settings
1122+from django_nova import adminclient
1123+
1124+
1125+def get_nova_admin_connection():
1126+ """
1127+ Returns a Nova administration connection.
1128+ """
1129+ return adminclient.NovaAdminClient (
1130+ clc_url=settings.NOVA_DEFAULT_ENDPOINT,
1131+ region=settings.NOVA_DEFAULT_REGION,
1132+ access_key=settings.NOVA_ACCESS_KEY,
1133+ secret_key=settings.NOVA_SECRET_KEY
1134+ )
1135+
1136+
1137+
1138
1139=== added file 'django-nova/src/django_nova/exceptions.py'
1140--- django-nova/src/django_nova/exceptions.py 1970-01-01 00:00:00 +0000
1141+++ django-nova/src/django_nova/exceptions.py 2011-03-04 07:54:59 +0000
1142@@ -0,0 +1,95 @@
1143+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1144+
1145+# Copyright 2010 United States Government as represented by the
1146+# Administrator of the National Aeronautics and Space Administration.
1147+# All Rights Reserved.
1148+#
1149+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1150+# not use this file except in compliance with the License. You may obtain
1151+# a copy of the License at
1152+#
1153+# http://www.apache.org/licenses/LICENSE-2.0
1154+#
1155+# Unless required by applicable law or agreed to in writing, software
1156+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1157+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1158+# License for the specific language governing permissions and limitations
1159+# under the License.
1160+
1161+"""
1162+Better wrappers for errors from Nova's admin api.
1163+"""
1164+
1165+import boto.exception
1166+from django.shortcuts import redirect
1167+from django.core import exceptions as core_exceptions
1168+
1169+
1170+class NovaServerError(Exception):
1171+ """
1172+ Consumes a BotoServerError and gives more meaningful errors.
1173+ """
1174+ def __init__(self, ec2error):
1175+ self.status = ec2error.status
1176+ self.message = ec2error.reason
1177+
1178+ def __str__(self):
1179+ return self.message
1180+
1181+
1182+class NovaApiError(Exception):
1183+ """
1184+ Used when Nova returns a 400 Bad Request status.
1185+ """
1186+ def __init__(self, ec2error):
1187+ self.message = ec2error.error_message
1188+
1189+ def __str__(self):
1190+ return self.message
1191+
1192+
1193+class NovaUnavailableError(NovaServerError):
1194+ """
1195+ Used when Nova returns a 503 Service Unavailable status.
1196+ """
1197+ pass
1198+
1199+
1200+class NovaUnauthorizedError(core_exceptions.PermissionDenied):
1201+ """
1202+ Used when Nova returns a 401 Not Authorized status.
1203+ """
1204+ pass
1205+
1206+
1207+def wrap_nova_error(func):
1208+ """
1209+ Used to decorate a function that interacts with boto. It will catch
1210+ and convert boto server errors and reraise as a more specific nova error.
1211+ """
1212+ def decorator(*args, **kwargs):
1213+ try:
1214+ return func(*args, **kwargs)
1215+ except boto.exception.BotoServerError, e:
1216+ if e.status == 400 and e.error_code == 'ApiError':
1217+ raise NovaApiError(e)
1218+ elif e.status == 401:
1219+ raise NovaUnauthorizedError()
1220+ elif e.status == 503:
1221+ raise NovaUnavailableError(e)
1222+ raise NovaServerError(e)
1223+ return decorator
1224+
1225+
1226+def handle_nova_error(func):
1227+ """
1228+ Decorator for handling nova errors in a generalized way.
1229+ """
1230+ def decorator(*args, **kwargs):
1231+ try:
1232+ return func(*args, **kwargs)
1233+ except NovaUnavailableError:
1234+ return redirect('nova_unavailable')
1235+ return decorator
1236+
1237+
1238
1239=== added file 'django-nova/src/django_nova/forms.py'
1240--- django-nova/src/django_nova/forms.py 1970-01-01 00:00:00 +0000
1241+++ django-nova/src/django_nova/forms.py 2011-03-04 07:54:59 +0000
1242@@ -0,0 +1,262 @@
1243+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1244+
1245+# Copyright 2010 United States Government as represented by the
1246+# Administrator of the National Aeronautics and Space Administration.
1247+# All Rights Reserved.
1248+#
1249+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1250+# not use this file except in compliance with the License. You may obtain
1251+# a copy of the License at
1252+#
1253+# http://www.apache.org/licenses/LICENSE-2.0
1254+#
1255+# Unless required by applicable law or agreed to in writing, software
1256+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1257+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1258+# License for the specific language governing permissions and limitations
1259+# under the License.
1260+
1261+"""
1262+Forms used by various views.
1263+"""
1264+
1265+import re
1266+
1267+from django import forms
1268+from django.contrib.auth import models as auth_models
1269+from django_nova.connection import get_nova_admin_connection
1270+from django_nova.exceptions import wrap_nova_error
1271+
1272+
1273+# TODO: Store this in settings.
1274+MAX_VOLUME_SIZE = 100
1275+
1276+alphanumeric_re = re.compile(r'^\w+$')
1277+
1278+
1279+@wrap_nova_error
1280+def get_instance_type_choices():
1281+ """
1282+ Returns list of instance types from nova admin api
1283+ """
1284+ nova = get_nova_admin_connection()
1285+ instance_types = nova.get_instance_types()
1286+ rv = []
1287+ for t in instance_types:
1288+ rv.append((t.name, "%s (%sMB memory, %s cpu, %sGB space)" % \
1289+ (t.name, t.memory_mb, t.vcpus, t.disk_gb)))
1290+ return rv
1291+
1292+def get_instance_choices(project):
1293+ choices = [(i.id, i.id) for i in project.get_instances()]
1294+ if not len(choices):
1295+ choices = [('', 'none available')]
1296+ return choices
1297+
1298+def get_key_pair_choices(project):
1299+ choices = [(k.name, k.name) for k in project.get_key_pairs()]
1300+ if not len(choices):
1301+ choices = [('', 'none available')]
1302+ return choices
1303+
1304+#def get_security_group_choices(project):
1305+# choices = [(g.name, g.description) for g in project.get_security_groups()]
1306+# if len(choices) == 0:
1307+# choices = [('', 'none available')]
1308+# return choices
1309+
1310+def get_available_volume_choices(project):
1311+ choices = [(v.id, '%s %s - %dGB' % (v.id, v.displayName, v.size)) for v in \
1312+ project.get_volumes() if v.status != "in-use"]
1313+ if not len(choices):
1314+ choices = [('', 'none available')]
1315+ return choices
1316+
1317+def get_protocols():
1318+ return (
1319+ ('tcp', 'tcp'),
1320+ ('udp', 'udp'),
1321+ )
1322+
1323+@wrap_nova_error
1324+def get_roles(project_roles=True):
1325+ nova = get_nova_admin_connection()
1326+ roles = nova.get_roles(project_roles=project_roles)
1327+ return [(role.role, role.role) for role in roles]
1328+
1329+@wrap_nova_error
1330+def get_members(project):
1331+ nova = get_nova_admin_connection()
1332+ members = nova.get_project_members(project)
1333+ return [str(user.memberId) for user in members]
1334+
1335+@wrap_nova_error
1336+def set_project_roles(projectname, username, roles):
1337+ nova = get_nova_admin_connection()
1338+ # hacky work around to interface correctly with multiple select form
1339+ _remove_roles(projectname, username)
1340+
1341+ for role in roles:
1342+ nova.add_user_role(username, str(role), projectname)
1343+
1344+def _remove_roles(project, username):
1345+ nova = get_nova_admin_connection()
1346+ userroles = nova.get_user_roles(username, project)
1347+ roles = [str(role.role) for role in userroles]
1348+
1349+ for role in roles:
1350+ if role == "developer":
1351+ nova.remove_user_role(username, "developer", project)
1352+ if role == "sysadmin":
1353+ nova.remove_user_role(username, "sysadmin", project)
1354+ if role == "netadmin":
1355+ nova.remove_user_role(username, "netadmin", project)
1356+
1357+
1358+class ProjectFormBase(forms.Form):
1359+ def __init__(self, project, *args, **kwargs):
1360+ self.project = project
1361+ super(ProjectFormBase, self).__init__(*args, **kwargs)
1362+
1363+
1364+class LaunchInstanceForm(forms.Form):
1365+ # nickname = forms.CharField()
1366+ # description = forms.CharField()
1367+
1368+ count = forms.ChoiceField(choices=[(x, x) for x in range(1, 6)])
1369+ size = forms.ChoiceField()
1370+ key_name = forms.ChoiceField()
1371+ #security_group = forms.ChoiceField()
1372+ user_data = forms.CharField(required=False, widget=forms.widgets.Textarea(attrs={'rows': 4}))
1373+
1374+ def __init__(self, project, *args, **kwargs):
1375+ forms.Form.__init__(self, *args, **kwargs)
1376+ #self.fields['security_group'].choices = get_security_group_choices(project)
1377+ self.fields['key_name'].choices = get_key_pair_choices(project)
1378+ self.fields['size'].choices = get_instance_type_choices()
1379+
1380+
1381+class UpdateInstanceForm(forms.Form):
1382+ nickname = forms.CharField(required=False, label="Name")
1383+ description = forms.CharField(required=False, widget=forms.Textarea, max_length=70)
1384+
1385+ def __init__(self, instance, *args, **kwargs):
1386+ forms.Form.__init__(self, *args, **kwargs)
1387+ self.fields['nickname'].initial = instance.displayName
1388+ self.fields['description'].initial = instance.displayDescription
1389+
1390+
1391+class UpdateImageForm(forms.Form):
1392+ nickname = forms.CharField(required=False, label="Name")
1393+ description = forms.CharField(required=False, widget=forms.Textarea, max_length=70)
1394+
1395+ def __init__(self, image, *args, **kwargs):
1396+ forms.Form.__init__(self, *args, **kwargs)
1397+ self.fields['nickname'].initial = image.displayName
1398+ self.fields['description'].initial = image.description
1399+
1400+
1401+class CreateKeyPairForm(ProjectFormBase):
1402+ name = forms.RegexField(regex=alphanumeric_re)
1403+
1404+ def clean_name(self):
1405+ name = self.cleaned_data['name']
1406+
1407+ if self.project.has_key_pair(name):
1408+ raise forms.ValidationError('A key named %s already exists.' % name)
1409+
1410+ return name
1411+
1412+
1413+class CreateSecurityGroupForm(ProjectFormBase):
1414+ name = forms.RegexField(regex=alphanumeric_re)
1415+ description = forms.CharField()
1416+
1417+ def clean_name(self):
1418+ name = self.cleaned_data['name']
1419+
1420+ if self.project.has_security_group(name):
1421+ raise forms.ValidationError('A security group named %s already exists.' % name)
1422+
1423+ return name
1424+
1425+
1426+class AuthorizeSecurityGroupRuleForm(forms.Form):
1427+ protocol = forms.ChoiceField(choices=get_protocols())
1428+ from_port = forms.IntegerField(min_value=1, max_value=65535)
1429+ to_port = forms.IntegerField(min_value=1, max_value=65535)
1430+
1431+
1432+class CreateVolumeForm(forms.Form):
1433+ size = forms.IntegerField(label='Size (in GB)', min_value=1, max_value=MAX_VOLUME_SIZE)
1434+ nickname = forms.CharField()
1435+ description = forms.CharField()
1436+
1437+
1438+class AttachVolumeForm(ProjectFormBase):
1439+ volume = forms.ChoiceField()
1440+ instance = forms.ChoiceField()
1441+ device = forms.CharField(initial='/dev/vdb')
1442+
1443+ def __init__(self, project, *args, **kwargs):
1444+ super(AttachVolumeForm, self).__init__(project, *args, **kwargs)
1445+ self.fields['volume'].choices = get_available_volume_choices(project)
1446+ self.fields['instance'].choices = get_instance_choices(project)
1447+
1448+
1449+class ProjectForm(forms.Form):
1450+ projectname = forms.CharField(label="Project Name", max_length=20)
1451+ description = forms.CharField(label="Description",
1452+ widget=forms.widgets.Textarea())
1453+ manager = forms.ModelChoiceField(queryset=auth_models.User.objects.all(),
1454+ label="Project Manager")
1455+
1456+
1457+class GlobalRolesForm(forms.Form):
1458+ role = forms.MultipleChoiceField(label='Roles', required=False)
1459+
1460+ def __init__(self, *args, **kwargs):
1461+ super(GlobalRolesForm, self).__init__(*args, **kwargs)
1462+ self.fields['role'].choices = get_roles(project_roles=False)
1463+
1464+
1465+class ProjectUserForm(forms.Form):
1466+ role = forms.MultipleChoiceField(label='Roles', required=False)
1467+
1468+ def __init__(self, project, user, *args, **kwargs):
1469+ super(ProjectUserForm, self).__init__(*args, **kwargs)
1470+ self.project = project
1471+ self.user = user
1472+ self.fields['role'].choices = get_roles()
1473+
1474+ def save(self):
1475+ set_project_roles(self.project.projectname,
1476+ self.user.username,
1477+ self.cleaned_data['role'])
1478+
1479+
1480+class AddProjectUserForm(forms.Form):
1481+ username = forms.ModelChoiceField(queryset='',
1482+ label='Username',
1483+ empty_label='Select a Username')
1484+ role = forms.MultipleChoiceField(label='Roles')
1485+
1486+ def __init__(self, *args, **kwargs):
1487+ project = kwargs.pop('project')
1488+ super(AddProjectUserForm, self).__init__(*args, **kwargs)
1489+ members = get_members(project)
1490+
1491+ self.fields['username'].queryset = \
1492+ auth_models.User.objects.exclude(username__in=members)
1493+ self.fields['role'].choices = get_roles()
1494+
1495+
1496+class SendCredentialsForm(forms.Form):
1497+ users = forms.MultipleChoiceField(label='Users', required=True)
1498+
1499+ def __init__(self, *args, **kwargs):
1500+ query_list = kwargs.pop('query_list')
1501+ super(SendCredentialsForm, self).__init__(*args, **kwargs)
1502+
1503+ self.fields['users'].choices = [(choices, choices) for choices in query_list]
1504+
1505
1506=== added directory 'django-nova/src/django_nova/management'
1507=== added file 'django-nova/src/django_nova/management/__init__.py'
1508=== added directory 'django-nova/src/django_nova/management/commands'
1509=== added file 'django-nova/src/django_nova/management/commands/__init__.py'
1510=== added file 'django-nova/src/django_nova/management/commands/createnovausers.py'
1511--- django-nova/src/django_nova/management/commands/createnovausers.py 1970-01-01 00:00:00 +0000
1512+++ django-nova/src/django_nova/management/commands/createnovausers.py 2011-03-04 07:54:59 +0000
1513@@ -0,0 +1,37 @@
1514+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1515+
1516+# Copyright 2010 United States Government as represented by the
1517+# Administrator of the National Aeronautics and Space Administration.
1518+# All Rights Reserved.
1519+#
1520+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1521+# not use this file except in compliance with the License. You may obtain
1522+# a copy of the License at
1523+#
1524+# http://www.apache.org/licenses/LICENSE-2.0
1525+#
1526+# Unless required by applicable law or agreed to in writing, software
1527+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1528+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1529+# License for the specific language governing permissions and limitations
1530+# under the License.
1531+"""
1532+Management commands for synchronizing the Django auth database and Nova
1533+users database.
1534+"""
1535+
1536+from django.core.management.base import NoArgsCommand
1537+from django.contrib.auth.models import User
1538+from django_nova.connection import get_nova_admin_connection
1539+
1540+class Command(NoArgsCommand):
1541+ help = 'Creates nova users for all users in the django auth database.'
1542+
1543+ def handle_noargs(self, **options):
1544+ nova = get_nova_admin_connection()
1545+ users = User.objects.all()
1546+ for user in users:
1547+ if not nova.has_user(user.username):
1548+ self.stdout.write('creating user %s... ' % user.username)
1549+ nova.create_user(user.username)
1550+ self.stdout.write('ok\n')
1551
1552=== added file 'django-nova/src/django_nova/manager.py'
1553--- django-nova/src/django_nova/manager.py 1970-01-01 00:00:00 +0000
1554+++ django-nova/src/django_nova/manager.py 2011-03-04 07:54:59 +0000
1555@@ -0,0 +1,340 @@
1556+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1557+
1558+# Copyright 2010 United States Government as represented by the
1559+# Administrator of the National Aeronautics and Space Administration.
1560+# All Rights Reserved.
1561+#
1562+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1563+# not use this file except in compliance with the License. You may obtain
1564+# a copy of the License at
1565+#
1566+# http://www.apache.org/licenses/LICENSE-2.0
1567+#
1568+# Unless required by applicable law or agreed to in writing, software
1569+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1570+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1571+# License for the specific language governing permissions and limitations
1572+# under the License.
1573+"""
1574+Simple API for interacting with Nova projects.
1575+"""
1576+
1577+import boto
1578+import boto.ec2.volume
1579+import boto.exception
1580+import boto.s3
1581+from django.conf import settings
1582+from django_nova.connection import get_nova_admin_connection
1583+from django_nova.exceptions import wrap_nova_error
1584+
1585+
1586+class ProjectManager(object):
1587+ def __init__(self, username, project, region):
1588+ self.username = username
1589+ self.projectname = project.projectname
1590+ self.projectManagerId = project.projectManagerId
1591+ self.region = region
1592+
1593+ def get_nova_connection(self):
1594+ """
1595+ Returns a boto connection for a user's project.
1596+ """
1597+ nova = get_nova_admin_connection()
1598+ return nova.connection_for(self.username,
1599+ self.projectname,
1600+ clc_url=self.region['endpoint'],
1601+ region=self.region['name'])
1602+
1603+ def get_zip(self):
1604+ """
1605+ Returns a buffer of a zip file containing signed credentials
1606+ for the project's Nova user.
1607+ """
1608+ nova = get_nova_admin_connection()
1609+ return nova.get_zip(self.username, self.projectname)
1610+
1611+ def get_images(self, image_ids=None):
1612+ conn = self.get_nova_connection()
1613+ images = conn.get_all_images(image_ids=image_ids)
1614+ sorted_images = [i for i in images if i.ownerId == self.username] + \
1615+ [i for i in images if i.ownerId != self.username]
1616+
1617+ return [i for i in sorted_images if i.type == 'machine' and i.location.split('/')[0] != 'nova']
1618+
1619+ def get_image(self, image_id):
1620+ try:
1621+ return self.get_images(image_ids=[image_id,])[0]
1622+ except IndexError:
1623+ return None
1624+
1625+ @wrap_nova_error
1626+ def deregister_image(self, image_id):
1627+ """
1628+ Removes the image's listing but leaves the image
1629+ and manifest in the object store in tact.
1630+ """
1631+ conn = self.get_nova_connection()
1632+ return conn.deregister_image(image_id)
1633+
1634+ @wrap_nova_error
1635+ def update_image(self, image_id, display_name=None, description=None):
1636+ conn = self.get_nova_connection()
1637+ params = {
1638+ 'ImageId': image_id,
1639+ 'DisplayName': display_name,
1640+ 'Description': description
1641+ }
1642+ return conn.get_object('UpdateImage', params, boto.ec2.image.Image)
1643+
1644+ @wrap_nova_error
1645+ def modify_image_attribute(self, image_id, attribute=None, operation=None,
1646+ groups='all'):
1647+ conn = self.get_nova_connection()
1648+ return conn.modify_image_attribute(image_id,
1649+ attribute='launchPermission',
1650+ operation='remove',
1651+ groups='all',)
1652+
1653+
1654+ @wrap_nova_error
1655+ def run_instances(self, image_id, **kwargs):
1656+ """
1657+ Runs instances of the specified image id.
1658+ """
1659+ conn = self.get_nova_connection()
1660+ return conn.run_instances(image_id, **kwargs)
1661+
1662+ def get_instance_count(self):
1663+ """
1664+ Returns the number of active instances in this project or None if unknown.
1665+ """
1666+ try:
1667+ return len(self.get_instances())
1668+ except:
1669+ return None
1670+
1671+ @wrap_nova_error
1672+ def get_instances(self):
1673+ """
1674+ Returns all instances in this project.
1675+ """
1676+ conn = self.get_nova_connection()
1677+ reservations = conn.get_all_instances()
1678+ instances = []
1679+ for reservation in reservations:
1680+ for instance in reservation.instances:
1681+ instances.append(instance)
1682+ return instances
1683+
1684+ @wrap_nova_error
1685+ def get_instance(self, instance_id):
1686+ """
1687+ Returns detail about the specified instance.
1688+ """
1689+ conn = self.get_nova_connection()
1690+ # TODO: Refactor this once nova's describe_instances filters by instance_id.
1691+ reservations = conn.get_all_instances()
1692+ for reservation in reservations:
1693+ for instance in reservation.instances:
1694+ if instance.id == instance_id:
1695+ return instance
1696+ return None
1697+
1698+ @wrap_nova_error
1699+ def update_instance(self, instance_id, updates):
1700+ conn = self.get_nova_connection()
1701+ params = {'InstanceId': instance_id, 'DisplayName': updates['nickname'],
1702+ 'DisplayDescription': updates['description']}
1703+ return conn.get_object('UpdateInstance', params,
1704+ boto.ec2.instance.Instance)
1705+
1706+ def get_instance_graph(self, region, instance_id, graph_name):
1707+ # TODO(devcamcar): Need better support for multiple regions.
1708+ # Need a way to get object store by region.
1709+ s3 = boto.s3.connection.S3Connection (
1710+ aws_access_key_id=settings.NOVA_ACCESS_KEY,
1711+ aws_secret_access_key=settings.NOVA_SECRET_KEY,
1712+ is_secure=False,
1713+ calling_format=boto.s3.connection.OrdinaryCallingFormat(),
1714+ port=3333,
1715+ host=settings.NOVA_CLC_IP
1716+ )
1717+ key = '_%s.monitor' % instance_id
1718+
1719+ try:
1720+ bucket = s3.get_bucket(key, validate=False)
1721+ except boto.exception.S3ResponseError, e:
1722+ if e.code == "NoSuchBucket":
1723+ return None
1724+ else:
1725+ raise e
1726+
1727+ key = bucket.get_key(graph_name)
1728+
1729+ return key.read()
1730+
1731+ @wrap_nova_error
1732+ def terminate_instance(self, instance_id):
1733+ """ Terminates the specified instance within this project. """
1734+ conn = self.get_nova_connection()
1735+ conn.terminate_instances([instance_id])
1736+
1737+ @wrap_nova_error
1738+ def get_security_groups(self):
1739+ """
1740+ Returns all security groups associated with this project.
1741+ """
1742+ conn = self.get_nova_connection()
1743+ groups = []
1744+
1745+ for g in conn.get_all_security_groups():
1746+ # Do not show vpn group.
1747+ #if g.name != 'vpn-secgroup':
1748+ groups.append(g)
1749+
1750+ return groups
1751+
1752+ @wrap_nova_error
1753+ def get_security_group(self, name):
1754+ """
1755+ Returns the specified security group for this project.
1756+ """
1757+ conn = self.get_nova_connection()
1758+
1759+ try:
1760+ return conn.get_all_security_groups(groupnames=name.encode('ASCII'))[0]
1761+ except IndexError:
1762+ return None
1763+
1764+ @wrap_nova_error
1765+ def has_security_group(self, name):
1766+ """
1767+ Indicates whether a security group with the specified name exists in this project.
1768+ """
1769+ return self.get_security_group(name) is not None
1770+
1771+ @wrap_nova_error
1772+ def create_security_group(self, name, description):
1773+ """
1774+ Creates a new security group in this project.
1775+ """
1776+ conn = self.get_nova_connection()
1777+ return conn.create_security_group(name, description)
1778+
1779+ @wrap_nova_error
1780+ def delete_security_group(self, name):
1781+ """
1782+ Deletes a security group from the project.
1783+ """
1784+ conn = self.get_nova_connection()
1785+ return conn.delete_security_group(name = name)
1786+
1787+ @wrap_nova_error
1788+ def authorize_security_group(self, group_name, ip_protocol, from_port, to_port):
1789+ """
1790+ Authorizes a rule for the specified security group.
1791+ """
1792+ conn = self.get_nova_connection()
1793+ return conn.authorize_security_group (
1794+ group_name = group_name,
1795+ ip_protocol = ip_protocol,
1796+ from_port = from_port,
1797+ to_port = to_port,
1798+ cidr_ip = '0.0.0.0/0'
1799+ )
1800+
1801+ @wrap_nova_error
1802+ def revoke_security_group(self, group_name, ip_protocol, from_port, to_port):
1803+ """
1804+ Revokes a rule for the specified security group.
1805+ """
1806+ conn = self.get_nova_connection()
1807+ return conn.revoke_security_group (
1808+ group_name = group_name,
1809+ ip_protocol = ip_protocol,
1810+ from_port = from_port,
1811+ to_port = to_port,
1812+ cidr_ip = '0.0.0.0/0'
1813+ )
1814+
1815+ @wrap_nova_error
1816+ def get_key_pairs(self):
1817+ """
1818+ Returns all key pairs associated with this project.
1819+ """
1820+ conn = self.get_nova_connection()
1821+ keys = []
1822+
1823+ for k in conn.get_all_key_pairs():
1824+ # Do not show vpn key.
1825+ if k.name != 'vpn-key':
1826+ keys.append(k)
1827+
1828+ return keys
1829+
1830+ @wrap_nova_error
1831+ def get_key_pair(self, name):
1832+ """
1833+ Returns the specified security group for this project.
1834+ """
1835+ conn = self.get_nova_connection()
1836+
1837+ try:
1838+ return conn.get_all_key_pairs(keynames=name.encode('ASCII'))[0]
1839+ except IndexError:
1840+ return None
1841+
1842+ @wrap_nova_error
1843+ def has_key_pair(self, name):
1844+ """
1845+ Indicates whether a key pair with the specified name exists in this project.
1846+ """
1847+ return self.get_key_pair(name) != None
1848+
1849+ @wrap_nova_error
1850+ def create_key_pair(self, name):
1851+ """
1852+ Creates a new key pair for this project.
1853+ """
1854+ conn = self.get_nova_connection()
1855+ return conn.create_key_pair(name)
1856+
1857+ @wrap_nova_error
1858+ def delete_key_pair(self, name):
1859+ """
1860+ Deletes a new key pair from this project.
1861+ """
1862+ conn = self.get_nova_connection()
1863+ conn.delete_key_pair(name)
1864+
1865+ @wrap_nova_error
1866+ def get_volumes(self):
1867+ """
1868+ Returns all volumes in this project.
1869+ """
1870+ conn = self.get_nova_connection()
1871+ return conn.get_all_volumes()
1872+
1873+ @wrap_nova_error
1874+ def create_volume(self, size, display_name=None, display_description=None,
1875+ snapshot=None):
1876+ conn = self.get_nova_connection()
1877+ params = {'Size': size, 'DisplayName': display_name,
1878+ 'DisplayDescription': display_description}
1879+ return conn.get_object('CreateVolume', params, boto.ec2.volume.Volume)
1880+
1881+ @wrap_nova_error
1882+ def delete_volume(self, volume_id):
1883+ conn = self.get_nova_connection()
1884+ return conn.delete_volume(volume_id)
1885+
1886+ @wrap_nova_error
1887+ def attach_volume(self, volume_id, instance_id, device):
1888+ conn = self.get_nova_connection()
1889+ return conn.attach_volume(volume_id, instance_id, device)
1890+
1891+ @wrap_nova_error
1892+ def detach_volume(self, volume_id):
1893+ conn = self.get_nova_connection()
1894+ return conn.detach_volume(volume_id)
1895+
1896
1897=== added file 'django-nova/src/django_nova/models.py'
1898--- django-nova/src/django_nova/models.py 1970-01-01 00:00:00 +0000
1899+++ django-nova/src/django_nova/models.py 2011-03-04 07:54:59 +0000
1900@@ -0,0 +1,121 @@
1901+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1902+
1903+# Copyright 2010 United States Government as represented by the
1904+# Administrator of the National Aeronautics and Space Administration.
1905+# All Rights Reserved.
1906+#
1907+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1908+# not use this file except in compliance with the License. You may obtain
1909+# a copy of the License at
1910+#
1911+# http://www.apache.org/licenses/LICENSE-2.0
1912+#
1913+# Unless required by applicable law or agreed to in writing, software
1914+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1915+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1916+# License for the specific language governing permissions and limitations
1917+# under the License.
1918+"""
1919+Database models for authorization credentials and synchronizing Nova users.
1920+"""
1921+
1922+import datetime
1923+import random
1924+import re
1925+import sha
1926+from django.conf import settings
1927+from django.contrib.auth import models as auth_models
1928+from django.contrib.sites import models as site_models
1929+from django.core import mail
1930+from django.db import models
1931+from django.db.models.signals import post_save
1932+from django.template.loader import render_to_string
1933+from django_nova.connection import get_nova_admin_connection
1934+
1935+
1936+SHA1_RE=re.compile('^[a-f0-9]{40}$')
1937+
1938+
1939+class CredentialsAuthorization(models.Model):
1940+ username = models.CharField(max_length=128)
1941+ project = models.CharField(max_length=128)
1942+ auth_token = models.CharField(max_length=40)
1943+ auth_date = models.DateTimeField(auto_now_add=True)
1944+
1945+ def __str__(self):
1946+ return '%s/%s:%s' % (self.username, self.project, self.auth_token)
1947+
1948+ @classmethod
1949+ def get_by_token(cls, token):
1950+ if SHA1_RE.search(token):
1951+ try:
1952+ credentials = cls.objects.get(auth_token=token)
1953+ except cls.DoesNotExist:
1954+ return None
1955+ if not credentials.auth_token_expired():
1956+ return credentials
1957+ return None
1958+
1959+ @classmethod
1960+ def authorize(cls, username, project):
1961+ return cls.objects.create(username=username,
1962+ project=project,
1963+ auth_token=cls.create_auth_token(username))
1964+
1965+ @staticmethod
1966+ def create_auth_token(username):
1967+ salt = sha.new(str(random.random())).hexdigest()[:5]
1968+ return sha.new(salt+username).hexdigest()
1969+
1970+ def auth_token_expired(self):
1971+ expiration_date = datetime.timedelta(days=int(settings.CREDENTIAL_AUTHORIZATION_DAYS))
1972+
1973+ return self.auth_date + expiration_date <= datetime.datetime.now()
1974+
1975+ def get_download_url(self):
1976+ return settings.CREDENTIAL_DOWNLOAD_URL + self.auth_token
1977+
1978+ def get_zip(self):
1979+ nova = get_nova_admin_connection()
1980+ self.delete()
1981+ return nova.get_zip(self.username, self.project)
1982+
1983+
1984+def credentials_post_save(sender, instance, created, *args, **kwargs):
1985+ """
1986+ Creates a Nova User when a new Django User is created.
1987+ """
1988+ if created:
1989+ site = site_models.Site.objects.get_current()
1990+ user = auth_models.User.objects.get(username=instance.username)
1991+ context = {
1992+ 'user': user,
1993+ 'download_url': instance.get_download_url(),
1994+ 'dashboard_url': 'http://%s/' % site.domain
1995+ }
1996+ subject = render_to_string('credentials/credentials_email_subject.txt')
1997+ body = render_to_string('credentials/credentials_email.txt', context)
1998+
1999+ message = mail.EmailMessage(subject=subject, body=body, to=[user.email])
2000+ message.send(fail_silently=False)
2001+post_save.connect(credentials_post_save,
2002+ CredentialsAuthorization,
2003+ dispatch_uid='django_nova.CredentialsAuthorization.post_save')
2004+
2005+
2006+def user_post_save(sender, instance, created, *args, **kwargs):
2007+ """
2008+ Creates a Nova User when a new Django User is created.
2009+ """
2010+
2011+ # NOTE(devcamcar): If running unit tests, don't use a real endpoint.
2012+ if settings.NOVA_DEFAULT_ENDPOINT == 'none':
2013+ return
2014+
2015+ if created:
2016+ nova = get_nova_admin_connection()
2017+ if not nova.has_user(instance.username):
2018+ nova.create_user(instance.username)
2019+post_save.connect(user_post_save,
2020+ auth_models.User,
2021+ dispatch_uid='django_nova.User.post_save')
2022
2023=== added file 'django-nova/src/django_nova/shortcuts.py'
2024--- django-nova/src/django_nova/shortcuts.py 1970-01-01 00:00:00 +0000
2025+++ django-nova/src/django_nova/shortcuts.py 2011-03-04 07:54:59 +0000
2026@@ -0,0 +1,131 @@
2027+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2028+
2029+# Copyright 2010 United States Government as represented by the
2030+# Administrator of the National Aeronautics and Space Administration.
2031+# All Rights Reserved.
2032+#
2033+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2034+# not use this file except in compliance with the License. You may obtain
2035+# a copy of the License at
2036+#
2037+# http://www.apache.org/licenses/LICENSE-2.0
2038+#
2039+# Unless required by applicable law or agreed to in writing, software
2040+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2041+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2042+# License for the specific language governing permissions and limitations
2043+# under the License.
2044+
2045+"""
2046+Helper methods for commonly used operations.
2047+"""
2048+
2049+from django.conf import settings
2050+from django.core.cache import cache
2051+from django.core.exceptions import PermissionDenied
2052+from django.http import Http404
2053+from django_nova import manager
2054+from django_nova.connection import get_nova_admin_connection
2055+from django_nova.exceptions import wrap_nova_error
2056+
2057+
2058+@wrap_nova_error
2059+def get_project_or_404(request, project_id):
2060+ """
2061+ Returns a project or 404s if it doesn't exist.
2062+ """
2063+
2064+ # Ensure that a connection is never attempted for a user that is unauthenticated.
2065+ if not request.user.is_authenticated:
2066+ raise PermissionDenied('User not authenticated')
2067+
2068+ nova = get_nova_admin_connection()
2069+ project = nova.get_project(project_id)
2070+ region = get_current_region(request)
2071+
2072+ if not project:
2073+ raise Http404('Project %s does not exist.' % project_id)
2074+
2075+ return manager.ProjectManager(request.user, project, region)
2076+
2077+
2078+@wrap_nova_error
2079+def get_projects(user):
2080+ """
2081+ Returns a list of projects for a user.
2082+ """
2083+ #key = 'projects.%s' % user
2084+ #projects = cache.get(key)
2085+
2086+ #if not projects:
2087+ # nova = get_nova_admin_connection()
2088+ # projects = nova.get_projects(user=user)
2089+ # cache.set(key, projects, 30)
2090+
2091+ #return projects
2092+ nova = get_nova_admin_connection()
2093+ return nova.get_projects(user=user)
2094+
2095+
2096+@wrap_nova_error
2097+def get_all_regions():
2098+ """
2099+ Returns a list of all regions.
2100+ """
2101+ regions = cache.get('regions')
2102+
2103+ if not regions:
2104+ nova = get_nova_admin_connection()
2105+ conn = nova.connection_for(settings.NOVA_ADMIN_USER, settings.NOVA_PROJECT)
2106+ results = conn.get_all_regions()
2107+ regions = [{'name': r.name, 'endpoint': r.endpoint} for r in results]
2108+ cache.set('regions', regions, 60 * 60 * 24)
2109+
2110+ return regions
2111+
2112+
2113+def get_region(region_name):
2114+ regions = get_all_regions()
2115+ try:
2116+ return [r for r in regions if r['name'] == region_name][0]
2117+ except IndexError:
2118+ return None
2119+
2120+
2121+def get_current_region(request):
2122+ """
2123+ Returns the currently selected region for a user.
2124+ """
2125+ region_name = request.session.get('region', settings.NOVA_DEFAULT_REGION)
2126+ return get_region(region_name)
2127+
2128+
2129+def set_current_region(request, region_name):
2130+ """
2131+ Sets the current region selection for a user.
2132+ """
2133+ request.session['region'] = region_name
2134+
2135+
2136+@wrap_nova_error
2137+def get_user_image_permissions(username, project_name):
2138+ """
2139+ Returns true if user is a sysadmin and can modify image attributes.
2140+ """
2141+ nova = get_nova_admin_connection()
2142+ user_has_modify_permissions = False
2143+
2144+ # checks global roles, if user is a sysadmin they can modify image attribtues.
2145+ if not user_has_modify_permissions:
2146+ for role in nova.get_user_roles(username):
2147+ if role.role == "sysadmin":
2148+ user_has_modify_permissions = True
2149+
2150+ # checks project roles, if user is a sysadmin they can modify image attribtues.
2151+ if not user_has_modify_permissions:
2152+ for role in nova.get_user_roles(username, project_name):
2153+ if role.role == "sysadmin":
2154+ user_has_modify_permissions = True
2155+
2156+ return user_has_modify_permissions
2157+
2158
2159=== added directory 'django-nova/src/django_nova/templates'
2160=== added directory 'django-nova/src/django_nova/templates/admin'
2161=== added directory 'django-nova/src/django_nova/templates/admin/django_nova'
2162=== added directory 'django-nova/src/django_nova/templates/admin/django_nova/project'
2163=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/add_project.html'
2164--- django-nova/src/django_nova/templates/admin/django_nova/project/add_project.html 1970-01-01 00:00:00 +0000
2165+++ django-nova/src/django_nova/templates/admin/django_nova/project/add_project.html 2011-03-04 07:54:59 +0000
2166@@ -0,0 +1,45 @@
2167+{% extends "admin/django_nova/project/base_projects.html" %}
2168+{% load admin_modify adminmedia %}
2169+
2170+{% block extrahead %}
2171+{{ block.super }}
2172+{{ media }}
2173+{% endblock %}
2174+
2175+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
2176+
2177+{% block coltype %}colMS{% endblock %}
2178+
2179+{% block bodyclass %} change-form{% endblock %}
2180+
2181+{% block breadcrumbs %}
2182+<div class="breadcrumbs">
2183+ <a href="/admin">Home</a> &rsaquo;
2184+ <a href="../../projects">Projects</a> &rsaquo;
2185+ Add Project
2186+</div>
2187+{% endblock %}
2188+
2189+{% block content %}
2190+<div id="content-main">
2191+ {% block object-tools %}
2192+ {% endblock %}
2193+ <form action="." method="post" enctype="multipart/form-data">
2194+ {% csrf_token %}
2195+ <fieldset class="module aligned {{ fieldset.classes }}">
2196+ {% for field in form.visible_fields %}
2197+ <div class="form-row">
2198+ {{ field.errors }}
2199+ {{ field.label_tag }}{{ field }}
2200+ {% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
2201+ </div>
2202+ {% endfor %}
2203+ {% for field in form.hidden_fields %}
2204+ {{ field }}
2205+ {% endfor %}
2206+
2207+ </fieldset>
2208+ <input type="submit" value="Save" />
2209+ </form>
2210+</div>
2211+{% endblock %}
2212
2213=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/add_project_user.html'
2214--- django-nova/src/django_nova/templates/admin/django_nova/project/add_project_user.html 1970-01-01 00:00:00 +0000
2215+++ django-nova/src/django_nova/templates/admin/django_nova/project/add_project_user.html 2011-03-04 07:54:59 +0000
2216@@ -0,0 +1,69 @@
2217+{% extends "admin/django_nova/project/base_projects.html" %}
2218+{% load admin_modify adminmedia %}
2219+
2220+{% block extrahead %}
2221+{{ block.super }}
2222+{{ media }}
2223+
2224+<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
2225+<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
2226+
2227+<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
2228+<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
2229+
2230+<script type="text/javascript" charset="utf-8">
2231+ django.jQuery(function(){
2232+ django.jQuery.each(django.jQuery(".edit_user_roles select[multiple]"), function () {
2233+ // "Locations" can be any label you want
2234+ SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
2235+ });
2236+ })
2237+</script>
2238+{% endblock %}
2239+
2240+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
2241+
2242+{% block coltype %}colMS{% endblock %}
2243+
2244+{% block bodyclass %} change-form{% endblock %}
2245+
2246+{% block breadcrumbs %}
2247+<div class="breadcrumbs">
2248+ <a href="/admin">Home</a> &rsaquo;
2249+ <a href="{% url admin_projects %}">Projects</a> &rsaquo;
2250+ <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
2251+ User
2252+ {{form.ProjectUserForm}}
2253+</div>
2254+{% endblock %}
2255+
2256+{% block content %}
2257+<div id="content-main">
2258+ {% block object-tools %}
2259+ {% endblock %}
2260+ <form class="edit_user_roles" action="." method="post" enctype="multipart/form-data">
2261+ {% csrf_token %}
2262+ <fieldset class="module aligned {{ fieldset.classes }}">
2263+ <input type="hidden" name="username" value="{{user.id}}" id="username" />
2264+ {% for field in form.visible_fields %}
2265+ <div class="form-row">
2266+ {{ field.errors }}
2267+ {{ field.label_tag }}{{ field }}
2268+ {% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
2269+ </div>
2270+ {% endfor %}
2271+ {% for field in form.hidden_fields %}
2272+ {{ field }}
2273+ {% endfor %}
2274+
2275+ </fieldset>
2276+ <div class="submit-row">
2277+ <p class="deletelink-box">
2278+ <a href="{% url admin_project_delete_user project.projectname user.username %}" class="deletelink">Delete</a>
2279+ </p>
2280+ <input type="submit" value="Save" class="default" />
2281+ </div>
2282+
2283+ </form>
2284+</div>
2285+{% endblock %}
2286
2287=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/base_projects.html'
2288--- django-nova/src/django_nova/templates/admin/django_nova/project/base_projects.html 1970-01-01 00:00:00 +0000
2289+++ django-nova/src/django_nova/templates/admin/django_nova/project/base_projects.html 2011-03-04 07:54:59 +0000
2290@@ -0,0 +1,16 @@
2291+{% extends "admin/change_list.html" %}
2292+
2293+{% block extrastyle %}
2294+ {{block.super}}
2295+ <link rel="stylesheet" type="text/css" href="{{settings.MEDIA_URL}}/stylesheets/extra_admin.css" />
2296+{% endblock %}
2297+{% block breadcrumbs %}<div class="breadcrumbs"><a href="/admin/">Home</a>&nbsp;&rsaquo;&nbsp;Projects</div>{% endblock %}
2298+{% block content %}
2299+ <div id="content-main">
2300+ <div class="module filtered" id="changelist">
2301+ <div id="toolbartable">
2302+ {% block innercontent %}{% endblock %}
2303+ </div>
2304+ </div>
2305+ </div>
2306+{% endblock %}
2307
2308=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/change_list.html'
2309--- django-nova/src/django_nova/templates/admin/django_nova/project/change_list.html 1970-01-01 00:00:00 +0000
2310+++ django-nova/src/django_nova/templates/admin/django_nova/project/change_list.html 2011-03-04 07:54:59 +0000
2311@@ -0,0 +1,3 @@
2312+{% extends "admin/change_list.html" %}
2313+{% load admin_extras %}
2314+{% block result_list %}{% project_result_list cl %}{% endblock %}
2315\ No newline at end of file
2316
2317=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/delete_project.html'
2318--- django-nova/src/django_nova/templates/admin/django_nova/project/delete_project.html 1970-01-01 00:00:00 +0000
2319+++ django-nova/src/django_nova/templates/admin/django_nova/project/delete_project.html 2011-03-04 07:54:59 +0000
2320@@ -0,0 +1,25 @@
2321+{% extends "admin/change_list.html" %}
2322+
2323+{% block extrastyle %}
2324+ {{block.super}}
2325+ <link rel="stylesheet" type="text/css" href="{{settings.MEDIA_URL}}/stylesheets/extra_admin.css" />
2326+{% endblock %}
2327+{% block breadcrumbs %}<div class="breadcrumbs"><a href="/admin/">Home</a>&nbsp;&rsaquo;&nbsp;<a href="/admin/projects">Projects</a>&nbsp;&rsaquo;&nbsp; <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a>&nbsp;&rsaquo;&nbsp;Delete</div>{% endblock %}
2328+{% block content %}
2329+ <div id="content-main">
2330+ <div class="module filtered" id="changelist">
2331+ <div id="toolbartable">
2332+ <h1>Delete Project</h1>
2333+ <p>Do you really want to delete this project?</p>
2334+ <ul>
2335+ <li><a href="{% url admin_project project.projectname %}">{{project.projectname}}</a></li>
2336+ </ul>
2337+
2338+ <form action="." method="post">
2339+ {% csrf_token %}
2340+ <p><input type="submit" value="Delete"></p>
2341+ </form>
2342+ </div>
2343+ </div>
2344+ </div>
2345+{% endblock %}
2346
2347=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/delete_project_user.html'
2348--- django-nova/src/django_nova/templates/admin/django_nova/project/delete_project_user.html 1970-01-01 00:00:00 +0000
2349+++ django-nova/src/django_nova/templates/admin/django_nova/project/delete_project_user.html 2011-03-04 07:54:59 +0000
2350@@ -0,0 +1,25 @@
2351+{% extends "admin/change_list.html" %}
2352+
2353+{% block extrastyle %}
2354+ {{block.super}}
2355+ <link rel="stylesheet" type="text/css" href="{{settings.MEDIA_URL}}/stylesheets/extra_admin.css" />
2356+{% endblock %}
2357+{% block breadcrumbs %}<div class="breadcrumbs"><a href="/admin/">Home</a>&nbsp;&rsaquo;&nbsp;<a href="/admin/projects">Projects</a>&nbsp;&rsaquo;&nbsp; <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a>&nbsp;&rsaquo;&nbsp;Delete</div>{% endblock %}
2358+{% block content %}
2359+ <div id="content-main">
2360+ <div class="module filtered" id="changelist">
2361+ <div id="toolbartable">
2362+ <h1>Remove User From Project</h1>
2363+ <p>Do you really want to remove this user from project?</p>
2364+ <ul>
2365+ <li><a href="{% url project_user project.projectname user.username %}">{{user.username}}</a> from <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a></li>
2366+ </ul>
2367+
2368+ <form action="." method="post">
2369+ {% csrf_token %}
2370+ <p><input type="submit" value="Delete"></p>
2371+ </form>
2372+ </div>
2373+ </div>
2374+ </div>
2375+{% endblock %}
2376
2377=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/edit_project.html'
2378--- django-nova/src/django_nova/templates/admin/django_nova/project/edit_project.html 1970-01-01 00:00:00 +0000
2379+++ django-nova/src/django_nova/templates/admin/django_nova/project/edit_project.html 2011-03-04 07:54:59 +0000
2380@@ -0,0 +1,95 @@
2381+{% extends "admin/django_nova/project/base_projects.html" %}
2382+{% load admin_modify adminmedia %}
2383+
2384+{% block extrahead %}
2385+{{ block.super }}
2386+{{ media }}
2387+{% endblock %}
2388+
2389+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
2390+
2391+{% block coltype %}colMS{% endblock %}
2392+
2393+{% block bodyclass %} change-form{% endblock %}
2394+
2395+{% block breadcrumbs %}
2396+<div class="breadcrumbs">
2397+ <a href="/admin">Home</a> &rsaquo;
2398+ <a href="{% url admin_projects %}">Projects</a> &rsaquo;
2399+ <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
2400+ Edit Project
2401+ {{form.ProjectEditForm}}
2402+</div>
2403+{% endblock %}
2404+
2405+{% block content %}
2406+<div id="content-main">
2407+ {% block object-tools %}
2408+ {% endblock %}
2409+ <form action="#" method="post" enctype="multipart/form-data">
2410+ {% csrf_token %}
2411+ <fieldset class="module aligned {{ fieldset.classes }}">
2412+ {% for field in form.visible_fields %}
2413+ <div class="form-row">
2414+ {{ field.errors }}
2415+ {{ field.label_tag }}{{ field }}
2416+ {% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
2417+ </div>
2418+ {% endfor %}
2419+ {% for field in form.hidden_fields %}
2420+ {{ field }}
2421+ {% endfor %}
2422+ <div class="form-row">
2423+ <label for="project_name">Project Name</label>
2424+ <span id="project_name" style="display: block; padding-top: 5px; padding-left: 5px; float: left;"><em>{{projectname}}</em></span>
2425+ </div>
2426+ <div class="form-row">
2427+ <label for="project_description">Description</label>
2428+ <span id="project_description" style="display: block; padding-top: 5px; padding-left: 5px; float: left;"><em>{{description}}</em></span>
2429+ </div>
2430+ <div class="form-row">
2431+ <label for="project_manager">Project Manager</label>
2432+ <span id="project_manager" style="display: block; padding-top: 5px; padding-left: 5px; float: left;"><em>{{manager}}</em></span>
2433+ </div>
2434+ </fieldset>
2435+ <div class="submit-row">
2436+ <p class="deletelink-box">
2437+ <a href="{% url delete_project project.projectname %}" class="deletelink">Delete Project</a>
2438+ </p>
2439+ {# <input type="submit" value="Save" class="default" /> #}
2440+ </div>
2441+
2442+ </form>
2443+
2444+
2445+ <table cellspacing="0" style="margin-top: 20px;">
2446+ <thead>
2447+ <tr>
2448+ <th>Username</th>
2449+ <th>Project Roles</th>
2450+ <th>Global Roles</th>
2451+ </tr>
2452+ </thead>
2453+ {% for user in users %}
2454+ <tr class="{% cycle 'row1' 'row2' %}">
2455+ <td>
2456+ <a href="{%url project_user project.projectname user.memberId %}">{{user.memberId}} {% if user.memberId == project.projectManagerId %}(<em>project manager</em>){%endif %}</a>
2457+
2458+ </td>
2459+ <td>
2460+ {{user.project_roles}}
2461+ </td>
2462+ <td>
2463+ {{user.global_roles}}
2464+ </td>
2465+ </tr>
2466+ {% endfor %}
2467+ </table>
2468+ <ul class="object-tools">
2469+ <li>
2470+ <a class="addlink" href="{% url add_project_user project.projectname %}">Add User</a>
2471+ </li>
2472+ </ul>
2473+
2474+</div>
2475+{% endblock %}
2476
2477=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/global_edit_user.html'
2478--- django-nova/src/django_nova/templates/admin/django_nova/project/global_edit_user.html 1970-01-01 00:00:00 +0000
2479+++ django-nova/src/django_nova/templates/admin/django_nova/project/global_edit_user.html 2011-03-04 07:54:59 +0000
2480@@ -0,0 +1,71 @@
2481+{% extends "admin/django_nova/project/base_projects.html" %}
2482+{% load admin_modify adminmedia %}
2483+
2484+{% block extrahead %}
2485+{{ block.super }}
2486+{{ media }}
2487+
2488+<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
2489+<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
2490+
2491+<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
2492+<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
2493+
2494+<script type="text/javascript" charset="utf-8">
2495+ django.jQuery(function(){
2496+ django.jQuery.each(django.jQuery("#global_users select"), function () {
2497+ // "Locations" can be any label you want
2498+ SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
2499+ });
2500+ })
2501+</script>
2502+{% endblock %}
2503+
2504+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
2505+
2506+{% block coltype %}colMS{% endblock %}
2507+
2508+{% block bodyclass %} change-form{% endblock %}
2509+
2510+{% block breadcrumbs %}
2511+<div class="breadcrumbs">
2512+ <a href="/admin">Home</a> &rsaquo;
2513+ <a href="{% url admin_users_list %}">Global Roles</a> &rsaquo;
2514+ {{user.username}}
2515+</div>
2516+{% endblock %}
2517+
2518+{% block content %}
2519+<div id="content-main">
2520+ {% block object-tools %}
2521+ {% endblock %}
2522+ <form action="." method="post" enctype="multipart/form-data" id="global_users">
2523+ {% csrf_token %}
2524+ <fieldset class="module aligned {{ fieldset.classes }}">
2525+ <div class="form-row">
2526+ <label for="id_username">Username</label>
2527+ <span>{{user.username}}</span>
2528+ </div>
2529+ <input type="hidden" name="username" value="{{user.id}}" id="username" />
2530+ {% for field in form.visible_fields %}
2531+ <div class="form-row">
2532+ {{ field.errors }}
2533+ {{ field.label_tag }}{{ field }}
2534+ {% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
2535+ </div>
2536+ {% endfor %}
2537+ {% for field in form.hidden_fields %}
2538+ {{ field }}
2539+ {% endfor %}
2540+
2541+ </fieldset>
2542+ <div class="submit-row">
2543+ <p class="deletelink-box">
2544+ {# <a href="{% url admin_project_delete_user project.projectname user.username %}" class="deletelink">Delete</a> #}
2545+ </p>
2546+ <input type="submit" value="Save" class="default" />
2547+ </div>
2548+
2549+ </form>
2550+</div>
2551+{% endblock %}
2552
2553=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/project_list.html'
2554--- django-nova/src/django_nova/templates/admin/django_nova/project/project_list.html 1970-01-01 00:00:00 +0000
2555+++ django-nova/src/django_nova/templates/admin/django_nova/project/project_list.html 2011-03-04 07:54:59 +0000
2556@@ -0,0 +1,42 @@
2557+{% extends "admin/django_nova/project/base_projects.html" %}
2558+{% block extrahead %}
2559+ {{ block.super }}
2560+{% endblock %}
2561+{% block innercontent %}
2562+ <ul class="object-tools">
2563+ <li>
2564+ <a class="addlink" href="{% url add_project %}">Add Project</a>
2565+ </li>
2566+ </ul>
2567+ <table cellspacing="0" style="margin-top: 20px;">
2568+ <thead>
2569+ <tr>
2570+ <th>Name</th>
2571+ <th>Description</th>
2572+ <th>Project Manager</th>
2573+ <th>Send Credentials</th>
2574+ <th>Start VPN</th>
2575+ </tr>
2576+ </thead>
2577+ {% for project in projects %}
2578+ <tr class="{% cycle 'row1' 'row2' %}">
2579+ <td>
2580+ <a href="{%url admin_project project.projectname %}">{{project.projectname}}</a>
2581+ </td>
2582+ <td>
2583+ {{project.description}}
2584+ </td>
2585+ <td>
2586+ {{project.projectManagerId}}
2587+ </td>
2588+ <td>
2589+ <a href="{% url admin_project_sendcredentials project.projectname %}">Send Credentials</a>
2590+ </td>
2591+ <td>
2592+ <a href="{% url admin_project_start_vpn project.projectname %}">Start VPN</a>
2593+ </td>
2594+
2595+ </tr>
2596+ {% endfor %}
2597+ </table>
2598+{% endblock %}
2599
2600=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/project_user.html'
2601--- django-nova/src/django_nova/templates/admin/django_nova/project/project_user.html 1970-01-01 00:00:00 +0000
2602+++ django-nova/src/django_nova/templates/admin/django_nova/project/project_user.html 2011-03-04 07:54:59 +0000
2603@@ -0,0 +1,76 @@
2604+{% extends "admin/django_nova/project/base_projects.html" %}
2605+{% load admin_modify adminmedia %}
2606+
2607+{% block extrahead %}
2608+{{ block.super }}
2609+{{ media }}
2610+
2611+<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
2612+<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
2613+
2614+<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
2615+<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
2616+
2617+<script type="text/javascript" charset="utf-8">
2618+ django.jQuery(function(){
2619+ django.jQuery.each(django.jQuery(".edit_user_roles select[multiple]"), function () {
2620+ // "Locations" can be any label you want
2621+ SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
2622+ });
2623+ })
2624+</script>
2625+{% endblock %}
2626+
2627+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
2628+
2629+{% block coltype %}colMS{% endblock %}
2630+
2631+{% block bodyclass %} change-form{% endblock %}
2632+
2633+{% block breadcrumbs %}
2634+<div class="breadcrumbs">
2635+ <a href="/admin">Home</a> &rsaquo;
2636+ <a href="{% url admin_projects %}">Projects</a> &rsaquo;
2637+ <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
2638+ User
2639+ {{form.ProjectUserForm}}
2640+</div>
2641+{% endblock %}
2642+
2643+{% block content %}
2644+<div id="content-main">
2645+ {% block object-tools %}
2646+ {% endblock %}
2647+ <form class="edit_user_roles" action="." method="post" enctype="multipart/form-data">
2648+ {% csrf_token %}
2649+ <fieldset class="module aligned {{ fieldset.classes }}">
2650+ <div class="form-row">
2651+ <label for="id_username">Username</label>
2652+ <span>{{user.username}}</span>
2653+ </div>
2654+ <input type="hidden" name="username" value="{{user.id}}" id="username" />
2655+ {% for field in form.visible_fields %}
2656+ <div class="form-row">
2657+ {{ field.errors }}
2658+ {{ field.label_tag }}{{ field }}
2659+ {% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
2660+ </div>
2661+ {% endfor %}
2662+ {% for field in form.hidden_fields %}
2663+ {{ field }}
2664+ {% endfor %}
2665+
2666+ </fieldset>
2667+ <div class="submit-row">
2668+ {% if project.projectManagerId != user.username %}
2669+ <p class="deletelink-box">
2670+ <a href="{% url admin_project_delete_user project.projectname user.username %}" class="deletelink">Remove User From Project</a>
2671+ </p>
2672+ {% endif %}
2673+
2674+ <input type="submit" value="Save" class="default" />
2675+ </div>
2676+
2677+ </form>
2678+</div>
2679+{% endblock %}
2680
2681=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/send_credentials.html'
2682--- django-nova/src/django_nova/templates/admin/django_nova/project/send_credentials.html 1970-01-01 00:00:00 +0000
2683+++ django-nova/src/django_nova/templates/admin/django_nova/project/send_credentials.html 2011-03-04 07:54:59 +0000
2684@@ -0,0 +1,87 @@
2685+{% extends "admin/base_site.html" %}
2686+{% load i18n admin_modify adminmedia %}
2687+
2688+{% block title %}Send project credentials{{ block.super }}{% endblock %}
2689+
2690+{% block extrahead %}
2691+{{ block.super }}
2692+{{ media }}
2693+
2694+<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
2695+<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
2696+
2697+<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
2698+<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
2699+
2700+<script type="text/javascript" charset="utf-8">
2701+ django.jQuery(function(){
2702+ django.jQuery.each(django.jQuery("#send_credentials select"), function () {
2703+ // "Locations" can be any label you want
2704+ SelectFilter.init(this.id, "Users", 0, "/media/admin/");
2705+ });
2706+ })
2707+</script>
2708+
2709+<style type="text/css" media="screen">
2710+ .errorlist, .successlist {background:#fcc;border:1px solid #c66;color:#600;list-style:none; padding: 10px 5px; margin: 25px 0 25px 0; float: left; width: 100%;}
2711+ .successlist {background: #CBFBD7; color: #1E5024; border-color: #6FBA5C;}
2712+</style>
2713+{% endblock %}
2714+
2715+
2716+{% block breadcrumbs %}
2717+<div class="breadcrumbs">
2718+ <a href="/admin">Home</a> &rsaquo;
2719+ <a href="{% url admin_projects %}">Projects</a> &rsaquo;
2720+ <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
2721+ Send Credentials
2722+</div>
2723+{% endblock %}
2724+
2725+{% block content %}
2726+<div id="content-main">
2727+
2728+
2729+ {% if not success %}
2730+ <h1>Send Credentials</h1>
2731+ <h3>Select which users you would like to send credentials to from the '{{ project.projectname }}' project.</h3>
2732+ {% else %}
2733+ <h1>Credentials sent successfully</h1>
2734+ {% endif %}
2735+
2736+ <div class="status">
2737+ {% if error %}
2738+ <span class="errorlist">{{ error }}</span>
2739+ {% endif %}
2740+
2741+ {% if success %}
2742+ <span class="successlist">{{ success }}</span>
2743+ {% endif %}
2744+
2745+ </div>
2746+
2747+ {% if not success %}
2748+ <form id="send_credentials" action="{% url admin_project_sendcredentials project.projectname %}" method="post">
2749+ {% csrf_token %}
2750+ <fieldset class="module aligned">
2751+
2752+ {% for field in form.visible_fields %}
2753+ <div class="form-row">
2754+ {{ field.errors }}
2755+ {{ field }}
2756+ {% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
2757+ </div>
2758+ {% endfor %}
2759+ {% for field in form.hidden_fields %}
2760+ {{ field }}
2761+ {% endfor %}
2762+
2763+ </fieldset>
2764+ <div class="submit-row">
2765+ <input style="margin-top:20px; margin-left:10px;" type="submit" value="Send Credentials" />
2766+ </div>
2767+
2768+ </form>
2769+ {% endif %}
2770+</div>
2771+{% endblock %}
2772
2773=== added file 'django-nova/src/django_nova/templates/admin/django_nova/project/user_list.html'
2774--- django-nova/src/django_nova/templates/admin/django_nova/project/user_list.html 1970-01-01 00:00:00 +0000
2775+++ django-nova/src/django_nova/templates/admin/django_nova/project/user_list.html 2011-03-04 07:54:59 +0000
2776@@ -0,0 +1,39 @@
2777+{% extends "admin/django_nova/project/base_projects.html" %}
2778+{% block extrahead %}
2779+ {{ block.super }}
2780+{% endblock %}
2781+
2782+{% block breadcrumbs %}
2783+<div class="breadcrumbs">
2784+ <a href="/admin">Home</a> &rsaquo;
2785+ Global Roles
2786+</div>
2787+{% endblock %}
2788+
2789+{% block innercontent %}
2790+ <h1>Select a User</h1>
2791+
2792+ <table cellspacing="0" style="margin-top: 20px;">
2793+ <thead>
2794+ <tr>
2795+ <th>Username</th>
2796+ <th>Global Roles</th>
2797+ <th>Actions</th>
2798+ </tr>
2799+ </thead>
2800+ {% for user in users %}
2801+ <tr class="{% cycle 'row1' 'row2' %}">
2802+ <td>
2803+ {{user.username}}
2804+ </td>
2805+ <td>
2806+ (temporarily hidden)
2807+ {#user.roles#}
2808+ </td>
2809+ <td>
2810+ <a href="{%url admin_user_roles user.username %}">Edit</a>
2811+ </td>
2812+ </tr>
2813+ {% endfor %}
2814+ </table>
2815+{% endblock %}
2816
2817=== added directory 'django-nova/src/django_nova/templates/django_nova'
2818=== added file 'django-nova/src/django_nova/templates/django_nova/_messages.html'
2819--- django-nova/src/django_nova/templates/django_nova/_messages.html 1970-01-01 00:00:00 +0000
2820+++ django-nova/src/django_nova/templates/django_nova/_messages.html 2011-03-04 07:54:59 +0000
2821@@ -0,0 +1,41 @@
2822+{% for message in messages %}
2823+ <div class="message ui-widget">
2824+ {% if message.tags == "info" %}
2825+ <div class="ui-state-highlight ui-corner-all">
2826+ <span class="close ui-icon ui-icon-circle-close"></span>
2827+ <p>
2828+ <span class="ui-icon ui-icon-info"></span>
2829+ {{ message }}
2830+ </p>
2831+ </div>
2832+ {% endif %}
2833+ {% if message.tags == "warning" %}
2834+ <div class="ui-state-highlight ui-corner-all" >
2835+ <span class="close ui-icon ui-icon-circle-close"></span>
2836+ <p>
2837+ <span class="ui-icon ui-icon-alert"></span>
2838+ {{ message }}
2839+ </p>
2840+ </div>
2841+ {% endif %}
2842+ {% if message.tags == "success" %}
2843+ <div class="ui-state-highlight ui-corner-all success" >
2844+ <span class="close ui-icon ui-icon-circle-close"></span>
2845+ <p>
2846+ <span class="ui-icon ui-icon-check"></span>
2847+ {{ message }}
2848+ </p>
2849+ </div>
2850+ {% endif %}
2851+ {% if message.tags == "error" %}
2852+ <div class="ui-state-error ui-corner-all" >
2853+ <span class="close ui-icon ui-icon-circle-close"></span>
2854+ <p>
2855+ <span class="ui-icon ui-icon-alert"></span>
2856+ {{ message }}
2857+ </p>
2858+ </div>
2859+ {% endif %}
2860+ </div>
2861+{% endfor %}
2862+
2863
2864=== added file 'django-nova/src/django_nova/templates/django_nova/base.html'
2865--- django-nova/src/django_nova/templates/django_nova/base.html 1970-01-01 00:00:00 +0000
2866+++ django-nova/src/django_nova/templates/django_nova/base.html 2011-03-04 07:54:59 +0000
2867@@ -0,0 +1,85 @@
2868+{% extends "base-sidebar.html" %}
2869+{% load region_tags %}
2870+{% load project_tags %}
2871+
2872+{% block headerjs %}
2873+{{ block.super }}
2874+{% endblock %}
2875+
2876+{% block region %}
2877+ <div id="region_selector">
2878+ {% load_regions %}
2879+ <span id="project_name"><strong>Project:</strong> {{ project.projectname }}</span>
2880+
2881+ <form id="frm_region" method="post" action="{% url region_change %}">
2882+ {% csrf_token %}
2883+ <fieldset>
2884+ <input name="redirect_url" type="hidden" value="{{ request.get_full_path }}" />
2885+ <noscript>
2886+ <input id="btn_region_change" type="submit" value="Change" />
2887+ </noscript>
2888+
2889+ <div id="region_form">
2890+ <label for="sel_region">Region: </label>
2891+ <select id="sel_region" name="region">
2892+ {% for region in regions %}
2893+ <option{% if region.name == current_region.name %} selected{% endif %}>
2894+ {{ region.name }}
2895+ </option>
2896+ {% endfor %}
2897+ </select>
2898+ </div>
2899+
2900+ </fieldset>
2901+ </form>
2902+ </div>
2903+{% endblock %}
2904+
2905+{% block nav_projects %}
2906+ {% load_projects %}
2907+ <li>
2908+ <h3 class="active"><a href="/">Projects</a></h3>
2909+ <div id="projects">
2910+ {% for p in projects %}
2911+ <div id="{{ p.projectname }}" class="project{% if p.projectname == project.projectname %} active{% endif %}">
2912+ <h4>
2913+ <a class="project_link" href="/project/{{ p.projectname }}">{{ p.projectname }}</a>
2914+ {% if p.projectManagerId == user.username %}
2915+ <a id="manage_project_{{p.projectname}}"
2916+ class="manage_link"
2917+ href="{% url nova_project_manage p.projectname %}"
2918+ title="Manage User Roles">Manage Project</a>
2919+ {% endif %}
2920+ </h4>
2921+ {% if project.projectname == p.projectname %}
2922+ <ul>
2923+ <li {% if p.projectname == project.projectname and sidebar_selected == "instances" %}class="active"{% endif %}>
2924+ <a id="lnk_instances_{{p.projectname}}" href="{% url nova_instances p.projectname %}">Instances</a>
2925+ </li>
2926+ <li {% if p.projectname == project.projectname and sidebar_selected == "images" %}class="active"{% endif %}>
2927+ <a id="lnk_images_{{p.projectname}}" href="{% url nova_images p.projectname %}">Images</a>
2928+ </li>
2929+ <li {% if p.projectname == project.projectname and sidebar_selected == "keys" %}class="active"{% endif %}>
2930+ <a id="lnk_keypairs_{{p.projectname}}" href="{% url nova_keypairs p.projectname %}">Keys</a>
2931+ </li>
2932+ <li {% if p.projectname == project.projectname and sidebar_selected == "volumes" %}class="active"{% endif %}>
2933+ <a id="lnk_volumes_{{p.projectname}}" href="{% url nova_volumes p.projectname %}">Volumes</a>
2934+ </li>
2935+ </ul>
2936+ {% endif %}
2937+ </div>
2938+ {% endfor %}
2939+ </div>
2940+ </li>
2941+{% endblock %}
2942+
2943+{% block footerjs %}
2944+{{ block.super }}
2945+<script type="text/javascript">
2946+ $(function() {
2947+ $('#sel_region').change(function() {
2948+ $('#frm_region').submit();
2949+ });
2950+ });
2951+</script>
2952+{% endblock %}
2953
2954=== added directory 'django-nova/src/django_nova/templates/django_nova/credentials'
2955=== added file 'django-nova/src/django_nova/templates/django_nova/credentials/expired.html'
2956--- django-nova/src/django_nova/templates/django_nova/credentials/expired.html 1970-01-01 00:00:00 +0000
2957+++ django-nova/src/django_nova/templates/django_nova/credentials/expired.html 2011-03-04 07:54:59 +0000
2958@@ -0,0 +1,17 @@
2959+{% load django_nova_tags %}
2960+<!DOCTYPE html>
2961+<html lang="en" xml:lang="en">
2962+ <head>
2963+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
2964+ <title>Expired Token</title>
2965+ </head>
2966+ <body>
2967+ <center>
2968+ <h1>The link you clicked has expired.</h1>
2969+ <p style="width:460px;">This credentials download link you have reached
2970+ is either invalid or has expired. Each link is only good for one use. If
2971+ you need to download your credentials again, please contact the
2972+ {% site_branding %} support team.</p>
2973+ </center>
2974+ </body>
2975+</html>
2976
2977=== added directory 'django-nova/src/django_nova/templates/django_nova/images'
2978=== added file 'django-nova/src/django_nova/templates/django_nova/images/_launch_form.html'
2979--- django-nova/src/django_nova/templates/django_nova/images/_launch_form.html 1970-01-01 00:00:00 +0000
2980+++ django-nova/src/django_nova/templates/django_nova/images/_launch_form.html 2011-03-04 07:54:59 +0000
2981@@ -0,0 +1,7 @@
2982+{% for field in form %}
2983+<div class="{% cycle 'odd' 'even'%}">
2984+ {{ field.label_tag }}
2985+ {% if field.errors %}{{ field.errors }}{% endif %}
2986+ {{ field }}
2987+</div>
2988+{% endfor %}
2989
2990=== added file 'django-nova/src/django_nova/templates/django_nova/images/_list.html'
2991--- django-nova/src/django_nova/templates/django_nova/images/_list.html 1970-01-01 00:00:00 +0000
2992+++ django-nova/src/django_nova/templates/django_nova/images/_list.html 2011-03-04 07:54:59 +0000
2993@@ -0,0 +1,112 @@
2994+ <h3 class="image_list_heading"> {{ heading }} </h3>
2995+ {% if images %}
2996+ <table id="image_launch">
2997+ <tr>
2998+ <th>ID</th>
2999+ <th>Description</th>
3000+ <th colspan="2">Owner</th>
3001+ </tr>
3002+ {% for image in images %}
3003+ {% if image.id == ami.id %}
3004+ <td class="detail_wrapper" colspan="4">
3005+ <div id="{{ ami.id }}" class="image_detail">
3006+ <div class="column">
3007+ <div class="image_detail_item">
3008+ <span class="label">Owner: </span>
3009+ <span class="data">{{ ami.ownerId }}</span>
3010+ </div>
3011+
3012+ <div class="image_detail_item">
3013+ <span class="label">Description: </span>
3014+ <span class="data">{{ ami.description }}</span>
3015+ </div>
3016+
3017+ <div class="image_detail_item">
3018+ <span class="label">Location: </span>
3019+ <span class="data">{{ ami.location }}</span>
3020+ </div>
3021+ </div>
3022+
3023+ <div class="column">
3024+ <div class="image_detail_item">
3025+ <span class="label">ID: </span>
3026+ <span class="data">{{ ami.id }}</span>
3027+ </div>
3028+ <div class="image_detail_item">
3029+ <span class="label">Name: </span>
3030+ <span class="data">{% if ami.displayName %}{{ ami.displayName }}{%else%}{{ ami.id }}{% endif %}</span>
3031+ </div>
3032+ <div class="image_detail_item">
3033+ <span class="label">Type: </span>
3034+ <span class="data">{{ ami.type }}</span>
3035+ </div>
3036+ <div class="image_detail_item">
3037+ <span class="label">Architecture: </span>
3038+ <span class="data">{{ ami.architecture }}</span>
3039+ </div>
3040+ </div>
3041+
3042+ <div id="last" class="column">
3043+ {% if ami.is_public %}
3044+ <div id="public" class="privacy">Public Image</div>
3045+ {% else %}
3046+ <div id="private" class="privacy">Private Image</div>
3047+ {% endif %}
3048+
3049+ <a id="launch_{{ image.id }}" class="launch" href="{% url nova_images_launch project.projectname image.id %}" title="Click to launch image">Launch</a>
3050+ {% if can_modify or user.username == ami.ownerId %}
3051+ <a id="edit_image_link" href="{% url nova_images_update project.projectname ami.id %}">Edit Image</a>
3052+ {% endif %}
3053+
3054+ </div>
3055+
3056+ {% if can_modify or user.username == ami.ownerId %}
3057+ <span class="image_privacy">
3058+ <form id="privacy_{{ ami.id }}" action="{% url nova_images_privacy project.projectname ami.id %}" method="post" accept-charset="utf-8">
3059+ {% csrf_token %}
3060+ {% if ami.is_public %}
3061+ <input class="private" type="submit" value="Make Private" />
3062+ {% else %}
3063+ <input class="public" type="submit" value="Make Public" />
3064+ {% endif %}
3065+ </form>
3066+ </span>
3067+
3068+ <span class="delete">
3069+ <form id="delete_{{ ami.id }}" action="{% url nova_images_remove project.projectname ami.id %}" method="post" accept-charset="utf-8">
3070+ {% csrf_token %}
3071+ <input type="submit" value="Remove Image" />
3072+ </form>
3073+ </span>
3074+ {% endif %}
3075+ </div>
3076+ </td>
3077+ {% else %}
3078+ <tr class="{% cycle 'odd' 'even' %}">
3079+ <td class="image_id">
3080+ <a href="{% url nova_images_detail project.projectname image.id %}">{% if image.displayName %}{{ image.displayName }}{%else%}{{ image.id }}{% endif %}</a>
3081+ </td>
3082+ <td class="image_location odd">
3083+ {% if image.description %}
3084+ {{ image.description }}
3085+ {% else %}
3086+ {{ image.location }}
3087+ {% endif %}
3088+ </td>
3089+ <td class="image_owner_id">{{ image.ownerId }}</td>
3090+ <td class="image_launch_btn odd"><a id="launch_{{ image.id }}" class="launch" href="{% url nova_images_launch project.projectname image.id %}">Launch</a></td>
3091+ {#<td class="odd"><a class="ui-state-default ui-corner-all" onclick="$('#dlg_launch').dialog('open');">Launch</a></td>#}
3092+ </tr>
3093+ {% endif %}
3094+ {% endfor %}
3095+ </table>
3096+ {% else %}
3097+ <div class="ui-widget">
3098+ <div class="ui-state-highlight ui-corner-all">
3099+ <p>
3100+ <span class="ui-icon ui-icon-info"></span>
3101+ No images currently available.
3102+ </p>
3103+ </div>
3104+ </div>
3105+ {% endif %}
3106
3107=== added file 'django-nova/src/django_nova/templates/django_nova/images/base.html'
3108--- django-nova/src/django_nova/templates/django_nova/images/base.html 1970-01-01 00:00:00 +0000
3109+++ django-nova/src/django_nova/templates/django_nova/images/base.html 2011-03-04 07:54:59 +0000
3110@@ -0,0 +1,7 @@
3111+{% extends "django_nova/base.html" %}
3112+{% load sidebar_tags %}
3113+
3114+{% block nav_projects %}
3115+ {% sidebar_select images %}
3116+ {{ block.super }}
3117+{% endblock %}
3118\ No newline at end of file
3119
3120=== added file 'django-nova/src/django_nova/templates/django_nova/images/detail_list.html'
3121--- django-nova/src/django_nova/templates/django_nova/images/detail_list.html 1970-01-01 00:00:00 +0000
3122+++ django-nova/src/django_nova/templates/django_nova/images/detail_list.html 2011-03-04 07:54:59 +0000
3123@@ -0,0 +1,207 @@
3124+{% extends "django_nova/images/base.html" %}
3125+
3126+{% block title %} - Launch an Image{% endblock %}
3127+
3128+{% block headerjs %}
3129+<script type="text/javascript" src="/media/django_nova/js/jquery.form.js"></script>
3130+{% endblock %}
3131+
3132+
3133+{% block content %}
3134+ <div id="right_content">
3135+ <div id="page_head">
3136+ <h2 id="page_heading">Images</h2>
3137+ <p id="page_description">Images are snapshots of running systems which can easily be deployed to run one or more instances.</p>
3138+ </div>
3139+
3140+ {% include "django_nova/_messages.html" %}
3141+
3142+ {% if images %}
3143+ <table id="image_launch">
3144+ <tr>
3145+ <th>ID</th>
3146+ <th>Description</th>
3147+ <th colspan="2">Owner</th>
3148+ </tr>
3149+ {% for image in images %}
3150+ <tr class="{% cycle 'odd' 'even' %}">
3151+ {% if image.id == ami.id %}
3152+ <td class="detail_wrapper" colspan="4">
3153+ <div id="{{ ami.id }}" class="image_detail">
3154+ <div class="column">
3155+ <div class="image_detail_item">
3156+ <span class="label">Owner: </span>
3157+ <span class="data">{{ ami.ownerId }}</span>
3158+ </div>
3159+
3160+ <div class="image_detail_item">
3161+ <span class="label">Description: </span>
3162+ <span class="data">{{ ami.description }}</span>
3163+ </div>
3164+
3165+ <div class="image_detail_item">
3166+ <span class="label">Location: </span>
3167+ <span class="data">{{ ami.location }}</span>
3168+ </div>
3169+ </div>
3170+
3171+ <div class="column">
3172+ <div class="image_detail_item">
3173+ <span class="label">ID: </span>
3174+ <span class="data">{{ ami.id }}</span>
3175+ </div>
3176+ <div class="image_detail_item">
3177+ <span class="label">Name: </span>
3178+ <span class="data">{% if ami.displayName %}{{ ami.displayName }}{%else%}{{ ami.id }}{% endif %}</span>
3179+ </div>
3180+ <div class="image_detail_item">
3181+ <span class="label">Type: </span>
3182+ <span class="data">{{ ami.type }}</span>
3183+ </div>
3184+ <div class="image_detail_item">
3185+ <span class="label">Architecture: </span>
3186+ <span class="data">{{ ami.architecture }}</span>
3187+ </div>
3188+ </div>
3189+
3190+ <div id="last" class="column">
3191+ {% if ami.is_public %}
3192+ <div id="public" class="privacy">Public Image</div>
3193+ {% else %}
3194+ <div id="private" class="privacy">Private Image</div>
3195+ {% endif %}
3196+
3197+ <a id="launch_{{ image.id }}" class="launch" href="{% url nova_images_launch project.projectname image.id %}" title="Click to launch image">Launch</a>
3198+ {% if can_modify or user.username == ami.ownerId %}
3199+ <a id="edit_image_link" href="{% url nova_images_update project.projectname ami.id %}">Edit Image</a>
3200+ {% endif %}
3201+
3202+ </div>
3203+
3204+ {% if can_modify or user.username == ami.ownerId %}
3205+ <span class="image_privacy">
3206+ <form id="privacy_{{ ami.id }}" action="{% url nova_images_privacy project.projectname ami.id %}" method="post" accept-charset="utf-8">
3207+ {% csrf_token %}
3208+ {% if ami.is_public %}
3209+ <input class="private" type="submit" value="Make Private" />
3210+ {% else %}
3211+ <input class="public" type="submit" value="Make Public" />
3212+ {% endif %}
3213+ </form>
3214+ </span>
3215+
3216+ <span class="delete">
3217+ <form id="delete_{{ ami.id }}" action="{% url nova_images_remove project.projectname ami.id %}" method="post" accept-charset="utf-8">
3218+ {% csrf_token %}
3219+ <input type="submit" value="Remove Image" />
3220+ </form>
3221+ </span>
3222+ {% endif %}
3223+ </div>
3224+ </td>
3225+ {% else %}
3226+ <td class="image_id"><a href="{% url nova_images_detail project.projectname image.id %}">{{ image.id }}</a></td>
3227+ <td class="image_location odd">
3228+ {% if image.description %}
3229+ {{ image.description }}
3230+ {% else %}
3231+ {{ image.location }}
3232+ {% endif %}
3233+ </td>
3234+ <td class="image_owner_id">{{ image.ownerId }}</td>
3235+ <td class="image_launch_btn odd"><a id="launch_{{ image.id }}" class="launch" href="{% url nova_images_launch project.projectname image.id %}">Launch</a></td>
3236+ {#<td class="odd"><a class="ui-state-default ui-corner-all" onclick="$('#dlg_launch').dialog('open');">Launch</a></td>#}
3237+ {% endif %}
3238+ </tr>
3239+ {% endfor %}
3240+ </table>
3241+ {% else %}
3242+ <div class="ui-widget">
3243+ <div class="ui-state-highlight ui-corner-all">
3244+ <p>
3245+ <span class="ui-icon ui-icon-info"></span>
3246+ No images currently available.
3247+ </p>
3248+ </div>
3249+ </div>
3250+ {% endif %}
3251+ </div>
3252+
3253+ <div id="dlg_launch" title="Launch Instance" style="display:none;">
3254+ <form id="frm_launch" action="url nova_images_launch project.projectname" method="post">
3255+ {% csrf_token %}
3256+ {% include "django_nova/images/_launch_form.html" %}
3257+ </form>
3258+ </div>
3259+
3260+ <div id="dlg_confirm" title="Confirm Termination">
3261+ <p>Are you sure you wish to unregister the <span id="ami_name"></span> image?</p>
3262+ </div>
3263+
3264+{% endblock %}
3265+
3266+{% block footerjs %}
3267+{{ block.super }}
3268+<script type="text/javascript">
3269+ var options = {
3270+ success: handleResponse,
3271+ beforeSubmit: showRequest,
3272+ dataType: 'json'
3273+ }
3274+
3275+ // TODO: On dialog open, reset form and validation.
3276+ $(function() {
3277+ $('#dlg_launch').dialog({
3278+ buttons: {
3279+ 'Ok': function() {
3280+ $('#frm_launch').ajaxSubmit(options);
3281+ },
3282+ 'Cancel': function() {
3283+ $(this).dialog('close');
3284+ }
3285+ },
3286+ autoOpen: false,
3287+ resizable: false,
3288+ width: 400,
3289+ height: 400
3290+ });
3291+ });
3292+
3293+ function showRequest(formData, jqForm, options) {
3294+ var queryString = $.param(formData);
3295+ alert('About to submit: \n\n' + queryString);
3296+ return true;
3297+ }
3298+
3299+ function handleResponse(data, statusText, xhr, $form) {
3300+ alert('status: ' + statusText + '\nsuccess:\n\n' + data.success);
3301+ }
3302+
3303+ $(function(){
3304+ $('.delete form').submit(function() {
3305+ ami_name = $(this).parent().parent().attr("id");
3306+ $('#ami_name').text(ami_name);
3307+ $('#dlg_confirm').dialog('open');
3308+ return false;
3309+ });
3310+
3311+ $('#dlg_confirm').dialog({
3312+ buttons: {
3313+ 'Ok': onConfirmOK,
3314+ 'Cancel': function() { $(this).dialog('close'); }
3315+ },
3316+ autoOpen: false,
3317+ resizable: false,
3318+ width: 500,
3319+ height: 200
3320+ });
3321+ })
3322+
3323+ function onConfirmOK() {
3324+ $(this).dialog('close');
3325+ form = document.getElementById('delete_' + ami_name);
3326+ if(form) form.submit();
3327+ }
3328+
3329+</script>
3330+{% endblock %}
3331
3332=== added file 'django-nova/src/django_nova/templates/django_nova/images/edit.html'
3333--- django-nova/src/django_nova/templates/django_nova/images/edit.html 1970-01-01 00:00:00 +0000
3334+++ django-nova/src/django_nova/templates/django_nova/images/edit.html 2011-03-04 07:54:59 +0000
3335@@ -0,0 +1,35 @@
3336+{% extends "django_nova/images/base.html" %}
3337+
3338+{% block title %} - Cloud Computing{% endblock %}
3339+
3340+{% block headerjs %}
3341+<script type="text/javascript" src="{{ COMMON_MEDIA_PREFIX }}js/jquery.form.js"></script>
3342+{% endblock %}
3343+
3344+{% block content %}
3345+<div id="right_content">
3346+ <div id="page_head">
3347+ <h2 id="page_heading">Edit Image</h2>
3348+ <p id="page_description">From this page you can edit the name and description of an image that belongs to you.</p>
3349+ </div>
3350+
3351+ <div class="dash_block first">
3352+ <h3 class="image_id">Edit Image: {{ ami.id }}</h3>
3353+
3354+ <form class="edit_image" id="rename_{{ ami.id }}" action="{% url nova_images_update project.projectname ami.id %}" method="post">
3355+ <fieldset>
3356+ {% csrf_token %}
3357+ {% for field in form %}
3358+
3359+ {{ field.label_tag }}
3360+ {% if field.errors %}{{ field.errors }}{% endif %}
3361+ {{ field }}
3362+ {% endfor %}
3363+ <input type="submit" value="Update Image" />
3364+ </fieldset>
3365+ </form>
3366+
3367+ </div>
3368+</div>
3369+
3370+{% endblock %}
3371
3372=== added file 'django-nova/src/django_nova/templates/django_nova/images/index.html'
3373--- django-nova/src/django_nova/templates/django_nova/images/index.html 1970-01-01 00:00:00 +0000
3374+++ django-nova/src/django_nova/templates/django_nova/images/index.html 2011-03-04 07:54:59 +0000
3375@@ -0,0 +1,70 @@
3376+{% extends "django_nova/images/base.html" %}
3377+
3378+{% block title %} - Launch an Image{% endblock %}
3379+
3380+{% block headerjs %}
3381+<script type="text/javascript" src="/media/django_nova/js/jquery.form.js"></script>
3382+{% endblock %}
3383+
3384+
3385+{% block content %}
3386+ <div id="right_content">
3387+ <div id="page_head">
3388+ <h2 id="page_heading">Images</h2>
3389+ <p id="page_description">Images are snapshots of running systems which can easily be deployed to run one or more instances.</p>
3390+ </div>
3391+
3392+ {% include "django_nova/_messages.html" %}
3393+
3394+ {% for heading, images in image_lists.items %}
3395+ {% include "django_nova/images/_list.html" %}
3396+ {% endfor %}
3397+
3398+ </div>
3399+
3400+ <div id="dlg_launch" title="Launch Instance" style="display:none;">
3401+ <form id="frm_launch" action="#" method="post">
3402+ {% csrf_token %}
3403+ {% include "django_nova/images/_launch_form.html" %}
3404+ </form>
3405+ </div>
3406+{% endblock %}
3407+
3408+{% block footerjs %}
3409+{{ block.super }}
3410+<script type="text/javascript">
3411+ var options = {
3412+ success: handleResponse,
3413+ beforeSubmit: showRequest,
3414+ dataType: 'json'
3415+ }
3416+
3417+ // TODO: On dialog open, reset form and validation.
3418+ $(function() {
3419+ $('#dlg_launch').dialog({
3420+ buttons: {
3421+ 'Ok': function() {
3422+ $('#frm_launch').ajaxSubmit(options);
3423+ },
3424+ 'Cancel': function() {
3425+ $(this).dialog('close');
3426+ }
3427+ },
3428+ autoOpen: false,
3429+ resizable: false,
3430+ width: 400,
3431+ height: 400
3432+ });
3433+ });
3434+
3435+ function showRequest(formData, jqForm, options) {
3436+ var queryString = $.param(formData);
3437+ alert('About to submit: \n\n' + queryString);
3438+ return true;
3439+ }
3440+
3441+ function handleResponse(data, statusText, xhr, $form) {
3442+ alert('status: ' + statusText + '\nsuccess:\n\n' + data.success);
3443+ }
3444+</script>
3445+{% endblock %}
3446
3447=== added file 'django-nova/src/django_nova/templates/django_nova/images/launch.html'
3448--- django-nova/src/django_nova/templates/django_nova/images/launch.html 1970-01-01 00:00:00 +0000
3449+++ django-nova/src/django_nova/templates/django_nova/images/launch.html 2011-03-04 07:54:59 +0000
3450@@ -0,0 +1,32 @@
3451+{% extends "django_nova/images/base.html" %}
3452+
3453+{% block title %} - Cloud Computing{% endblock %}
3454+
3455+{% block headerjs %}
3456+<script type="text/javascript" src="{{ COMMON_MEDIA_PREFIX }}js/jquery.form.js"></script>
3457+{% endblock %}
3458+
3459+{% block content %}
3460+<div id="right_content">
3461+ <div id="page_head">
3462+ <h2 id="page_heading">Launch Image</h2>
3463+ <p id="page_description">You can launch up to five instances of an image at a time. Some images allow for custom configuration to be passed in via User data. (<a href="/kb/show/UserData/">read more</a>)</p>
3464+ </div>
3465+
3466+ <div class="dash_block first">
3467+ <form id="frm_launch" action="{% url nova_images_launch project.projectname ami.id %}" method="post">
3468+ {% csrf_token %}
3469+ <fieldset>
3470+ <h3 class="image_id">Launch Image {{ ami.id }}</h3>
3471+ <div class="even">
3472+ <label>Location</label>
3473+ <span class="image_location">{{ ami.location }}</span>
3474+ </div>
3475+ {% include "django_nova/images/_launch_form.html" %}
3476+ <input id="launch_image" type="submit" value="Launch" />
3477+ </fieldset>
3478+ </form>
3479+ </div>
3480+</div>
3481+
3482+{% endblock %}
3483
3484=== added directory 'django-nova/src/django_nova/templates/django_nova/instances'
3485=== added file 'django-nova/src/django_nova/templates/django_nova/instances/_instances_list.html'
3486--- django-nova/src/django_nova/templates/django_nova/instances/_instances_list.html 1970-01-01 00:00:00 +0000
3487+++ django-nova/src/django_nova/templates/django_nova/instances/_instances_list.html 2011-03-04 07:54:59 +0000
3488@@ -0,0 +1,104 @@
3489+ {% if instances %}
3490+ <table style="width: 100%">
3491+ <tr>
3492+ <th>ID</th>
3493+ <th>Image</th>
3494+ <th>Size</th>
3495+ <th>IP</th>
3496+ <th colspan="2">State</th>
3497+ </tr>
3498+ {% for instance in instances %}
3499+ <tr class="{% cycle 'odd' 'even' %}">
3500+ {% if instance.id == selected_instance.id %}
3501+ <td class="detail_wrapper" colspan="6">
3502+ <div id="{{selected_instance.id}}" class="instance_detail">
3503+ <div class="column">
3504+ <div class="instance_detail_item">
3505+ <span class="label">Instance ID: </span>
3506+ <span class="data">{{ selected_instance.id }}</span>
3507+ </div>
3508+ <div class="instance_detail_item">
3509+ <span class="label">Name: </span>
3510+ <span class="data">{% if selected_instance.displayName != "" %}{{ selected_instance.displayName }}{% else %} None {% endif %}</span>
3511+ </div>
3512+ <div class="instance_detail_item">
3513+ <span class="label">Description: </span>
3514+ <span class="data" id="desc">{{ selected_instance.displayDescription }}</span>
3515+ </div>
3516+ </div>
3517+
3518+ <div class="column">
3519+ <div class="instance_detail_item">
3520+ <span class="label">Region: </span>
3521+ <span class="data">{{ selected_instance.region.name }}</span>
3522+ </div>
3523+ <div class="instance_detail_item">
3524+ <span class="label">Size: </span>
3525+ <span class="data">{{ selected_instance.instance_type }}</span>
3526+ </div>
3527+ <div class="instance_detail_item">
3528+ <span class="label">State: </span>
3529+ <span class="data">{{ selected_instance.state }}</span>
3530+ </div>
3531+ <div class="instance_detail_item">
3532+ <span class="label">Image ID: </span>
3533+ <span class="data">{{ selected_instance.image_id }}</span>
3534+ </div>
3535+ <div class="instance_detail_item">
3536+ <span class="label">IP Address: </span>
3537+ <span class="data">{{ selected_instance.dns_name }}</span>
3538+ </div>
3539+
3540+ </div>
3541+
3542+ <div id="last" class="column">
3543+ {% if instance.state == "running" %}
3544+ <a href="{% url nova_instances_console project.projectname instance.id %}" id="console_{{instance.id}}" class="console" target="_blank">Show Console</a>{% endif %}
3545+ <a id="edit_instance_link" href="{% url nova_instance_update project.projectname instance.id %}">Edit Instance</a>
3546+
3547+
3548+ </div>
3549+
3550+ <span class="delete">
3551+ <form id="form_terminate_{{ instance.id }}" class="form-terminate" method="post" action="{% url nova_instances_terminate project.projectname %}" >
3552+ <input name="instance_id" type="hidden" value="{{ instance.id }}" />
3553+ <input id="terminate_{{instance.id}}" class="terminate" type="submit" value="Terminate" />
3554+ {% csrf_token %}
3555+ </form>
3556+ </span>
3557+
3558+ </div>
3559+
3560+ </td>
3561+ {% else %}
3562+
3563+ <td><a href="{% url nova_instances_detail project.projectname instance.id %}">{{ instance.id }} {% if instance.displayName %}({{ instance.displayName }}){% endif %}
3564+ </a></td>
3565+ <td class="odd">{{ instance.image_id }}</td>
3566+ <td>{{ instance.instance_type }}</td>
3567+ <td class="odd">{{ instance.dns_name }}</td>
3568+ <td>{{ instance.state }}</td>
3569+ <td id="actions" class="odd">
3570+ <form id="form_terminate_{{ instance.id }}" class="form-terminate" method="post" action="{% url nova_instances_terminate project.projectname %}" >
3571+ <input name="instance_id" type="hidden" value="{{ instance.id }}" />
3572+ <input id="terminate_{{instance.id}}" class="terminate" type="submit" value="Terminate" />
3573+ {% csrf_token %}
3574+ </form>
3575+ {% if instance.state == "running" %}
3576+ <a href="{% url nova_instances_console project.projectname instance.id %}" id="console_{{instance.id}}" class="console" target="_blank">Show Console</a>
3577+ {% endif %}
3578+ </td>
3579+ {% endif %}
3580+ </tr>
3581+ {% endfor %}
3582+ </table>
3583+ {% else %}
3584+ <div class="ui-widget">
3585+ <div class="ui-state-highlight ui-corner-all">
3586+ <p>
3587+ <span class="ui-icon ui-icon-info"></span>
3588+ No instances are currently running. You may start a new instance from the <a href="{% url nova_images project.projectname %}">images</a> tab.
3589+ </p>
3590+ </div>
3591+ </div>
3592+ {% endif %}
3593
3594=== added file 'django-nova/src/django_nova/templates/django_nova/instances/base.html'
3595--- django-nova/src/django_nova/templates/django_nova/instances/base.html 1970-01-01 00:00:00 +0000
3596+++ django-nova/src/django_nova/templates/django_nova/instances/base.html 2011-03-04 07:54:59 +0000
3597@@ -0,0 +1,7 @@
3598+{% extends "django_nova/base.html" %}
3599+{% load sidebar_tags %}
3600+
3601+{% block nav_projects %}
3602+ {% sidebar_select instances %}
3603+ {{ block.super }}
3604+{% endblock %}
3605\ No newline at end of file
3606
3607=== added file 'django-nova/src/django_nova/templates/django_nova/instances/detail_list.html'
3608--- django-nova/src/django_nova/templates/django_nova/instances/detail_list.html 1970-01-01 00:00:00 +0000
3609+++ django-nova/src/django_nova/templates/django_nova/instances/detail_list.html 2011-03-04 07:54:59 +0000
3610@@ -0,0 +1,33 @@
3611+{% extends "django_nova/instances/base.html" %}
3612+
3613+{% block title %} - Cloud Computing{% endblock %}
3614+
3615+{% block content %}
3616+
3617+<div id="right_content">
3618+ <div id="page_head">
3619+ <h2 id="page_heading">Instance ID: {{ instance.id }}</h2>
3620+ <p id="page_description">Here you can see up to the minute performance data about your instance.</p>
3621+ </div>
3622+
3623+ {% include "django_nova/_messages.html" %}
3624+
3625+
3626+ <div class="dash_block first">
3627+ <h3 class="image_id">Edit Instance: {{ instance.id }}</h3>
3628+
3629+ <form class="edit_instance" id="rename_{{ instance.id }}" action="{% url nova_instance_update project.projectname instance.id %}" method="post">
3630+ <fieldset>
3631+ {% csrf_token %}
3632+ {% for field in form %}
3633+
3634+ {{ field.label_tag }}
3635+ {% if field.errors %}{{ field.errors }}{% endif %}
3636+ {{ field }}
3637+ {% endfor %}
3638+ <input type="submit" value="Update Instance" />
3639+ </fieldset>
3640+ </form>
3641+ </div>
3642+
3643+{% endblock %}
3644
3645=== added file 'django-nova/src/django_nova/templates/django_nova/instances/edit.html'
3646--- django-nova/src/django_nova/templates/django_nova/instances/edit.html 1970-01-01 00:00:00 +0000
3647+++ django-nova/src/django_nova/templates/django_nova/instances/edit.html 2011-03-04 07:54:59 +0000
3648@@ -0,0 +1,34 @@
3649+{% extends "django_nova/instances/base.html" %}
3650+
3651+{% block title %} - Cloud Computing{% endblock %}
3652+
3653+{% block headerjs %}
3654+<script type="text/javascript" src="{{ COMMON_MEDIA_PREFIX }}js/jquery.form.js"></script>
3655+{% endblock %}
3656+
3657+{% block content %}
3658+<div id="right_content">
3659+ <div id="page_head">
3660+ <h2 id="page_heading">Edit Instance</h2>
3661+ <p id="page_description">From this page you can give your instance an alias, so you don't have to remember its unique id.</p>
3662+ </div>
3663+
3664+ <div class="dash_block first">
3665+ <h3 class="image_id">Edit Instance: {{ instance.id }}</h3>
3666+
3667+ <form class="edit_instance" id="rename_{{ selected_instance.id }}" action="{% url nova_instance_update project.projectname instance.id %}" method="post">
3668+ <fieldset>
3669+ {% csrf_token %}
3670+ {% for field in update_form %}
3671+
3672+ {{ field.label_tag }}
3673+ {% if field.errors %}{{ field.errors }}{% endif %}
3674+ {{ field }}
3675+ {% endfor %}
3676+ <input type="submit" value="Update Instance" />
3677+ </fieldset>
3678+ </form>
3679+ </div>
3680+</div>
3681+
3682+{% endblock %}
3683
3684=== added file 'django-nova/src/django_nova/templates/django_nova/instances/index.html'
3685--- django-nova/src/django_nova/templates/django_nova/instances/index.html 1970-01-01 00:00:00 +0000
3686+++ django-nova/src/django_nova/templates/django_nova/instances/index.html 2011-03-04 07:54:59 +0000
3687@@ -0,0 +1,98 @@
3688+{% extends "django_nova/instances/base.html" %}
3689+
3690+{% block title %} - {{ project.projectname|capfirst }} Instances{% endblock %}
3691+{% block pageclass %}instances{% endblock %}
3692+
3693+{% block content %}
3694+ <div id="page_head">
3695+ <div id="spinner"></div>
3696+
3697+ <h2 id="page_heading">Instances</h2>
3698+ <p id="page_description">Instances are virtual servers launched from images. You can launch instances from the <a href="{% url nova_images project.projectname %}">images tab</a>.</p>
3699+ </div>
3700+
3701+ {% include "django_nova/_messages.html" %}
3702+
3703+ <div id="instances">
3704+ {% include "django_nova/instances/_instances_list.html" %}
3705+ </div>
3706+
3707+ <div id="dlg_confirm" title="Confirm Termination" style="display:none;">
3708+ <p>Are you sure you wish to terminate instance <span id="spn_terminate"></span>?</p>
3709+ </div>
3710+
3711+ <div id="connection_error" style="display:none;" title="Connection Error">
3712+ <p><span class="ui-icon ui-icon-alert"></span>A connection error has occurred. Please ensure you are still connected to VPN.</p>
3713+ </div>
3714+{% endblock %}
3715+
3716+{% block footerjs %}
3717+{{ block.super }}
3718+<script type="text/javascript">
3719+
3720+ $(function() {
3721+ setInterval(function() {
3722+ $('#spinner').show();
3723+ {% if detail %}
3724+ $('#instances').load('{% url nova_instances_refresh_detail project.projectname selected_instance.id %}', onInstancesUpdated);
3725+ {% else %}
3726+ $('#instances').load('{% url nova_instances_refresh project.projectname %}', onInstancesUpdated);
3727+ {% endif %}
3728+ }, 15000);
3729+
3730+ initInstanceForms();
3731+
3732+ $('#dlg_confirm').dialog({
3733+ buttons: {
3734+ 'Ok': onConfirmOK,
3735+ 'Cancel': function() { $(this).dialog('close'); }
3736+ },
3737+ autoOpen: false,
3738+ resizable: false,
3739+ width: 500,
3740+ height: 200
3741+ });
3742+ });
3743+
3744+ var _terminateID = null;
3745+
3746+ function initInstanceForms() {
3747+ $('.form-terminate').submit(function() {
3748+ _terminateID = $(this).children(':first').val()
3749+ $('#spn_terminate').text(_terminateID);
3750+ $('#dlg_confirm').dialog('open');
3751+ return false;
3752+ });
3753+ }
3754+
3755+ function onInstancesUpdated(response, status, xhr) {
3756+ $('#spinner').hide();
3757+
3758+ switch(xhr.status) {
3759+ case 200:
3760+ initInstanceForms();
3761+ break;
3762+
3763+ case 403:
3764+ document.location = '{% url auth_login %}';
3765+ break;
3766+
3767+ default:
3768+ $('#connection_error').dialog({
3769+ dialogClass: 'alert',
3770+ modal: true,
3771+ closeOnEscape: true,
3772+ buttons:{ "Close": function() { $(this).dialog("close"); } },
3773+ });
3774+ $('#connection_error').dialog('open');
3775+ break;
3776+ }
3777+ }
3778+
3779+ function onConfirmOK() {
3780+ $(this).dialog('close');
3781+ form = document.getElementById('form_terminate_' + _terminateID);
3782+ if(form) form.submit();
3783+ }
3784+</script>
3785+{% endblock %}
3786
3787=== added file 'django-nova/src/django_nova/templates/django_nova/instances/performance.html'
3788--- django-nova/src/django_nova/templates/django_nova/instances/performance.html 1970-01-01 00:00:00 +0000
3789+++ django-nova/src/django_nova/templates/django_nova/instances/performance.html 2011-03-04 07:54:59 +0000
3790@@ -0,0 +1,58 @@
3791+{% extends "django_nova/instances/base.html" %}
3792+
3793+{% block title %} - Cloud Computing{% endblock %}
3794+
3795+{% block content %}
3796+
3797+<div id="right_content">
3798+ <div id="page_head">
3799+ <h2 id="page_heading">Instance ID: {{ instance.id }} Performance</h2>
3800+ <p id="page_description">Here you can see up to the minute performance data about your instance.</p>
3801+ </div>
3802+
3803+ {% include "django_nova/_messages.html" %}
3804+
3805+ <p>
3806+ <h1>CPU Usage</h1>
3807+ <h3>Today</h3>
3808+ <img src="{% url nova_instances_graph project.projectname instance.id "cpu-1d.png" %}" />
3809+ </p>
3810+ <p>
3811+ <h3>This Week</h3>
3812+ <img src="{% url nova_instances_graph project.projectname instance.id "cpu-1w.png" %}" />
3813+ </p>
3814+ <p>
3815+ <h3>This Month</h3>
3816+ <img src="{% url nova_instances_graph project.projectname instance.id "cpu-1m.png" %}" />
3817+ </p>
3818+
3819+ <p>
3820+ <h1>Network Activity</h1>
3821+ <h3>Today</h3>
3822+ <img src="{% url nova_instances_graph project.projectname instance.id "net-1d.png" %}" />
3823+ </p>
3824+ <p>
3825+ <h3>This Week</h3>
3826+ <img src="{% url nova_instances_graph project.projectname instance.id "net-1w.png" %}" />
3827+ </p>
3828+ <p>
3829+ <h3>This Month</h3>
3830+ <img src="{% url nova_instances_graph project.projectname instance.id "net-1m.png" %}" />
3831+ </p>
3832+
3833+ <p>
3834+ <h1>Disk Activity</h3>
3835+ <h3>Today</h3>
3836+ <img src="{% url nova_instances_graph project.projectname instance.id "disk-1d.png" %}" />
3837+ </p>
3838+ <p>
3839+ <h3>This Week</h3>
3840+ <img src="{% url nova_instances_graph project.projectname instance.id "disk-1w.png" %}" />
3841+ </p>
3842+ <p>
3843+ <h3>This Month</h3>
3844+ <img src="{% url nova_instances_graph project.projectname instance.id "disk-1m.png" %}" />
3845+ </p>
3846+</div>
3847+
3848+{% endblock %}
3849
3850=== added directory 'django-nova/src/django_nova/templates/django_nova/keypairs'
3851=== added file 'django-nova/src/django_nova/templates/django_nova/keypairs/_create_form.html'
3852--- django-nova/src/django_nova/templates/django_nova/keypairs/_create_form.html 1970-01-01 00:00:00 +0000
3853+++ django-nova/src/django_nova/templates/django_nova/keypairs/_create_form.html 2011-03-04 07:54:59 +0000
3854@@ -0,0 +1,5 @@
3855+{% for field in create_form %}
3856+{{ field.label_tag }}
3857+{% if field.errors %}{{ field.errors }}{% endif %}
3858+{{ field }}
3859+{% endfor %}
3860
3861=== added file 'django-nova/src/django_nova/templates/django_nova/keypairs/_list.html'
3862--- django-nova/src/django_nova/templates/django_nova/keypairs/_list.html 1970-01-01 00:00:00 +0000
3863+++ django-nova/src/django_nova/templates/django_nova/keypairs/_list.html 2011-03-04 07:54:59 +0000
3864@@ -0,0 +1,31 @@
3865+ {% if keypairs %}
3866+ <table style="width: 100%">
3867+ <tr>
3868+ <th>Key Pair Name</th>
3869+ <th>Fingerprint</th>
3870+ <th>&nbsp;</th>
3871+ </tr>
3872+ {% for keypair in keypairs %}
3873+ <tr class="{% cycle 'odd' 'even' %}">
3874+ <td>{{ keypair.name }}</td>
3875+ <td class="odd">{{ keypair.fingerprint }}</td>
3876+ <td>
3877+ <form id="form_key_delete_{{keypair.name}}" class="form-key-delete" method="post" action="{% url nova_keypairs_delete project.projectname %}">
3878+ <input name="key_name" type="hidden" value="{{ keypair.name }}" />
3879+ <input id="keypair_delete_{{keypair.name}}" class="delete" type="submit" value="Delete" />
3880+ {% csrf_token %}
3881+ </form>
3882+ </td>
3883+ </tr>
3884+ {% endfor %}
3885+ </table>
3886+ {% else %}
3887+ <div class="ui-widget">
3888+ <div class="ui-state-highlight ui-corner-all">
3889+ <p>
3890+ <span class="ui-icon ui-icon-info"></span>
3891+ No key pairs currently exist.
3892+ </p>
3893+ </div>
3894+ </div>
3895+ {% endif %}
3896
3897=== added file 'django-nova/src/django_nova/templates/django_nova/keypairs/base.html'
3898--- django-nova/src/django_nova/templates/django_nova/keypairs/base.html 1970-01-01 00:00:00 +0000
3899+++ django-nova/src/django_nova/templates/django_nova/keypairs/base.html 2011-03-04 07:54:59 +0000
3900@@ -0,0 +1,7 @@
3901+{% extends "django_nova/base.html" %}
3902+{% load sidebar_tags %}
3903+
3904+{% block nav_projects %}
3905+ {% sidebar_select keys %}
3906+ {{ block.super }}
3907+{% endblock %}
3908\ No newline at end of file
3909
3910=== added file 'django-nova/src/django_nova/templates/django_nova/keypairs/index.html'
3911--- django-nova/src/django_nova/templates/django_nova/keypairs/index.html 1970-01-01 00:00:00 +0000
3912+++ django-nova/src/django_nova/templates/django_nova/keypairs/index.html 2011-03-04 07:54:59 +0000
3913@@ -0,0 +1,77 @@
3914+{% extends "django_nova/keypairs/base.html" %}
3915+
3916+{% block title %} - Cloud Computing{% endblock %}
3917+
3918+{% block headerjs %}
3919+{{ block.super }}
3920+<script type="text/javascript" src="/media/dashboard/js/jquery.form.js"></script>
3921+{% endblock %}
3922+
3923+{% block content %}
3924+ <div id="page_head">
3925+ <h2 id="page_heading">Keys</h2>
3926+ <p id="page_description">Key pairs are ssh credentials which are injected into images when they are launched. Creating a new key pair registers the public key and downloads the private key (a pem file). <em>Protect and use the key as a normal private key.</em></p>
3927+ </div>
3928+
3929+ {% include "django_nova/_messages.html" %}
3930+
3931+ <div id="instances">
3932+ {% include "django_nova/keypairs/_list.html" %}
3933+ </div>
3934+
3935+ <div class="dash_block first">
3936+ <form id="frm_key_create" action="{% url nova_keypairs_add project.projectname %}" method="post">
3937+ {% csrf_token %}
3938+ <input id="js" name="js" type="hidden" value="0" />
3939+ <fieldset>
3940+ <h3>Create New Keypair</h3>
3941+ {% include "django_nova/keypairs/_create_form.html" %}
3942+ <input id="keypair_create" class="create" type="submit" value="Create" />
3943+ </fieldset>
3944+ </form>
3945+ </div>
3946+
3947+ <div id="dlg_confirm" title="Confirm Termination">
3948+ <p>Are you sure you wish to delete key <span id="spn_delete_key_name"></span>?</p>
3949+ </div>
3950+{% endblock %}
3951+
3952+{% block footerjs %}
3953+{{ block.super }}
3954+<script type="text/javascript">
3955+ $(function() { $('#js').val('1'); });
3956+
3957+ {% if download_key %}
3958+ $(function() { window.location = '{% url nova_keypairs_download project.projectname download_key %}'; });
3959+ {% endif %}
3960+
3961+ $(function() {
3962+ $('.form-key-delete').submit(function() {
3963+ _key_name = $(this).children(':first').val()
3964+ $('#spn_delete_key_name').text(_key_name);
3965+ $('#dlg_confirm').dialog('open');
3966+ return false;
3967+ });
3968+
3969+ $('#dlg_confirm').dialog({
3970+ buttons: {
3971+ 'Ok': onConfirmOK,
3972+ 'Cancel': function() { $(this).dialog('close'); }
3973+ },
3974+ autoOpen: false,
3975+ resizable: false,
3976+ width: 500,
3977+ height: 200
3978+ });
3979+
3980+ });
3981+
3982+ var _terminateID = null;
3983+
3984+ function onConfirmOK() {
3985+ $(this).dialog('close');
3986+ form = document.getElementById('form_key_delete_' + _key_name);
3987+ if(form) form.submit();
3988+ }
3989+</script>
3990+{% endblock %}
3991
3992=== added directory 'django-nova/src/django_nova/templates/django_nova/projects'
3993=== added file 'django-nova/src/django_nova/templates/django_nova/projects/edit_user.html'
3994--- django-nova/src/django_nova/templates/django_nova/projects/edit_user.html 1970-01-01 00:00:00 +0000
3995+++ django-nova/src/django_nova/templates/django_nova/projects/edit_user.html 2011-03-04 07:54:59 +0000
3996@@ -0,0 +1,72 @@
3997+{% extends "django_nova/base.html" %}
3998+{% block title %} - {{ project.projectname|capfirst }} Overview{% endblock %}
3999+{% block pageclass %}overview{% endblock %}
4000+
4001+{% block headerjs %}
4002+{{ block.super }}
4003+<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
4004+<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
4005+
4006+<script type="text/javascript" charset="utf-8">
4007+ $(function(){
4008+ $.each($("#user_edit form select"), function () {
4009+ // "Locations" can be any label you want
4010+ SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
4011+ });
4012+ })
4013+</script>
4014+{% endblock %}
4015+
4016+
4017+{% block content %}
4018+ <div id="page_head">
4019+ <h2 id="page_heading">Edit User Roles</h2>
4020+ <p id="page_description">From here you can edit multiple user roles.</p>
4021+ </div>
4022+
4023+ {% include "django_nova/_messages.html" %}
4024+ <div id="user_edit" class="dash_block first">
4025+ {% if user %}
4026+
4027+ <form action="." method="post" enctype="multipart/form-data">
4028+ {% csrf_token %}
4029+ <fieldset class="module aligned {{ fieldset.classes }}">
4030+ <h3 id="edit_{{ user.username }}">Edit Roles for User: {{ user.username }}</h3>
4031+ <div class="form-row">
4032+ <label>User</label>
4033+ <span id="user_name">{{ user.username }}</span>
4034+ </div>
4035+ <input type="hidden" name="username" value="{{ user.id }}" id="username" />
4036+
4037+ {% for field in form.visible_fields %}
4038+ <div class="form-row">
4039+ {{ field.errors }}
4040+ {{ field.label_tag }}{{ field }}
4041+ {% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
4042+ </div>
4043+ {% endfor %}
4044+ {% for field in form.hidden_fields %}
4045+ {{ field }}
4046+ {% endfor %}
4047+ </fieldset>
4048+ <div class="cancel">
4049+ <a href="{% url nova_project_manage project.projectname %}">Cancel</a>
4050+ </div>
4051+ <div class="submit-row">
4052+ <input type="submit" value="Save" class="default" />
4053+ {# <a href="#" class="deletelink">Remove User</a> #}
4054+ </div>
4055+ </form>
4056+ {% else %}
4057+ <div class="ui-widget">
4058+ <div class="ui-state-highlight ui-corner-all">
4059+ <p>
4060+ <span class="ui-icon ui-icon-info"></span>
4061+ No users are currently associated with this project.
4062+ </p>
4063+ </div>
4064+ </div>
4065+ {% endif %}
4066+ </div>
4067+{% endblock %}
4068+
4069
4070=== added file 'django-nova/src/django_nova/templates/django_nova/projects/index.html'
4071--- django-nova/src/django_nova/templates/django_nova/projects/index.html 1970-01-01 00:00:00 +0000
4072+++ django-nova/src/django_nova/templates/django_nova/projects/index.html 2011-03-04 07:54:59 +0000
4073@@ -0,0 +1,26 @@
4074+{% extends "django_nova/base.html" %}
4075+{% block title %} - {{ project.projectname|capfirst }} Overview{% endblock %}
4076+{% block pageclass %}overview{% endblock %}
4077+
4078+{% block content %}
4079+ <div id="page_head">
4080+ <h2><span>{{ project.projectname|capfirst }}</span> Overview</h2>
4081+ </div>
4082+
4083+ {% include "django_nova/_messages.html" %}
4084+
4085+ <div id="welcome">
4086+ <p>Welcome to the <span>{{ project.projectname|capfirst }}</span> Overview. From here you can manage your instances, images, keys, and security groups.</p>
4087+ <p>To get started using the command line management tools, you can <a target="_blank" href="http://open.eucalyptus.com/wiki/Euca2oolsGuide_v1.1">download euca2ools</a> and use them with your x509 credentials.</p>
4088+ </div>
4089+
4090+ <div id="resources" class="dash_block">
4091+ <h3>Project Resources</h3>
4092+ <ul>
4093+ <li><a href="{% url nova_download_credentials project.projectname %}">Generate X509 credentials.</a></li>
4094+ <li><a href="{% url nova_instances project.projectname %}">View Instances&nbsp;(<strong>{{ instance_count }}</strong> running).</a></li>
4095+ <li><a href="{% url nova_images project.projectname %}">View Images.</a></li>
4096+ </ul>
4097+ </div>
4098+{% endblock %}
4099+
4100
4101=== added file 'django-nova/src/django_nova/templates/django_nova/projects/manage.html'
4102--- django-nova/src/django_nova/templates/django_nova/projects/manage.html 1970-01-01 00:00:00 +0000
4103+++ django-nova/src/django_nova/templates/django_nova/projects/manage.html 2011-03-04 07:54:59 +0000
4104@@ -0,0 +1,45 @@
4105+{% extends "django_nova/base.html" %}
4106+{% block title %} - {{ project.projectname|capfirst }} Overview{% endblock %}
4107+{% block pageclass %}overview{% endblock %}
4108+
4109+{% block content %}
4110+ <div id="page_head">
4111+ <h2 id="page_heading">Manage Users and Roles</h2>
4112+ <p id="page_description">From here you can manage users and roles.</p>
4113+ </div>
4114+
4115+ {% include "django_nova/_messages.html" %}
4116+
4117+ <div id="users">
4118+ {% if members %}
4119+ <table style="width: 100%">
4120+ <tr>
4121+ <th>Username</th>
4122+ <th>Project Roles</th>
4123+ <th>Global Roles</th>
4124+ <th>&nbsp;</th>
4125+ </tr>
4126+ {% for member in members %}
4127+ <tr class="{% cycle 'odd' 'even' %}">
4128+ <td>{{ member.memberId }} {% if project.projectManagerId == member.memberId %}(<em>project manager</em>){% endif %}</td>
4129+ <td>{{ member.project_roles }}</td>
4130+ <td>{{ member.global_roles }}</td>
4131+ <td class="odd">
4132+ <a href="{% url nova_project_edit_user project.projectname member.memberId%}">Edit</a>
4133+ </td>
4134+ </tr>
4135+ {% endfor %}
4136+ </table>
4137+ {% else %}
4138+ <div class="ui-widget">
4139+ <div class="ui-state-highlight ui-corner-all">
4140+ <p>
4141+ <span class="ui-icon ui-icon-info"></span>
4142+ No users are currently associated with this project.
4143+ </p>
4144+ </div>
4145+ </div>
4146+ {% endif %}
4147+ </div>
4148+{% endblock %}
4149+
4150
4151=== added directory 'django-nova/src/django_nova/templates/django_nova/securitygroups'
4152=== added file 'django-nova/src/django_nova/templates/django_nova/securitygroups/_authorize_form.html'
4153--- django-nova/src/django_nova/templates/django_nova/securitygroups/_authorize_form.html 1970-01-01 00:00:00 +0000
4154+++ django-nova/src/django_nova/templates/django_nova/securitygroups/_authorize_form.html 2011-03-04 07:54:59 +0000
4155@@ -0,0 +1,5 @@
4156+{% for field in authorize_form %}
4157+ {{ field.label_tag }}
4158+ {% if field.errors %}{{ field.errors }}{% endif %}
4159+ {{ field }}
4160+{% endfor %}
4161\ No newline at end of file
4162
4163=== added file 'django-nova/src/django_nova/templates/django_nova/securitygroups/_create_form.html'
4164--- django-nova/src/django_nova/templates/django_nova/securitygroups/_create_form.html 1970-01-01 00:00:00 +0000
4165+++ django-nova/src/django_nova/templates/django_nova/securitygroups/_create_form.html 2011-03-04 07:54:59 +0000
4166@@ -0,0 +1,5 @@
4167+{% for field in create_form %}
4168+{{ field.label_tag }}
4169+{% if field.errors %}{{ field.errors }}{% endif %}
4170+{{ field }}
4171+{% endfor %}
4172
4173=== added file 'django-nova/src/django_nova/templates/django_nova/securitygroups/_revoke_form.html'
4174--- django-nova/src/django_nova/templates/django_nova/securitygroups/_revoke_form.html 1970-01-01 00:00:00 +0000
4175+++ django-nova/src/django_nova/templates/django_nova/securitygroups/_revoke_form.html 2011-03-04 07:54:59 +0000
4176@@ -0,0 +1,3 @@
4177+<input type="hidden" name="protocol" value="{{ rule.ip_protocol }}" />
4178+<input type="hidden" name="from_port" value="{{ rule.from_port}}" />
4179+<input type="hidden" name="to_port" value="{{ rule.to_port }}" />
4180
4181=== added file 'django-nova/src/django_nova/templates/django_nova/securitygroups/base.html'
4182--- django-nova/src/django_nova/templates/django_nova/securitygroups/base.html 1970-01-01 00:00:00 +0000
4183+++ django-nova/src/django_nova/templates/django_nova/securitygroups/base.html 2011-03-04 07:54:59 +0000
4184@@ -0,0 +1,7 @@
4185+{% extends "django_nova/base.html" %}
4186+{% load sidebar_tags %}
4187+
4188+{% block nav_projects %}
4189+ {% sidebar_select securitygroups %}
4190+ {{ block.super }}
4191+{% endblock %}
4192\ No newline at end of file
4193
4194=== added file 'django-nova/src/django_nova/templates/django_nova/securitygroups/detail.html'
4195--- django-nova/src/django_nova/templates/django_nova/securitygroups/detail.html 1970-01-01 00:00:00 +0000
4196+++ django-nova/src/django_nova/templates/django_nova/securitygroups/detail.html 2011-03-04 07:54:59 +0000
4197@@ -0,0 +1,62 @@
4198+{% extends "django_nova/securitygroups/base.html" %}
4199+
4200+{% block title %} - Cloud Computing{% endblock %}
4201+
4202+{% block content %}
4203+ <div id="dashboard_tabs">
4204+ <div id="tabs-1" class="ui-tabs-panel ui-widget-content ui-corner-bottom dash-wrap" style="margin-left:0;min-height:300px;">
4205+ <ul id="dashboard_nav">
4206+ <li><a id="lnk_overview" href="{% url dashboard_project project.projectname %}">Overview</a></li>
4207+ <li><a id="lnk_instances" href="{% url dashboard_instances project.projectname %}">Instances</a></li>
4208+ <li><a id="lnk_images" href="{% url dashboard_images project.projectname %}">Images</a></li>
4209+ <li><a id="lnk_keypairs" href="{% url dashboard_keypairs project.projectname %}">Keys</a></li>
4210+ <li class="active"><a id="lnk_securitygroups" href="{% url dashboard_securitygroups project.projectname %}">Security Groups</a></li>
4211+ <li><a id="lnk_volumes" href="{% url dashboard_volumes project.projectname %}">Volumes</a></li>
4212+ </ul>
4213+ <div id="right_content">
4214+ <div id="page_head">
4215+ <h2>Security Group: {{ securitygroup.name }}</h2>
4216+ <p>Add and remove protocols to the security group by authorizing and revoking port forwarding. For instance<br /> [tcp, 80, 80] will allow access to HTTP from devices outside this security group.</p>
4217+ </div>
4218+
4219+ {% include "django_nova/_messages.html" %}
4220+
4221+ <table>
4222+ <tr>
4223+ <th>Protocol</th>
4224+ <th>From Port</th>
4225+ <th>To Port</th>
4226+ <th></th>
4227+ </tr>
4228+ {% for rule in securitygroup.rules %}
4229+ <tr class="{% cycle 'odd' 'even' %}">
4230+ <td>{{ rule.ip_protocol }}</td>
4231+ <td class="odd">{{ rule.from_port }}</td>
4232+ <td>{{ rule.to_port }}</td>
4233+ <td class="odd">
4234+ <form id="security_groups" method="post" action="{% url dashboard_securitygroups_revoke project.projectname securitygroup.name %}">
4235+ {% csrf_token %}
4236+ {% include "django_nova/securitygroups/_revoke_form.html" %}
4237+ <input class="ui-state-default ui-corner-all" type="submit" value="Revoke" />
4238+ </form>
4239+ </td>
4240+ </tr>
4241+ {% endfor %}
4242+ </table>
4243+
4244+ <div class="block">
4245+ <h3>Authorize</h3>
4246+ <form id="authorize" method="post" action="{% url dashboard_securitygroups_authorize project.projectname securitygroup.name %}">
4247+ {% csrf_token %}
4248+ <fieldset>
4249+ <input type="hidden" name="group" value="{{ securitygroup.name }}" />
4250+ {% include "django_nova/securitygroups/_authorize_form.html" %}
4251+ <input class="ui-state-default ui-corner-all" type="submit" value="Authorize">
4252+ </fieldset>
4253+ </form>
4254+ </div>
4255+ </div>
4256+ <div class="clr"></div>
4257+ </div>
4258+ </div>
4259+{% endblock %}
4260
4261=== added file 'django-nova/src/django_nova/templates/django_nova/securitygroups/index.html'
4262--- django-nova/src/django_nova/templates/django_nova/securitygroups/index.html 1970-01-01 00:00:00 +0000
4263+++ django-nova/src/django_nova/templates/django_nova/securitygroups/index.html 2011-03-04 07:54:59 +0000
4264@@ -0,0 +1,59 @@
4265+{% extends "django_nova/securitygroups/base.html" %}
4266+
4267+{% block title %} - Cloud Computing{% endblock %}
4268+
4269+{% block content %}
4270+ <div id="dashboard_tabs">
4271+ <div id="tabs-1" class="ui-tabs-panel ui-widget-content ui-corner-bottom dash-wrap" style="margin-left:0px;min-height:300px;">
4272+ <ul id="dashboard_nav">
4273+ <li><a id="lnk_overview" href="{% url dashboard_project project.projectname %}">Overview</a></li>
4274+ <li><a id="lnk_instances" href="{% url dashboard_instances project.projectname %}">Instances</a></li>
4275+ <li><a id="lnk_images" href="{% url dashboard_images project.projectname %}">Images</a></li>
4276+ <li><a id="lnk_keypairs" href="{% url dashboard_keypairs project.projectname %}">Keys</a></li>
4277+ <li class="active"><a id="lnk_securitygroups" href="{% url dashboard_securitygroups project.projectname %}">Security Groups</a></li>
4278+ <li><a id="lnk_volumes" href="{% url dashboard_volumes project.projectname %}">Volumes</a></li>
4279+ </ul>
4280+ <div id="right_content">
4281+ <div id="page_head">
4282+ <h2 id="page_heading">Security Groups</h2>
4283+ <p id="page_description">Security groups are firewall rules which allow access to your instances from other groups as well as the internet. All ports/protocols are denied by default.</p>
4284+ </div>
4285+
4286+ {% include "django_nova/_messages.html" %}
4287+
4288+ <table style="width:100%;">
4289+ <tr>
4290+ <th>Name</th>
4291+ <th style="min-width:60%;">Description</th>
4292+ <th>Rules</th>
4293+ <th>&nbsp;</th>
4294+ </tr>
4295+ {% for securitygroup in securitygroups %}
4296+ <tr class="{% cycle 'odd' 'even' %}">
4297+ <td id="group_{{ securitygroup.id }}"><a href="{% url dashboard_securitygroups_detail project.projectname securitygroup.name %}">{{ securitygroup.name }}</a></td>
4298+ <td id="group_{{ securitygroup.id }}_description" class="odd">{{ securitygroup.description }}</td>
4299+ <td id="group_{{ securitygroup.id }}_rules">{{ securitygroup.rules|length }}</td>
4300+ <td class="odd">
4301+ <form id="delete_group_{{ securitygroup.id }}" method="post" action="{% url dashboard_securitygroups_delete project.projectname securitygroup.name %}">
4302+ {% csrf_token %}
4303+ <input class="ui-state-default ui-corner-all" type="submit" value="Delete">
4304+ </form>
4305+ </td>
4306+ </tr>
4307+ {% endfor %}
4308+ </table>
4309+ <div class="block">
4310+ <form id="add_group_form" method="post" action="{% url dashboard_securitygroups_add project.projectname %}">
4311+ {% csrf_token %}
4312+ <fieldset>
4313+ <h3>New Group</h3>
4314+ {% include "django_nova/securitygroups/_create_form.html" %}
4315+ <label>&nbsp;</label><input class="ui-state-default ui-corner-all" type="submit" value="Create" />
4316+ </fieldset>
4317+ </form>
4318+ </div>
4319+ </div>
4320+ <div class="clr"></div>
4321+ </div>
4322+ </div>
4323+{% endblock %}
4324
4325=== added directory 'django-nova/src/django_nova/templates/django_nova/volumes'
4326=== added file 'django-nova/src/django_nova/templates/django_nova/volumes/_attach_form.html'
4327--- django-nova/src/django_nova/templates/django_nova/volumes/_attach_form.html 1970-01-01 00:00:00 +0000
4328+++ django-nova/src/django_nova/templates/django_nova/volumes/_attach_form.html 2011-03-04 07:54:59 +0000
4329@@ -0,0 +1,5 @@
4330+{% for field in attach_form %}
4331+{{ field.label_tag }}
4332+{% if field.errors %}{{ field.errors }}{% endif %}
4333+{{ field }}
4334+{% endfor %}
4335
4336=== added file 'django-nova/src/django_nova/templates/django_nova/volumes/_create_form.html'
4337--- django-nova/src/django_nova/templates/django_nova/volumes/_create_form.html 1970-01-01 00:00:00 +0000
4338+++ django-nova/src/django_nova/templates/django_nova/volumes/_create_form.html 2011-03-04 07:54:59 +0000
4339@@ -0,0 +1,5 @@
4340+{% for field in create_form %}
4341+{{ field.label_tag }}
4342+{% if field.errors %}{{ field.errors }}{% endif %}
4343+{{ field }}
4344+{% endfor %}
4345
4346=== added file 'django-nova/src/django_nova/templates/django_nova/volumes/base.html'
4347--- django-nova/src/django_nova/templates/django_nova/volumes/base.html 1970-01-01 00:00:00 +0000
4348+++ django-nova/src/django_nova/templates/django_nova/volumes/base.html 2011-03-04 07:54:59 +0000
4349@@ -0,0 +1,7 @@
4350+{% extends "django_nova/base.html" %}
4351+{% load sidebar_tags %}
4352+
4353+{% block nav_projects %}
4354+ {% sidebar_select volumes %}
4355+ {{ block.super }}
4356+{% endblock %}
4357\ No newline at end of file
4358
4359=== added file 'django-nova/src/django_nova/templates/django_nova/volumes/index.html'
4360--- django-nova/src/django_nova/templates/django_nova/volumes/index.html 1970-01-01 00:00:00 +0000
4361+++ django-nova/src/django_nova/templates/django_nova/volumes/index.html 2011-03-04 07:54:59 +0000
4362@@ -0,0 +1,84 @@
4363+{% extends "django_nova/volumes/base.html" %}
4364+
4365+{% block title %} - Cloud Computing{% endblock %}
4366+
4367+{% block content %}
4368+ <div id="page_head">
4369+ <h2 id="page_heading">Volumes</h2>
4370+ <p id="page_description">Volumes provide persistent block storage. Creating a new volume gives you a raw block device which you may format with your choice of filesystems (ext3 is recommended). A volume may only be attached to a single instance at a time.</p>
4371+ </div>
4372+
4373+ {% include "django_nova/_messages.html" %}
4374+
4375+ {% if volumes %}
4376+ <table style="width: 100%">
4377+ <tr>
4378+ <th>ID</th>
4379+ <th>Size</th>
4380+ <th colspan="2">Status</th>
4381+ </tr>
4382+ {% for volume in volumes %}
4383+ <tr class="{% cycle 'odd' 'even' %}">
4384+ <td id="volume_{{ volume.id }}">{{ volume.id }} {{ volume.displayName }}</td>
4385+ <td id="volume_{{ volume.id }}_size" class="odd">{{ volume.size }}GB</td>
4386+ <td id="volume_{{ volume.id }})_status">
4387+ {% if volume.status == "in-use" %}
4388+ {% if volume.attachment_state == "attached" %}
4389+ attached: {{ volume.attach_data.instance_id }}
4390+ {% else %}
4391+ {{ volume.attachment_state }}
4392+ {% endif %}
4393+ {% else %}
4394+ {{ volume.status }}
4395+ {% endif %}
4396+ </td>
4397+ {% if volume.attachment_state == "attached" %}
4398+ <td class="odd">
4399+ <form class="volume" action="{% url nova_volumes_detach project.projectname volume.id %}" method="post">
4400+ {% csrf_token %}
4401+ <input id="detach_{{ volume.id }}" class="detach" type="submit" value="Detach">
4402+ </form>
4403+ </td>
4404+ {% else %}
4405+ <td class="odd">
4406+ <form class="volume" action="{% url nova_volumes_delete project.projectname volume.id %}" method="post">
4407+ {% csrf_token %}
4408+ <input id="delete_{{ volume.id }}" class="delete" type="submit" value="Delete">
4409+ </form>
4410+ </td>
4411+ {% endif %}
4412+ </tr>
4413+ {% endfor %}
4414+ </table>
4415+ {% else %}
4416+ <div class="ui-widget">
4417+ <div class="ui-state-highlight ui-corner-all">
4418+ <p>
4419+ <span class="ui-icon ui-icon-info"></span>
4420+ No volumes currently exist.
4421+ </p>
4422+ </div>
4423+ </div>
4424+ {% endif %}
4425+ <div class="dash_block first">
4426+ <form id="new_volume_form" method="post" action="{% url nova_volumes_add project.projectname %}">
4427+ {% csrf_token %}
4428+ <fieldset>
4429+ <h3>Create New Volume</h3>
4430+ {% include "django_nova/volumes/_create_form.html" %}
4431+ <input id="create_volume" class="create" type="submit" value="Create" />
4432+ </fieldset>
4433+ </form>
4434+ </div>
4435+
4436+ <div class="dash_block">
4437+ <form id="new_volume_form" method="post" action="{% url nova_volumes_attach project.projectname %}">
4438+ {% csrf_token %}
4439+ <fieldset>
4440+ <h3>Attach Volume</h3>
4441+ {% include "django_nova/volumes/_attach_form.html" %}
4442+ <input id="attach_volume" class="attach" type="submit" value="Attach" />
4443+ </fieldset>
4444+ </form>
4445+ </div>
4446+{% endblock %}
4447
4448=== added directory 'django-nova/src/django_nova/templatetags'
4449=== added file 'django-nova/src/django_nova/templatetags/__init__.py'
4450=== added file 'django-nova/src/django_nova/templatetags/admin_extras.py'
4451--- django-nova/src/django_nova/templatetags/admin_extras.py 1970-01-01 00:00:00 +0000
4452+++ django-nova/src/django_nova/templatetags/admin_extras.py 2011-03-04 07:54:59 +0000
4453@@ -0,0 +1,50 @@
4454+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4455+
4456+# Copyright 2010 United States Government as represented by the
4457+# Administrator of the National Aeronautics and Space Administration.
4458+# All Rights Reserved.
4459+#
4460+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4461+# not use this file except in compliance with the License. You may obtain
4462+# a copy of the License at
4463+#
4464+# http://www.apache.org/licenses/LICENSE-2.0
4465+#
4466+# Unless required by applicable law or agreed to in writing, software
4467+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4468+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4469+# License for the specific language governing permissions and limitations
4470+# under the License.
4471+"""
4472+Template tags for extending the Django admin interface.
4473+"""
4474+
4475+from django.contrib.admin.templatetags.admin_list import items_for_result, result_headers
4476+from django.core.urlresolvers import reverse
4477+from django.template import Library
4478+from django.utils.safestring import mark_safe
4479+
4480+
4481+register = Library()
4482+
4483+def project_result_list(cl):
4484+ headers = list(result_headers(cl))
4485+ headers.append({'text': mark_safe('&nbsp;')})
4486+
4487+ results = list()
4488+
4489+ for project in cl.result_list:
4490+ rl = list(items_for_result(cl,project,None))
4491+
4492+ url = reverse('admin_project_sendcredentials', args=[project.projectname])
4493+ content = mark_safe('<td><a href="%s">Send Credentials</a></td>' % url)
4494+
4495+ rl.append(content)
4496+ results.append(rl)
4497+
4498+ return {
4499+ 'cl': cl,
4500+ 'result_headers': headers,
4501+ 'results': results
4502+ }
4503+project_result_list = register.inclusion_tag("admin/change_list_results.html")(project_result_list)
4504
4505=== added file 'django-nova/src/django_nova/templatetags/django_nova_tags.py'
4506--- django-nova/src/django_nova/templatetags/django_nova_tags.py 1970-01-01 00:00:00 +0000
4507+++ django-nova/src/django_nova/templatetags/django_nova_tags.py 2011-03-04 07:54:59 +0000
4508@@ -0,0 +1,37 @@
4509+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4510+
4511+# Copyright 2010 United States Government as represented by the
4512+# Administrator of the National Aeronautics and Space Administration.
4513+# All Rights Reserved.
4514+#
4515+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4516+# not use this file except in compliance with the License. You may obtain
4517+# a copy of the License at
4518+#
4519+# http://www.apache.org/licenses/LICENSE-2.0
4520+#
4521+# Unless required by applicable law or agreed to in writing, software
4522+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4523+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4524+# License for the specific language governing permissions and limitations
4525+# under the License.
4526+
4527+"""
4528+Template tags for working with django_nova.
4529+"""
4530+
4531+from django import template
4532+from django.conf import settings
4533+
4534+
4535+register = template.Library()
4536+
4537+
4538+class SiteBrandingNode(template.Node):
4539+ def render(self, context):
4540+ return settings.SITE_BRANDING
4541+
4542+@register.tag
4543+def site_branding(parser, token):
4544+ return SiteBrandingNode()
4545+
4546
4547=== added file 'django-nova/src/django_nova/templatetags/project_tags.py'
4548--- django-nova/src/django_nova/templatetags/project_tags.py 1970-01-01 00:00:00 +0000
4549+++ django-nova/src/django_nova/templatetags/project_tags.py 2011-03-04 07:54:59 +0000
4550@@ -0,0 +1,39 @@
4551+ # vim: tabstop=4 shiftwidth=4 softtabstop=4
4552+
4553+# Copyright 2010 United States Government as represented by the
4554+# Administrator of the National Aeronautics and Space Administration.
4555+# All Rights Reserved.
4556+#
4557+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4558+# not use this file except in compliance with the License. You may obtain
4559+# a copy of the License at
4560+#
4561+# http://www.apache.org/licenses/LICENSE-2.0
4562+#
4563+# Unless required by applicable law or agreed to in writing, software
4564+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4565+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4566+# License for the specific language governing permissions and limitations
4567+# under the License.
4568+
4569+"""
4570+Template tags for gathering contextual region data.
4571+"""
4572+
4573+from django import template
4574+from django_nova.shortcuts import get_projects
4575+
4576+
4577+register = template.Library()
4578+
4579+
4580+class ProjectsNode(template.Node):
4581+ def render(self, context):
4582+ # Store project list in template context.
4583+ context['projects'] = get_projects(context['request'].user)
4584+ return ''
4585+
4586+
4587+@register.tag
4588+def load_projects(parser, token):
4589+ return ProjectsNode()
4590
4591=== added file 'django-nova/src/django_nova/templatetags/region_tags.py'
4592--- django-nova/src/django_nova/templatetags/region_tags.py 1970-01-01 00:00:00 +0000
4593+++ django-nova/src/django_nova/templatetags/region_tags.py 2011-03-04 07:54:59 +0000
4594@@ -0,0 +1,40 @@
4595+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4596+
4597+# Copyright 2010 United States Government as represented by the
4598+# Administrator of the National Aeronautics and Space Administration.
4599+# All Rights Reserved.
4600+#
4601+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4602+# not use this file except in compliance with the License. You may obtain
4603+# a copy of the License at
4604+#
4605+# http://www.apache.org/licenses/LICENSE-2.0
4606+#
4607+# Unless required by applicable law or agreed to in writing, software
4608+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4609+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4610+# License for the specific language governing permissions and limitations
4611+# under the License.
4612+"""
4613+Template tags for gathering contextual region data.
4614+"""
4615+
4616+from django import template
4617+from django_nova.shortcuts import get_current_region, get_all_regions
4618+
4619+
4620+register = template.Library()
4621+
4622+
4623+class RegionsNode(template.Node):
4624+ def render(self, context):
4625+ # Store region info in template context.
4626+ context['current_region'] = get_current_region(context['request'])
4627+ context['regions'] = get_all_regions()
4628+ return ''
4629+
4630+
4631+@register.tag
4632+def load_regions(parser, token):
4633+ return RegionsNode()
4634+
4635
4636=== added file 'django-nova/src/django_nova/templatetags/sidebar_tags.py'
4637--- django-nova/src/django_nova/templatetags/sidebar_tags.py 1970-01-01 00:00:00 +0000
4638+++ django-nova/src/django_nova/templatetags/sidebar_tags.py 2011-03-04 07:54:59 +0000
4639@@ -0,0 +1,46 @@
4640+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4641+
4642+# Copyright 2010 United States Government as represented by the
4643+# Administrator of the National Aeronautics and Space Administration.
4644+# All Rights Reserved.
4645+#
4646+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4647+# not use this file except in compliance with the License. You may obtain
4648+# a copy of the License at
4649+#
4650+# http://www.apache.org/licenses/LICENSE-2.0
4651+#
4652+# Unless required by applicable law or agreed to in writing, software
4653+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4654+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4655+# License for the specific language governing permissions and limitations
4656+# under the License.
4657+
4658+"""
4659+Template tags for rendering the sidebar.
4660+"""
4661+
4662+from django import template
4663+
4664+
4665+register = template.Library()
4666+
4667+
4668+class SidebarSelectNode(template.Node):
4669+ def __init__(self, selected):
4670+ self.selected = selected
4671+
4672+ def render(self, context):
4673+ # Store page type in template context.
4674+ context['sidebar_selected'] = self.selected
4675+ return ''
4676+
4677+
4678+@register.tag
4679+def sidebar_select(parser, token):
4680+ try:
4681+ tag_name, selected = token.split_contents()
4682+ except ValueError:
4683+ raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
4684+ return SidebarSelectNode(str(selected))
4685+
4686
4687=== added file 'django-nova/src/django_nova/templatetags/truncate_filter.py'
4688--- django-nova/src/django_nova/templatetags/truncate_filter.py 1970-01-01 00:00:00 +0000
4689+++ django-nova/src/django_nova/templatetags/truncate_filter.py 2011-03-04 07:54:59 +0000
4690@@ -0,0 +1,31 @@
4691+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4692+
4693+# Copyright 2010 United States Government as represented by the
4694+# Administrator of the National Aeronautics and Space Administration.
4695+# All Rights Reserved.
4696+#
4697+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4698+# not use this file except in compliance with the License. You may obtain
4699+# a copy of the License at
4700+#
4701+# http://www.apache.org/licenses/LICENSE-2.0
4702+#
4703+# Unless required by applicable law or agreed to in writing, software
4704+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4705+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4706+# License for the specific language governing permissions and limitations
4707+# under the License.
4708+"""
4709+Template tags for truncating strings.
4710+"""
4711+
4712+from django import template
4713+
4714+register = template.Library()
4715+
4716+@register.filter("truncate")
4717+def truncate(value, size):
4718+ if len(value) > size and size > 3:
4719+ return value[0:(size-3)] + '...'
4720+ else:
4721+ return value[0:size]
4722
4723=== added directory 'django-nova/src/django_nova/tests'
4724=== added file 'django-nova/src/django_nova/tests/__init__.py'
4725--- django-nova/src/django_nova/tests/__init__.py 1970-01-01 00:00:00 +0000
4726+++ django-nova/src/django_nova/tests/__init__.py 2011-03-04 07:54:59 +0000
4727@@ -0,0 +1,1 @@
4728+from view_tests import *
4729\ No newline at end of file
4730
4731=== added directory 'django-nova/src/django_nova/tests/templates'
4732=== added file 'django-nova/src/django_nova/tests/templates/base-sidebar.html'
4733=== added file 'django-nova/src/django_nova/tests/urls.py'
4734--- django-nova/src/django_nova/tests/urls.py 1970-01-01 00:00:00 +0000
4735+++ django-nova/src/django_nova/tests/urls.py 2011-03-04 07:54:59 +0000
4736@@ -0,0 +1,36 @@
4737+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4738+
4739+# Copyright 2010 United States Government as represented by the
4740+# Administrator of the National Aeronautics and Space Administration.
4741+# All Rights Reserved.
4742+#
4743+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4744+# not use this file except in compliance with the License. You may obtain
4745+# a copy of the License at
4746+#
4747+# http://www.apache.org/licenses/LICENSE-2.0
4748+#
4749+# Unless required by applicable law or agreed to in writing, software
4750+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4751+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4752+# License for the specific language governing permissions and limitations
4753+# under the License.
4754+
4755+"""
4756+URL patterns for testing django-nova views.
4757+"""
4758+
4759+from django.conf.urls.defaults import *
4760+from django.conf.urls.defaults import *
4761+
4762+
4763+urlpatterns = patterns('',
4764+ url(r'^projects/', include('django_nova.urls.project')),
4765+ url(r'^region/', include('django_nova.urls.region')),
4766+ url(r'^admin/projects/', include('django_nova.urls.admin_project')),
4767+ url(r'^admin/roles/', include('django_nova.urls.admin_roles')),
4768+ url(r'^credentials/download/(?P<auth_token>\w+)/$',
4769+ 'django_nova.views.credentials.authorize_credentials',
4770+ name='nova_credentials_authorize'),
4771+)
4772+
4773
4774=== added directory 'django-nova/src/django_nova/tests/view_tests'
4775=== added file 'django-nova/src/django_nova/tests/view_tests/__init__.py'
4776--- django-nova/src/django_nova/tests/view_tests/__init__.py 1970-01-01 00:00:00 +0000
4777+++ django-nova/src/django_nova/tests/view_tests/__init__.py 2011-03-04 07:54:59 +0000
4778@@ -0,0 +1,7 @@
4779+from credential_tests import *
4780+from image_tests import *
4781+from instance_tests import *
4782+from keypair_tests import *
4783+from region_tests import *
4784+from volume_tests import *
4785+
4786
4787=== added file 'django-nova/src/django_nova/tests/view_tests/base.py'
4788--- django-nova/src/django_nova/tests/view_tests/base.py 1970-01-01 00:00:00 +0000
4789+++ django-nova/src/django_nova/tests/view_tests/base.py 2011-03-04 07:54:59 +0000
4790@@ -0,0 +1,90 @@
4791+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4792+
4793+# Copyright 2010 United States Government as represented by the
4794+# Administrator of the National Aeronautics and Space Administration.
4795+# All Rights Reserved.
4796+#
4797+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4798+# not use this file except in compliance with the License. You may obtain
4799+# a copy of the License at
4800+#
4801+# http://www.apache.org/licenses/LICENSE-2.0
4802+#
4803+# Unless required by applicable law or agreed to in writing, software
4804+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4805+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4806+# License for the specific language governing permissions and limitations
4807+# under the License.
4808+
4809+"""
4810+Base classes for view based unit tests.
4811+"""
4812+
4813+import mox
4814+
4815+from django import test
4816+from django.conf import settings
4817+from django.contrib.auth import models as auth_models
4818+from django_nova import adminclient
4819+from django_nova import manager
4820+from django_nova import shortcuts
4821+
4822+
4823+TEST_PROJECT = 'test'
4824+TEST_USER = 'test'
4825+TEST_REGION = 'test'
4826+
4827+
4828+class BaseViewTests(test.TestCase):
4829+ def setUp(self):
4830+ self.mox = mox.Mox()
4831+
4832+ def tearDown(self):
4833+ self.mox.UnsetStubs()
4834+
4835+ def assertRedirectsNoFollow(self, response, expected_url):
4836+ self.assertEqual(response._headers['location'],
4837+ ('Location', settings.TESTSERVER + expected_url))
4838+ self.assertEqual(response.status_code, 302)
4839+
4840+ def authenticateTestUser(self):
4841+ user = auth_models.User.objects.create_user(TEST_USER,
4842+ 'test@test.com',
4843+ password='test')
4844+ login = self.client.login(username=TEST_USER, password='test')
4845+ self.failUnless(login, 'Unable to login')
4846+ return user
4847+
4848+
4849+class BaseProjectViewTests(BaseViewTests):
4850+ def setUp(self):
4851+ super(BaseProjectViewTests, self).setUp()
4852+
4853+ project = adminclient.ProjectInfo()
4854+ project.projectname = TEST_PROJECT
4855+ project.projectManagerId = TEST_USER
4856+
4857+ self.user = self.authenticateTestUser()
4858+ self.region = adminclient.RegionInfo(name=TEST_REGION,
4859+ endpoint='http://test:8773/')
4860+ self.project = manager.ProjectManager(self.user.username,
4861+ project,
4862+ self.region)
4863+ self.mox.StubOutWithMock(shortcuts, 'get_project_or_404')
4864+ shortcuts.get_project_or_404(mox.IgnoreArg(),
4865+ 'test').AndReturn(self.project)
4866+
4867+ def create_key_pair_choices(self, key_names):
4868+ return [(k, k) for k in key_names]
4869+
4870+ def create_instance_type_choices(self):
4871+ return [('m1.medium', 'm1.medium'),
4872+ ('m1.large', 'm1.large')]
4873+
4874+ def create_instance_choices(self, instance_ids):
4875+ return [(id, id) for id in instance_ids]
4876+
4877+ def create_available_volume_choices(self, volumes):
4878+ return [(v.id, '%s %s - %dGB' % (v.id, v.displayName, v.size)) \
4879+ for v in volumes]
4880+
4881
4882=== added file 'django-nova/src/django_nova/tests/view_tests/credential_tests.py'
4883--- django-nova/src/django_nova/tests/view_tests/credential_tests.py 1970-01-01 00:00:00 +0000
4884+++ django-nova/src/django_nova/tests/view_tests/credential_tests.py 2011-03-04 07:54:59 +0000
4885@@ -0,0 +1,70 @@
4886+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4887+
4888+# Copyright 2010 United States Government as represented by the
4889+# Administrator of the National Aeronautics and Space Administration.
4890+# All Rights Reserved.
4891+#
4892+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4893+# not use this file except in compliance with the License. You may obtain
4894+# a copy of the License at
4895+#
4896+# http://www.apache.org/licenses/LICENSE-2.0
4897+#
4898+# Unless required by applicable law or agreed to in writing, software
4899+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4900+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4901+# License for the specific language governing permissions and limitations
4902+# under the License.
4903+
4904+"""
4905+Unit tests for credential views.
4906+"""
4907+
4908+import mox
4909+from django.conf import settings
4910+from django.core.urlresolvers import reverse
4911+from django_nova import models
4912+from django_nova.tests.view_tests.base import BaseViewTests
4913+
4914+
4915+class CredentialViewTests(BaseViewTests):
4916+ def test_download_expired_credentials(self):
4917+ auth_token = 'expired'
4918+ self.mox.StubOutWithMock(models.CredentialsAuthorization,
4919+ 'get_by_token')
4920+ models.CredentialsAuthorization.get_by_token(auth_token) \
4921+ .AndReturn(None)
4922+ self.mox.ReplayAll()
4923+
4924+ res = self.client.get(reverse('nova_credentials_authorize',
4925+ args=[auth_token]))
4926+ self.assertTemplateUsed(res, 'django_nova/credentials/expired.html')
4927+
4928+ self.mox.VerifyAll()
4929+
4930+ def test_download_good_credentials(self):
4931+ auth_token = 'good'
4932+
4933+ creds = models.CredentialsAuthorization()
4934+ creds.username = 'test'
4935+ creds.project = 'test'
4936+ creds.auth_token = auth_token
4937+
4938+ self.mox.StubOutWithMock(models.CredentialsAuthorization,
4939+ 'get_by_token')
4940+ self.mox.StubOutWithMock(creds, 'get_zip')
4941+ models.CredentialsAuthorization.get_by_token(auth_token) \
4942+ .AndReturn(creds)
4943+ creds.get_zip().AndReturn('zip')
4944+
4945+ self.mox.ReplayAll()
4946+
4947+ res = self.client.get(reverse('nova_credentials_authorize',
4948+ args=[auth_token]))
4949+ self.assertEqual(res.status_code, 200)
4950+ self.assertEqual(res['Content-Disposition'],
4951+ 'attachment; filename=%s-test-test-x509.zip' %
4952+ settings.SITE_NAME)
4953+ self.assertContains(res, 'zip')
4954+
4955+ self.mox.VerifyAll()
4956
4957=== added file 'django-nova/src/django_nova/tests/view_tests/image_tests.py'
4958--- django-nova/src/django_nova/tests/view_tests/image_tests.py 1970-01-01 00:00:00 +0000
4959+++ django-nova/src/django_nova/tests/view_tests/image_tests.py 2011-03-04 07:54:59 +0000
4960@@ -0,0 +1,232 @@
4961+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4962+
4963+# Copyright 2010 United States Government as represented by the
4964+# Administrator of the National Aeronautics and Space Administration.
4965+# All Rights Reserved.
4966+#
4967+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4968+# not use this file except in compliance with the License. You may obtain
4969+# a copy of the License at
4970+#
4971+# http://www.apache.org/licenses/LICENSE-2.0
4972+#
4973+# Unless required by applicable law or agreed to in writing, software
4974+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4975+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4976+# License for the specific language governing permissions and limitations
4977+# under the License.
4978+
4979+"""
4980+Unit tests for image views.
4981+"""
4982+
4983+import boto.ec2.image
4984+import boto.ec2.instance
4985+import mox
4986+
4987+from django.core.urlresolvers import reverse
4988+from django_nova import forms
4989+from django_nova import shortcuts
4990+from django_nova.tests.view_tests.base import BaseProjectViewTests, TEST_PROJECT
4991+
4992+
4993+TEST_IMAGE_ID = 'ami_test'
4994+TEST_INSTANCE_ID = 'i-abcdefg'
4995+TEST_KEY = 'foo'
4996+
4997+
4998+class ImageViewTests(BaseProjectViewTests):
4999+ def setUp(self):
5000+ self.ami = boto.ec2.image.Image()
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches