Merge lp:~devcamcar/horizon/combine-django-nova into lp:~hudson-openstack/horizon/trunk
- combine-django-nova
- Merge into 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 |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Devin Carlen | Pending | ||
Review via email: mp+52172@code.launchpad.net |
Commit message
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> › |
2184 | + <a href="../../projects">Projects</a> › |
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> › |
2249 | + <a href="{% url admin_projects %}">Projects</a> › |
2250 | + <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> › |
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> › 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> › <a href="/admin/projects">Projects</a> › <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> › 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> › <a href="/admin/projects">Projects</a> › <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> › 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> › |
2398 | + <a href="{% url admin_projects %}">Projects</a> › |
2399 | + <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> › |
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> › |
2513 | + <a href="{% url admin_users_list %}">Global Roles</a> › |
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> › |
2636 | + <a href="{% url admin_projects %}">Projects</a> › |
2637 | + <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> › |
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> › |
2719 | + <a href="{% url admin_projects %}">Projects</a> › |
2720 | + <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> › |
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> › |
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> </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 (<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> </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> </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> </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(' ')}) |
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.