Added django-nova to openstack-dashboard repo

This commit is contained in:
Devin Carlen 2011-03-02 17:20:17 -08:00
parent e6c6059ec2
commit d4a5bc97f3
188 changed files with 6872 additions and 72 deletions

View File

@ -1,3 +1,4 @@
.dashboard-venv
local/dashboard_openstack.sqlite3
local/local_settings.py
django-nova/src/django_nova.egg-info
openstack-dashboard/.dashboard-venv
openstack-dashboard/local/dashboard_openstack.sqlite3
openstack-dashboard/local/local_settings.py

49
README
View File

@ -1,49 +0,0 @@
OpenStack Dashboard
-------------------
The OpenStack Dashboard is a reference implementation of a Django site that
uses the Django-Nova project to provide web based interactions with the
OpenStack Nova cloud controller.
For more information about the Django-Nova project, please visit:
http://launchpad.net/django-nova
Getting Started
---------------
The first step is to obtain a local copy of the django-nova project:
$ mkdir django-nova
$ cd django-nova
$ bzr init-repo .
$ bzr branch lp:django-nova/trunk
Next we will create the virtualenv for local development. A tool is included to
create one for you:
$ python tools/install_venv.py <path to django-nova/trunk>
Now that the virtualenv is created, you need to configure your local
environment. To do this, create a local_settings.py file in the local/
directory. There is a local_settings.py.example file there that may be used
as a template.
Finally, issue the django syncdb command:
$ tools/with_venv.sh dashboard/manage.py syncdb
If after you have specified the admin user the script appears to hang, it
probably means the installation of Nova being referred to in local_settings.py
is unavailable.
If all is well you should now able to run the server locally:
$ tools/with_venv.sh dashboard/manage.py runserver

176
django-nova/LICENSE Normal file
View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

42
django-nova/README Normal file
View File

@ -0,0 +1,42 @@
OpenStack Django-Nova
---------------------
The Django-Nova project is a Django module that is used to provide web based
interactions with the OpenStack Nova cloud controller.
There is a reference implementation that uses this module located at:
http://launchpad.net/openstack-dashboard
It is highly recommended that you make use of this reference implementation
so that changes you make can be visualized effectively and are consistent.
Using this reference implementation as a development environment will greatly
simplify development of the django-nova module.
Of course, if you are developing your own Django site using django-nova, then
you can disregard this advice.
Getting Started
---------------
Django-Nova uses Buildout (http://www.buildout.org/) to manage local
development. To configure your local Buildout environment:
$ python bootstrap.py
$ bin/buildout
This will install all the dependencies of django-nova and provide some useful
scripts in the bin/ directory:
bin/python provides a python shell for the current buildout.
bin/django provides django functions for the current buildout.
You should now be able to run unit tests as follows:
$ bin/django test

260
django-nova/bootstrap.py Normal file
View File

@ -0,0 +1,260 @@
##############################################################################
#
# Copyright (c) 2006 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
"""
import os, shutil, sys, tempfile, textwrap, urllib, urllib2, subprocess
from optparse import OptionParser
if sys.platform == 'win32':
def quote(c):
if ' ' in c:
return '"%s"' % c # work around spawn lamosity on windows
else:
return c
else:
quote = str
# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
stdout, stderr = subprocess.Popen(
[sys.executable, '-Sc',
'try:\n'
' import ConfigParser\n'
'except ImportError:\n'
' print 1\n'
'else:\n'
' print 0\n'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
has_broken_dash_S = bool(int(stdout.strip()))
# In order to be more robust in the face of system Pythons, we want to
# run without site-packages loaded. This is somewhat tricky, in
# particular because Python 2.6's distutils imports site, so starting
# with the -S flag is not sufficient. However, we'll start with that:
if not has_broken_dash_S and 'site' in sys.modules:
# We will restart with python -S.
args = sys.argv[:]
args[0:0] = [sys.executable, '-S']
args = map(quote, args)
os.execv(sys.executable, args)
# Now we are running with -S. We'll get the clean sys.path, import site
# because distutils will do it later, and then reset the path and clean
# out any namespace packages from site-packages that might have been
# loaded by .pth files.
clean_path = sys.path[:]
import site
sys.path[:] = clean_path
for k, v in sys.modules.items():
if k in ('setuptools', 'pkg_resources') or (
hasattr(v, '__path__') and
len(v.__path__)==1 and
not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
# This is a namespace package. Remove it.
sys.modules.pop(k)
is_jython = sys.platform.startswith('java')
setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
distribute_source = 'http://python-distribute.org/distribute_setup.py'
# parsing arguments
def normalize_to_url(option, opt_str, value, parser):
if value:
if '://' not in value: # It doesn't smell like a URL.
value = 'file://%s' % (
urllib.pathname2url(
os.path.abspath(os.path.expanduser(value))),)
if opt_str == '--download-base' and not value.endswith('/'):
# Download base needs a trailing slash to make the world happy.
value += '/'
else:
value = None
name = opt_str[2:].replace('-', '_')
setattr(parser.values, name, value)
usage = '''\
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
Bootstraps a buildout-based project.
Simply run this script in a directory containing a buildout.cfg, using the
Python that you want bin/buildout to use.
Note that by using --setup-source and --download-base to point to
local resources, you can keep this script from going over the network.
'''
parser = OptionParser(usage=usage)
parser.add_option("-v", "--version", dest="version",
help="use a specific zc.buildout version")
parser.add_option("-d", "--distribute",
action="store_true", dest="use_distribute", default=False,
help="Use Distribute rather than Setuptools.")
parser.add_option("--setup-source", action="callback", dest="setup_source",
callback=normalize_to_url, nargs=1, type="string",
help=("Specify a URL or file location for the setup file. "
"If you use Setuptools, this will default to " +
setuptools_source + "; if you use Distribute, this "
"will default to " + distribute_source +"."))
parser.add_option("--download-base", action="callback", dest="download_base",
callback=normalize_to_url, nargs=1, type="string",
help=("Specify a URL or directory for downloading "
"zc.buildout and either Setuptools or Distribute. "
"Defaults to PyPI."))
parser.add_option("--eggs",
help=("Specify a directory for storing eggs. Defaults to "
"a temporary directory that is deleted when the "
"bootstrap script completes."))
parser.add_option("-t", "--accept-buildout-test-releases",
dest='accept_buildout_test_releases',
action="store_true", default=False,
help=("Normally, if you do not specify a --version, the "
"bootstrap script and buildout gets the newest "
"*final* versions of zc.buildout and its recipes and "
"extensions for you. If you use this flag, "
"bootstrap and buildout will get the newest releases "
"even if they are alphas or betas."))
parser.add_option("-c", None, action="store", dest="config_file",
help=("Specify the path to the buildout configuration "
"file to be used."))
options, args = parser.parse_args()
# if -c was provided, we push it back into args for buildout's main function
if options.config_file is not None:
args += ['-c', options.config_file]
if options.eggs:
eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
else:
eggs_dir = tempfile.mkdtemp()
if options.setup_source is None:
if options.use_distribute:
options.setup_source = distribute_source
else:
options.setup_source = setuptools_source
if options.accept_buildout_test_releases:
args.append('buildout:accept-buildout-test-releases=true')
args.append('bootstrap')
try:
import pkg_resources
import setuptools # A flag. Sometimes pkg_resources is installed alone.
if not hasattr(pkg_resources, '_distribute'):
raise ImportError
except ImportError:
ez_code = urllib2.urlopen(
options.setup_source).read().replace('\r\n', '\n')
ez = {}
exec ez_code in ez
setup_args = dict(to_dir=eggs_dir, download_delay=0)
if options.download_base:
setup_args['download_base'] = options.download_base
if options.use_distribute:
setup_args['no_fake'] = True
ez['use_setuptools'](**setup_args)
if 'pkg_resources' in sys.modules:
reload(sys.modules['pkg_resources'])
import pkg_resources
# This does not (always?) update the default working set. We will
# do it.
for path in sys.path:
if path not in pkg_resources.working_set.entries:
pkg_resources.working_set.add_entry(path)
cmd = [quote(sys.executable),
'-c',
quote('from setuptools.command.easy_install import main; main()'),
'-mqNxd',
quote(eggs_dir)]
if not has_broken_dash_S:
cmd.insert(1, '-S')
find_links = options.download_base
if not find_links:
find_links = os.environ.get('bootstrap-testing-find-links')
if find_links:
cmd.extend(['-f', quote(find_links)])
if options.use_distribute:
setup_requirement = 'distribute'
else:
setup_requirement = 'setuptools'
ws = pkg_resources.working_set
setup_requirement_path = ws.find(
pkg_resources.Requirement.parse(setup_requirement)).location
env = dict(
os.environ,
PYTHONPATH=setup_requirement_path)
requirement = 'zc.buildout'
version = options.version
if version is None and not options.accept_buildout_test_releases:
# Figure out the most recent final version of zc.buildout.
import setuptools.package_index
_final_parts = '*final-', '*final'
def _final_version(parsed_version):
for part in parsed_version:
if (part[:1] == '*') and (part not in _final_parts):
return False
return True
index = setuptools.package_index.PackageIndex(
search_path=[setup_requirement_path])
if find_links:
index.add_find_links((find_links,))
req = pkg_resources.Requirement.parse(requirement)
if index.obtain(req) is not None:
best = []
bestv = None
for dist in index[req.project_name]:
distv = dist.parsed_version
if _final_version(distv):
if bestv is None or distv > bestv:
best = [dist]
bestv = distv
elif distv == bestv:
best.append(dist)
if best:
best.sort()
version = best[-1].version
if version:
requirement = '=='.join((requirement, version))
cmd.append(requirement)
if is_jython:
import subprocess
exitcode = subprocess.Popen(cmd, env=env).wait()
else: # Windows prefers this, apparently; otherwise we would prefer subprocess
exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
if exitcode != 0:
sys.stdout.flush()
sys.stderr.flush()
print ("An error occurred when trying to install zc.buildout. "
"Look above this message for any errors that "
"were output by easy_install.")
sys.exit(exitcode)
ws.add_entry(eggs_dir)
ws.require(requirement)
import zc.buildout.buildout
zc.buildout.buildout.main(args)
if not options.eggs: # clean up temporary egg directory
shutil.rmtree(eggs_dir)

19
django-nova/buildout.cfg Normal file
View File

@ -0,0 +1,19 @@
[buildout]
parts = python django
develop = .
eggs = django-nova
[python]
recipe = zc.recipe.egg
interpreter = python
eggs = ${buildout:eggs}
[django]
recipe = djangorecipe
version = 1.2.4
project = django_nova
projectegg = django_nova
settings = testsettings
test = django_nova
eggs = ${buildout:eggs}

29
django-nova/setup.py Normal file
View File

@ -0,0 +1,29 @@
import os
from setuptools import setup, find_packages
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
name = "django-nova",
version = "0.1",
url = 'https://launchpad.net/django-nova/',
license = 'Apache 2.0',
description = "A Django interface for OpenStack Nova.",
long_description = read('README'),
author = 'Devin Carlen',
author_email = 'devin.carlen@gmail.com',
packages = find_packages('src'),
package_dir = {'': 'src'},
install_requires = ['setuptools', 'boto==1.9b', 'mox>=0.5.0'],
classifiers = [
'Development Status :: 4 - Beta',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP',
]
)

View File

@ -0,0 +1,498 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Nova User API client library.
"""
import base64
import boto
import boto.exception
import httplib
import re
import string
from boto.ec2.regioninfo import RegionInfo
DEFAULT_CLC_URL='http://127.0.0.1:8773'
DEFAULT_REGION='nova'
DEFAULT_ACCESS_KEY='admin'
DEFAULT_SECRET_KEY='admin'
class UserInfo(object):
"""
Information about a Nova user, as parsed through SAX
fields include:
username
accesskey
secretkey
and an optional field containing a zip with X509 cert & rc
file
"""
def __init__(self, connection=None, username=None, endpoint=None):
self.connection = connection
self.username = username
self.endpoint = endpoint
def __repr__(self):
return 'UserInfo:%s' % self.username
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'username':
self.username = str(value)
elif name == 'file':
self.file = base64.b64decode(str(value))
elif name == 'accesskey':
self.accesskey = str(value)
elif name == 'secretkey':
self.secretkey = str(value)
class UserRole(object):
"""
Information about a Nova user's role, as parsed through SAX.
Fields include:
role
"""
def __init__(self, connection=None):
self.connection = connection
self.role = None
def __repr__(self):
return 'UserRole:%s' % self.role
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'role':
self.role = value
else:
setattr(self, name, str(value))
class ProjectInfo(object):
"""
Information about a Nova project, as parsed through SAX
Fields include:
projectname
description
projectManagerId
memberIds
"""
def __init__(self, connection=None):
self.connection = connection
self.projectname = None
self.description = None
self.projectManagerId = None
self.memberIds = []
def __repr__(self):
return 'ProjectInfo:%s' % self.projectname
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'projectname':
self.projectname = value
elif name == 'description':
self.description = value
elif name == 'projectManagerId':
self.projectManagerId = value
elif name == 'memberId':
self.memberIds.append(value)
else:
setattr(self, name, str(value))
class ProjectMember(object):
"""
Information about a Nova project member, as parsed through SAX.
Fields include:
memberId
"""
def __init__(self, connection=None):
self.connection = connection
self.memberId = None
def __repr__(self):
return 'ProjectMember:%s' % self.memberId
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'member':
self.memberId = value
else:
setattr(self, name, str(value))
class HostInfo(object):
"""
Information about a Nova Host, as parsed through SAX:
Hostname
Compute Service Status
Volume Service Status
"""
def __init__(self, connection=None):
self.connection = connection
self.hostname = None
self.compute = None
self.volume = None
self.instance_count = 0
self.volume_count = 0
def __repr__(self):
return 'Host:%s' % self.hostname
# this is needed by the sax parser, so ignore the ugly name
def startElement(self, name, attrs, connection):
return None
# this is needed by the sax parser, so ignore the ugly name
def endElement(self, name, value, connection):
fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name))
setattr(self, fixed_name, value)
class Vpn(object):
"""
Information about a Vpn, as parsed through SAX
fields include:
instance_id
project_id
public_ip
public_port
created_at
internal_ip
state
"""
def __init__(self, connection=None):
self.connection = connection
self.instance_id = None
self.project_id = None
def __repr__(self):
return 'Vpn:%s:%s' % (self.project_id, self.instance_id)
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'instanceId':
self.instance_id = str(value)
elif name == 'projectId':
self.project_id = str(value)
elif name == 'publicIp':
self.public_ip = str(value)
elif name == 'publicPort':
self.public_port = str(value)
elif name == 'createdAt':
self.created_at = str(value)
elif name == 'internalIp':
self.internal_ip = str(value)
else:
setattr(self, name, str(value))
class InstanceType(object):
"""
Information about a Nova instance type, as parsed through SAX.
**Fields include**
* name
* vcpus
* disk_gb
* memory_mb
* flavor_id
"""
def __init__(self, connection=None):
self.connection = connection
self.name = None
self.vcpus = None
self.disk_gb = None
self.memory_mb = None
self.flavor_id = None
def __repr__(self):
return 'InstanceType:%s' % self.name
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == "memoryMb":
self.memory_mb = str(value)
elif name == "flavorId":
self.flavor_id = str(value)
elif name == "diskGb":
self.disk_gb = str(value)
else:
setattr(self, name, str(value))
class StatusResponse(object):
def __init__(self, connection=None):
self.connection = connection
self.status = None
def __repr__(self):
return 'Status:%s' % self.status
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
setattr(self, name, str(value))
class NovaAdminClient(object):
def __init__(self, clc_url=DEFAULT_CLC_URL, region=DEFAULT_REGION,
access_key=DEFAULT_ACCESS_KEY, secret_key=DEFAULT_SECRET_KEY,
**kwargs):
parts = self.split_clc_url(clc_url)
self.clc_url = clc_url
self.region = region
self.access = access_key
self.secret = secret_key
self.apiconn = boto.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
is_secure=parts['is_secure'],
region=RegionInfo(None,
region,
parts['ip']),
port=parts['port'],
path='/services/Admin',
**kwargs)
self.apiconn.APIVersion = 'nova'
def connection_for(self, username, project, clc_url=None, region=None,
**kwargs):
"""
Returns a boto ec2 connection for the given username.
"""
if not clc_url:
clc_url = self.clc_url
if not region:
region = self.region
parts = self.split_clc_url(clc_url)
user = self.get_user(username)
access_key = '%s:%s' % (user.accesskey, project)
return boto.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=user.secretkey,
is_secure=parts['is_secure'],
region=RegionInfo(None,
self.region,
parts['ip']),
port=parts['port'],
path='/services/Cloud',
**kwargs)
def split_clc_url(self, clc_url):
"""
Splits a cloud controller endpoint url.
"""
parts = httplib.urlsplit(clc_url)
is_secure = parts.scheme == 'https'
ip, port = parts.netloc.split(':')
return {'ip': ip, 'port': int(port), 'is_secure': is_secure}
def get_users(self):
""" grabs the list of all users """
return self.apiconn.get_list('DescribeUsers', {}, [('item', UserInfo)])
def get_user(self, name):
""" grab a single user by name """
try:
return self.apiconn.get_object('DescribeUser', {'Name': name}, UserInfo)
except boto.exception.BotoServerError, e:
if e.status == 400 and e.error_code == 'NotFound':
return None
raise
def has_user(self, username):
""" determine if user exists """
return self.get_user(username) != None
def create_user(self, username):
""" creates a new user, returning the userinfo object with access/secret """
return self.apiconn.get_object('RegisterUser', {'Name': username}, UserInfo)
def delete_user(self, username):
""" deletes a user """
return self.apiconn.get_object('DeregisterUser', {'Name': username}, UserInfo)
def get_roles(self, project_roles=True):
"""Returns a list of available roles."""
return self.apiconn.get_list('DescribeRoles',
{'ProjectRoles': project_roles},
[('item', UserRole)])
def get_user_roles(self, user, project=None):
"""Returns a list of roles for the given user.
Omitting project will return any global roles that the user has.
Specifying project will return only project specific roles.
"""
params = {'User':user}
if project:
params['Project'] = project
return self.apiconn.get_list('DescribeUserRoles',
params,
[('item', UserRole)])
def add_user_role(self, user, role, project=None):
"""
Add a role to a user either globally or for a specific project.
"""
return self.modify_user_role(user, role, project=project,
operation='add')
def remove_user_role(self, user, role, project=None):
"""
Remove a role from a user either globally or for a specific project.
"""
return self.modify_user_role(user, role, project=project,
operation='remove')
def modify_user_role(self, user, role, project=None, operation='add',
**kwargs):
"""
Add or remove a role for a user and project.
"""
params = {'User': user,
'Role': role,
'Project': project,
'Operation': operation}
return self.apiconn.get_status('ModifyUserRole', params)
def get_projects(self, user=None):
"""
Returns a list of all projects.
"""
if user:
params = {'User': user}
else:
params = {}
return self.apiconn.get_list('DescribeProjects',
params,
[('item', ProjectInfo)])
def get_project(self, name):
"""
Returns a single project with the specified name.
"""
project = self.apiconn.get_object('DescribeProject',
{'Name': name},
ProjectInfo)
if project.projectname != None:
return project
def create_project(self, projectname, manager_user, description=None,
member_users=None):
"""
Creates a new project.
"""
params = {'Name': projectname,
'ManagerUser': manager_user,
'Description': description,
'MemberUsers': member_users}
return self.apiconn.get_object('RegisterProject', params, ProjectInfo)
def delete_project(self, projectname):
"""
Permanently deletes the specified project.
"""
return self.apiconn.get_object('DeregisterProject',
{'Name': projectname},
ProjectInfo)
def get_project_members(self, name):
"""
Returns a list of members of a project.
"""
return self.apiconn.get_list('DescribeProjectMembers',
{'Name': name},
[('item', ProjectMember)])
def add_project_member(self, user, project):
"""
Adds a user to a project.
"""
return self.modify_project_member(user, project, operation='add')
def remove_project_member(self, user, project):
"""
Removes a user from a project.
"""
return self.modify_project_member(user, project, operation='remove')
def modify_project_member(self, user, project, operation='add'):
"""
Adds or removes a user from a project.
"""
params = {'User': user,
'Project': project,
'Operation': operation}
return self.apiconn.get_status('ModifyProjectMember', params)
def get_zip(self, user, project):
"""
Returns the content of a zip file containing novarc and access credentials.
"""
params = {'Name': user, 'Project': project}
zip = self.apiconn.get_object('GenerateX509ForUser', params, UserInfo)
return zip.file
def start_vpn(self, project):
"""
Starts the vpn for a user
"""
return self.apiconn.get_object('StartVpn', {'Project': project}, Vpn)
def get_vpns(self):
"""Return a list of vpn with project name"""
return self.apiconn.get_list('DescribeVpns', {}, [('item', Vpn)])
def get_hosts(self):
return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)])
def get_instance_types(self):
"""Grabs the list of all users."""
return self.apiconn.get_list('DescribeInstanceTypes', {},
[('item', InstanceType)])
def disable_project_credentials(self, project):
"""Revoke project credentials and kill the cloudpipe/vpn instance"""
return self.apiconn.get_object('DisableProjectCredentials',
{'Project': project}, StatusResponse)

View File

@ -0,0 +1,38 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Manage connections to Nova's admin API.
"""
from django.conf import settings
from django_nova import adminclient
def get_nova_admin_connection():
"""
Returns a Nova administration connection.
"""
return adminclient.NovaAdminClient (
clc_url=settings.NOVA_DEFAULT_ENDPOINT,
region=settings.NOVA_DEFAULT_REGION,
access_key=settings.NOVA_ACCESS_KEY,
secret_key=settings.NOVA_SECRET_KEY
)

View File

@ -0,0 +1,95 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Better wrappers for errors from Nova's admin api.
"""
import boto.exception
from django.shortcuts import redirect
from django.core import exceptions as core_exceptions
class NovaServerError(Exception):
"""
Consumes a BotoServerError and gives more meaningful errors.
"""
def __init__(self, ec2error):
self.status = ec2error.status
self.message = ec2error.reason
def __str__(self):
return self.message
class NovaApiError(Exception):
"""
Used when Nova returns a 400 Bad Request status.
"""
def __init__(self, ec2error):
self.message = ec2error.error_message
def __str__(self):
return self.message
class NovaUnavailableError(NovaServerError):
"""
Used when Nova returns a 503 Service Unavailable status.
"""
pass
class NovaUnauthorizedError(core_exceptions.PermissionDenied):
"""
Used when Nova returns a 401 Not Authorized status.
"""
pass
def wrap_nova_error(func):
"""
Used to decorate a function that interacts with boto. It will catch
and convert boto server errors and reraise as a more specific nova error.
"""
def decorator(*args, **kwargs):
try:
return func(*args, **kwargs)
except boto.exception.BotoServerError, e:
if e.status == 400 and e.error_code == 'ApiError':
raise NovaApiError(e)
elif e.status == 401:
raise NovaUnauthorizedError()
elif e.status == 503:
raise NovaUnavailableError(e)
raise NovaServerError(e)
return decorator
def handle_nova_error(func):
"""
Decorator for handling nova errors in a generalized way.
"""
def decorator(*args, **kwargs):
try:
return func(*args, **kwargs)
except NovaUnavailableError:
return redirect('nova_unavailable')
return decorator

View File

@ -0,0 +1,262 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Forms used by various views.
"""
import re
from django import forms
from django.contrib.auth import models as auth_models
from django_nova.connection import get_nova_admin_connection
from django_nova.exceptions import wrap_nova_error
# TODO: Store this in settings.
MAX_VOLUME_SIZE = 100
alphanumeric_re = re.compile(r'^\w+$')
@wrap_nova_error
def get_instance_type_choices():
"""
Returns list of instance types from nova admin api
"""
nova = get_nova_admin_connection()
instance_types = nova.get_instance_types()
rv = []
for t in instance_types:
rv.append((t.name, "%s (%sMB memory, %s cpu, %sGB space)" % \
(t.name, t.memory_mb, t.vcpus, t.disk_gb)))
return rv
def get_instance_choices(project):
choices = [(i.id, i.id) for i in project.get_instances()]
if not len(choices):
choices = [('', 'none available')]
return choices
def get_key_pair_choices(project):
choices = [(k.name, k.name) for k in project.get_key_pairs()]
if not len(choices):
choices = [('', 'none available')]
return choices
#def get_security_group_choices(project):
# choices = [(g.name, g.description) for g in project.get_security_groups()]
# if len(choices) == 0:
# choices = [('', 'none available')]
# return choices
def get_available_volume_choices(project):
choices = [(v.id, '%s %s - %dGB' % (v.id, v.displayName, v.size)) for v in \
project.get_volumes() if v.status != "in-use"]
if not len(choices):
choices = [('', 'none available')]
return choices
def get_protocols():
return (
('tcp', 'tcp'),
('udp', 'udp'),
)
@wrap_nova_error
def get_roles(project_roles=True):
nova = get_nova_admin_connection()
roles = nova.get_roles(project_roles=project_roles)
return [(role.role, role.role) for role in roles]
@wrap_nova_error
def get_members(project):
nova = get_nova_admin_connection()
members = nova.get_project_members(project)
return [str(user.memberId) for user in members]
@wrap_nova_error
def set_project_roles(projectname, username, roles):
nova = get_nova_admin_connection()
# hacky work around to interface correctly with multiple select form
_remove_roles(projectname, username)
for role in roles:
nova.add_user_role(username, str(role), projectname)
def _remove_roles(project, username):
nova = get_nova_admin_connection()
userroles = nova.get_user_roles(username, project)
roles = [str(role.role) for role in userroles]
for role in roles:
if role == "developer":
nova.remove_user_role(username, "developer", project)
if role == "sysadmin":
nova.remove_user_role(username, "sysadmin", project)
if role == "netadmin":
nova.remove_user_role(username, "netadmin", project)
class ProjectFormBase(forms.Form):
def __init__(self, project, *args, **kwargs):
self.project = project
super(ProjectFormBase, self).__init__(*args, **kwargs)
class LaunchInstanceForm(forms.Form):
# nickname = forms.CharField()
# description = forms.CharField()
count = forms.ChoiceField(choices=[(x, x) for x in range(1, 6)])
size = forms.ChoiceField()
key_name = forms.ChoiceField()
#security_group = forms.ChoiceField()
user_data = forms.CharField(required=False, widget=forms.widgets.Textarea(attrs={'rows': 4}))
def __init__(self, project, *args, **kwargs):
forms.Form.__init__(self, *args, **kwargs)
#self.fields['security_group'].choices = get_security_group_choices(project)
self.fields['key_name'].choices = get_key_pair_choices(project)
self.fields['size'].choices = get_instance_type_choices()
class UpdateInstanceForm(forms.Form):
nickname = forms.CharField(required=False, label="Name")
description = forms.CharField(required=False, widget=forms.Textarea, max_length=70)
def __init__(self, instance, *args, **kwargs):
forms.Form.__init__(self, *args, **kwargs)
self.fields['nickname'].initial = instance.displayName
self.fields['description'].initial = instance.displayDescription
class UpdateImageForm(forms.Form):
nickname = forms.CharField(required=False, label="Name")
description = forms.CharField(required=False, widget=forms.Textarea, max_length=70)
def __init__(self, image, *args, **kwargs):
forms.Form.__init__(self, *args, **kwargs)
self.fields['nickname'].initial = image.displayName
self.fields['description'].initial = image.description
class CreateKeyPairForm(ProjectFormBase):
name = forms.RegexField(regex=alphanumeric_re)
def clean_name(self):
name = self.cleaned_data['name']
if self.project.has_key_pair(name):
raise forms.ValidationError('A key named %s already exists.' % name)
return name
class CreateSecurityGroupForm(ProjectFormBase):
name = forms.RegexField(regex=alphanumeric_re)
description = forms.CharField()
def clean_name(self):
name = self.cleaned_data['name']
if self.project.has_security_group(name):
raise forms.ValidationError('A security group named %s already exists.' % name)
return name
class AuthorizeSecurityGroupRuleForm(forms.Form):
protocol = forms.ChoiceField(choices=get_protocols())
from_port = forms.IntegerField(min_value=1, max_value=65535)
to_port = forms.IntegerField(min_value=1, max_value=65535)
class CreateVolumeForm(forms.Form):
size = forms.IntegerField(label='Size (in GB)', min_value=1, max_value=MAX_VOLUME_SIZE)
nickname = forms.CharField()
description = forms.CharField()
class AttachVolumeForm(ProjectFormBase):
volume = forms.ChoiceField()
instance = forms.ChoiceField()
device = forms.CharField(initial='/dev/vdb')
def __init__(self, project, *args, **kwargs):
super(AttachVolumeForm, self).__init__(project, *args, **kwargs)
self.fields['volume'].choices = get_available_volume_choices(project)
self.fields['instance'].choices = get_instance_choices(project)
class ProjectForm(forms.Form):
projectname = forms.CharField(label="Project Name", max_length=20)
description = forms.CharField(label="Description",
widget=forms.widgets.Textarea())
manager = forms.ModelChoiceField(queryset=auth_models.User.objects.all(),
label="Project Manager")
class GlobalRolesForm(forms.Form):
role = forms.MultipleChoiceField(label='Roles', required=False)
def __init__(self, *args, **kwargs):
super(GlobalRolesForm, self).__init__(*args, **kwargs)
self.fields['role'].choices = get_roles(project_roles=False)
class ProjectUserForm(forms.Form):
role = forms.MultipleChoiceField(label='Roles', required=False)
def __init__(self, project, user, *args, **kwargs):
super(ProjectUserForm, self).__init__(*args, **kwargs)
self.project = project
self.user = user
self.fields['role'].choices = get_roles()
def save(self):
set_project_roles(self.project.projectname,
self.user.username,
self.cleaned_data['role'])
class AddProjectUserForm(forms.Form):
username = forms.ModelChoiceField(queryset='',
label='Username',
empty_label='Select a Username')
role = forms.MultipleChoiceField(label='Roles')
def __init__(self, *args, **kwargs):
project = kwargs.pop('project')
super(AddProjectUserForm, self).__init__(*args, **kwargs)
members = get_members(project)
self.fields['username'].queryset = \
auth_models.User.objects.exclude(username__in=members)
self.fields['role'].choices = get_roles()
class SendCredentialsForm(forms.Form):
users = forms.MultipleChoiceField(label='Users', required=True)
def __init__(self, *args, **kwargs):
query_list = kwargs.pop('query_list')
super(SendCredentialsForm, self).__init__(*args, **kwargs)
self.fields['users'].choices = [(choices, choices) for choices in query_list]

View File

@ -0,0 +1,37 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Management commands for synchronizing the Django auth database and Nova
users database.
"""
from django.core.management.base import NoArgsCommand
from django.contrib.auth.models import User
from django_nova.connection import get_nova_admin_connection
class Command(NoArgsCommand):
help = 'Creates nova users for all users in the django auth database.'
def handle_noargs(self, **options):
nova = get_nova_admin_connection()
users = User.objects.all()
for user in users:
if not nova.has_user(user.username):
self.stdout.write('creating user %s... ' % user.username)
nova.create_user(user.username)
self.stdout.write('ok\n')

View File

@ -0,0 +1,340 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Simple API for interacting with Nova projects.
"""
import boto
import boto.ec2.volume
import boto.exception
import boto.s3
from django.conf import settings
from django_nova.connection import get_nova_admin_connection
from django_nova.exceptions import wrap_nova_error
class ProjectManager(object):
def __init__(self, username, project, region):
self.username = username
self.projectname = project.projectname
self.projectManagerId = project.projectManagerId
self.region = region
def get_nova_connection(self):
"""
Returns a boto connection for a user's project.
"""
nova = get_nova_admin_connection()
return nova.connection_for(self.username,
self.projectname,
clc_url=self.region['endpoint'],
region=self.region['name'])
def get_zip(self):
"""
Returns a buffer of a zip file containing signed credentials
for the project's Nova user.
"""
nova = get_nova_admin_connection()
return nova.get_zip(self.username, self.projectname)
def get_images(self, image_ids=None):
conn = self.get_nova_connection()
images = conn.get_all_images(image_ids=image_ids)
sorted_images = [i for i in images if i.ownerId == self.username] + \
[i for i in images if i.ownerId != self.username]
return [i for i in sorted_images if i.type == 'machine' and i.location.split('/')[0] != 'nova']
def get_image(self, image_id):
try:
return self.get_images(image_ids=[image_id,])[0]
except IndexError:
return None
@wrap_nova_error
def deregister_image(self, image_id):
"""
Removes the image's listing but leaves the image
and manifest in the object store in tact.
"""
conn = self.get_nova_connection()
return conn.deregister_image(image_id)
@wrap_nova_error
def update_image(self, image_id, display_name=None, description=None):
conn = self.get_nova_connection()
params = {
'ImageId': image_id,
'DisplayName': display_name,
'Description': description
}
return conn.get_object('UpdateImage', params, boto.ec2.image.Image)
@wrap_nova_error
def modify_image_attribute(self, image_id, attribute=None, operation=None,
groups='all'):
conn = self.get_nova_connection()
return conn.modify_image_attribute(image_id,
attribute='launchPermission',
operation='remove',
groups='all',)
@wrap_nova_error
def run_instances(self, image_id, **kwargs):
"""
Runs instances of the specified image id.
"""
conn = self.get_nova_connection()
return conn.run_instances(image_id, **kwargs)
def get_instance_count(self):
"""
Returns the number of active instances in this project or None if unknown.
"""
try:
return len(self.get_instances())
except:
return None
@wrap_nova_error
def get_instances(self):
"""
Returns all instances in this project.
"""
conn = self.get_nova_connection()
reservations = conn.get_all_instances()
instances = []
for reservation in reservations:
for instance in reservation.instances:
instances.append(instance)
return instances
@wrap_nova_error
def get_instance(self, instance_id):
"""
Returns detail about the specified instance.
"""
conn = self.get_nova_connection()
# TODO: Refactor this once nova's describe_instances filters by instance_id.
reservations = conn.get_all_instances()
for reservation in reservations:
for instance in reservation.instances:
if instance.id == instance_id:
return instance
return None
@wrap_nova_error
def update_instance(self, instance_id, updates):
conn = self.get_nova_connection()
params = {'InstanceId': instance_id, 'DisplayName': updates['nickname'],
'DisplayDescription': updates['description']}
return conn.get_object('UpdateInstance', params,
boto.ec2.instance.Instance)
def get_instance_graph(self, region, instance_id, graph_name):
# TODO(devcamcar): Need better support for multiple regions.
# Need a way to get object store by region.
s3 = boto.s3.connection.S3Connection (
aws_access_key_id=settings.NOVA_ACCESS_KEY,
aws_secret_access_key=settings.NOVA_SECRET_KEY,
is_secure=False,
calling_format=boto.s3.connection.OrdinaryCallingFormat(),
port=3333,
host=settings.NOVA_CLC_IP
)
key = '_%s.monitor' % instance_id
try:
bucket = s3.get_bucket(key, validate=False)
except boto.exception.S3ResponseError, e:
if e.code == "NoSuchBucket":
return None
else:
raise e
key = bucket.get_key(graph_name)
return key.read()
@wrap_nova_error
def terminate_instance(self, instance_id):
""" Terminates the specified instance within this project. """
conn = self.get_nova_connection()
conn.terminate_instances([instance_id])
@wrap_nova_error
def get_security_groups(self):
"""
Returns all security groups associated with this project.
"""
conn = self.get_nova_connection()
groups = []
for g in conn.get_all_security_groups():
# Do not show vpn group.
#if g.name != 'vpn-secgroup':
groups.append(g)
return groups
@wrap_nova_error
def get_security_group(self, name):
"""
Returns the specified security group for this project.
"""
conn = self.get_nova_connection()
try:
return conn.get_all_security_groups(groupnames=name.encode('ASCII'))[0]
except IndexError:
return None
@wrap_nova_error
def has_security_group(self, name):
"""
Indicates whether a security group with the specified name exists in this project.
"""
return self.get_security_group(name) is not None
@wrap_nova_error
def create_security_group(self, name, description):
"""
Creates a new security group in this project.
"""
conn = self.get_nova_connection()
return conn.create_security_group(name, description)
@wrap_nova_error
def delete_security_group(self, name):
"""
Deletes a security group from the project.
"""
conn = self.get_nova_connection()
return conn.delete_security_group(name = name)
@wrap_nova_error
def authorize_security_group(self, group_name, ip_protocol, from_port, to_port):
"""
Authorizes a rule for the specified security group.
"""
conn = self.get_nova_connection()
return conn.authorize_security_group (
group_name = group_name,
ip_protocol = ip_protocol,
from_port = from_port,
to_port = to_port,
cidr_ip = '0.0.0.0/0'
)
@wrap_nova_error
def revoke_security_group(self, group_name, ip_protocol, from_port, to_port):
"""
Revokes a rule for the specified security group.
"""
conn = self.get_nova_connection()
return conn.revoke_security_group (
group_name = group_name,
ip_protocol = ip_protocol,
from_port = from_port,
to_port = to_port,
cidr_ip = '0.0.0.0/0'
)
@wrap_nova_error
def get_key_pairs(self):
"""
Returns all key pairs associated with this project.
"""
conn = self.get_nova_connection()
keys = []
for k in conn.get_all_key_pairs():
# Do not show vpn key.
if k.name != 'vpn-key':
keys.append(k)
return keys
@wrap_nova_error
def get_key_pair(self, name):
"""
Returns the specified security group for this project.
"""
conn = self.get_nova_connection()
try:
return conn.get_all_key_pairs(keynames=name.encode('ASCII'))[0]
except IndexError:
return None
@wrap_nova_error
def has_key_pair(self, name):
"""
Indicates whether a key pair with the specified name exists in this project.
"""
return self.get_key_pair(name) != None
@wrap_nova_error
def create_key_pair(self, name):
"""
Creates a new key pair for this project.
"""
conn = self.get_nova_connection()
return conn.create_key_pair(name)
@wrap_nova_error
def delete_key_pair(self, name):
"""
Deletes a new key pair from this project.
"""
conn = self.get_nova_connection()
conn.delete_key_pair(name)
@wrap_nova_error
def get_volumes(self):
"""
Returns all volumes in this project.
"""
conn = self.get_nova_connection()
return conn.get_all_volumes()
@wrap_nova_error
def create_volume(self, size, display_name=None, display_description=None,
snapshot=None):
conn = self.get_nova_connection()
params = {'Size': size, 'DisplayName': display_name,
'DisplayDescription': display_description}
return conn.get_object('CreateVolume', params, boto.ec2.volume.Volume)
@wrap_nova_error
def delete_volume(self, volume_id):
conn = self.get_nova_connection()
return conn.delete_volume(volume_id)
@wrap_nova_error
def attach_volume(self, volume_id, instance_id, device):
conn = self.get_nova_connection()
return conn.attach_volume(volume_id, instance_id, device)
@wrap_nova_error
def detach_volume(self, volume_id):
conn = self.get_nova_connection()
return conn.detach_volume(volume_id)

View File

@ -0,0 +1,121 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Database models for authorization credentials and synchronizing Nova users.
"""
import datetime
import random
import re
import sha
from django.conf import settings
from django.contrib.auth import models as auth_models
from django.contrib.sites import models as site_models
from django.core import mail
from django.db import models
from django.db.models.signals import post_save
from django.template.loader import render_to_string
from django_nova.connection import get_nova_admin_connection
SHA1_RE=re.compile('^[a-f0-9]{40}$')
class CredentialsAuthorization(models.Model):
username = models.CharField(max_length=128)
project = models.CharField(max_length=128)
auth_token = models.CharField(max_length=40)
auth_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s/%s:%s' % (self.username, self.project, self.auth_token)
@classmethod
def get_by_token(cls, token):
if SHA1_RE.search(token):
try:
credentials = cls.objects.get(auth_token=token)
except cls.DoesNotExist:
return None
if not credentials.auth_token_expired():
return credentials
return None
@classmethod
def authorize(cls, username, project):
return cls.objects.create(username=username,
project=project,
auth_token=cls.create_auth_token(username))
@staticmethod
def create_auth_token(username):
salt = sha.new(str(random.random())).hexdigest()[:5]
return sha.new(salt+username).hexdigest()
def auth_token_expired(self):
expiration_date = datetime.timedelta(days=int(settings.CREDENTIAL_AUTHORIZATION_DAYS))
return self.auth_date + expiration_date <= datetime.datetime.now()
def get_download_url(self):
return settings.CREDENTIAL_DOWNLOAD_URL + self.auth_token
def get_zip(self):
nova = get_nova_admin_connection()
self.delete()
return nova.get_zip(self.username, self.project)
def credentials_post_save(sender, instance, created, *args, **kwargs):
"""
Creates a Nova User when a new Django User is created.
"""
if created:
site = site_models.Site.objects.get_current()
user = auth_models.User.objects.get(username=instance.username)
context = {
'user': user,
'download_url': instance.get_download_url(),
'dashboard_url': 'http://%s/' % site.domain
}
subject = render_to_string('credentials/credentials_email_subject.txt')
body = render_to_string('credentials/credentials_email.txt', context)
message = mail.EmailMessage(subject=subject, body=body, to=[user.email])
message.send(fail_silently=False)
post_save.connect(credentials_post_save,
CredentialsAuthorization,
dispatch_uid='django_nova.CredentialsAuthorization.post_save')
def user_post_save(sender, instance, created, *args, **kwargs):
"""
Creates a Nova User when a new Django User is created.
"""
# NOTE(devcamcar): If running unit tests, don't use a real endpoint.
if settings.NOVA_DEFAULT_ENDPOINT == 'none':
return
if created:
nova = get_nova_admin_connection()
if not nova.has_user(instance.username):
nova.create_user(instance.username)
post_save.connect(user_post_save,
auth_models.User,
dispatch_uid='django_nova.User.post_save')

View File

@ -0,0 +1,131 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Helper methods for commonly used operations.
"""
from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django_nova import manager
from django_nova.connection import get_nova_admin_connection
from django_nova.exceptions import wrap_nova_error
@wrap_nova_error
def get_project_or_404(request, project_id):
"""
Returns a project or 404s if it doesn't exist.
"""
# Ensure that a connection is never attempted for a user that is unauthenticated.
if not request.user.is_authenticated:
raise PermissionDenied('User not authenticated')
nova = get_nova_admin_connection()
project = nova.get_project(project_id)
region = get_current_region(request)
if not project:
raise Http404('Project %s does not exist.' % project_id)
return manager.ProjectManager(request.user, project, region)
@wrap_nova_error
def get_projects(user):
"""
Returns a list of projects for a user.
"""
#key = 'projects.%s' % user
#projects = cache.get(key)
#if not projects:
# nova = get_nova_admin_connection()
# projects = nova.get_projects(user=user)
# cache.set(key, projects, 30)
#return projects
nova = get_nova_admin_connection()
return nova.get_projects(user=user)
@wrap_nova_error
def get_all_regions():
"""
Returns a list of all regions.
"""
regions = cache.get('regions')
if not regions:
nova = get_nova_admin_connection()
conn = nova.connection_for(settings.NOVA_ADMIN_USER, settings.NOVA_PROJECT)
results = conn.get_all_regions()
regions = [{'name': r.name, 'endpoint': r.endpoint} for r in results]
cache.set('regions', regions, 60 * 60 * 24)
return regions
def get_region(region_name):
regions = get_all_regions()
try:
return [r for r in regions if r['name'] == region_name][0]
except IndexError:
return None
def get_current_region(request):
"""
Returns the currently selected region for a user.
"""
region_name = request.session.get('region', settings.NOVA_DEFAULT_REGION)
return get_region(region_name)
def set_current_region(request, region_name):
"""
Sets the current region selection for a user.
"""
request.session['region'] = region_name
@wrap_nova_error
def get_user_image_permissions(username, project_name):
"""
Returns true if user is a sysadmin and can modify image attributes.
"""
nova = get_nova_admin_connection()
user_has_modify_permissions = False
# checks global roles, if user is a sysadmin they can modify image attribtues.
if not user_has_modify_permissions:
for role in nova.get_user_roles(username):
if role.role == "sysadmin":
user_has_modify_permissions = True
# checks project roles, if user is a sysadmin they can modify image attribtues.
if not user_has_modify_permissions:
for role in nova.get_user_roles(username, project_name):
if role.role == "sysadmin":
user_has_modify_permissions = True
return user_has_modify_permissions

View File

@ -0,0 +1,45 @@
{% extends "admin/django_nova/project/base_projects.html" %}
{% load admin_modify adminmedia %}
{% block extrahead %}
{{ block.super }}
{{ media }}
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %} change-form{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="/admin">Home</a> &rsaquo;
<a href="../../projects">Projects</a> &rsaquo;
Add Project
</div>
{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
{% endblock %}
<form action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="module aligned {{ fieldset.classes }}">
{% for field in form.visible_fields %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}{{ field }}
{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
</div>
{% endfor %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
</fieldset>
<input type="submit" value="Save" />
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,69 @@
{% extends "admin/django_nova/project/base_projects.html" %}
{% load admin_modify adminmedia %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
<script type="text/javascript" charset="utf-8">
django.jQuery(function(){
django.jQuery.each(django.jQuery(".edit_user_roles select[multiple]"), function () {
// "Locations" can be any label you want
SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
});
})
</script>
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %} change-form{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="/admin">Home</a> &rsaquo;
<a href="{% url admin_projects %}">Projects</a> &rsaquo;
<a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
User
{{form.ProjectUserForm}}
</div>
{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
{% endblock %}
<form class="edit_user_roles" action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="module aligned {{ fieldset.classes }}">
<input type="hidden" name="username" value="{{user.id}}" id="username" />
{% for field in form.visible_fields %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}{{ field }}
{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
</div>
{% endfor %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
</fieldset>
<div class="submit-row">
<p class="deletelink-box">
<a href="{% url admin_project_delete_user project.projectname user.username %}" class="deletelink">Delete</a>
</p>
<input type="submit" value="Save" class="default" />
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "admin/change_list.html" %}
{% block extrastyle %}
{{block.super}}
<link rel="stylesheet" type="text/css" href="{{settings.MEDIA_URL}}/stylesheets/extra_admin.css" />
{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="/admin/">Home</a>&nbsp;&rsaquo;&nbsp;Projects</div>{% endblock %}
{% block content %}
<div id="content-main">
<div class="module filtered" id="changelist">
<div id="toolbartable">
{% block innercontent %}{% endblock %}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,3 @@
{% extends "admin/change_list.html" %}
{% load admin_extras %}
{% block result_list %}{% project_result_list cl %}{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "admin/change_list.html" %}
{% block extrastyle %}
{{block.super}}
<link rel="stylesheet" type="text/css" href="{{settings.MEDIA_URL}}/stylesheets/extra_admin.css" />
{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="/admin/">Home</a>&nbsp;&rsaquo;&nbsp;<a href="/admin/projects">Projects</a>&nbsp;&rsaquo;&nbsp; <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a>&nbsp;&rsaquo;&nbsp;Delete</div>{% endblock %}
{% block content %}
<div id="content-main">
<div class="module filtered" id="changelist">
<div id="toolbartable">
<h1>Delete Project</h1>
<p>Do you really want to delete this project?</p>
<ul>
<li><a href="{% url admin_project project.projectname %}">{{project.projectname}}</a></li>
</ul>
<form action="." method="post">
{% csrf_token %}
<p><input type="submit" value="Delete"></p>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "admin/change_list.html" %}
{% block extrastyle %}
{{block.super}}
<link rel="stylesheet" type="text/css" href="{{settings.MEDIA_URL}}/stylesheets/extra_admin.css" />
{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="/admin/">Home</a>&nbsp;&rsaquo;&nbsp;<a href="/admin/projects">Projects</a>&nbsp;&rsaquo;&nbsp; <a href="{% url admin_project project.projectname %}">{{project.projectname}}</a>&nbsp;&rsaquo;&nbsp;Delete</div>{% endblock %}
{% block content %}
<div id="content-main">
<div class="module filtered" id="changelist">
<div id="toolbartable">
<h1>Remove User From Project</h1>
<p>Do you really want to remove this user from project?</p>
<ul>
<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>
</ul>
<form action="." method="post">
{% csrf_token %}
<p><input type="submit" value="Delete"></p>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,95 @@
{% extends "admin/django_nova/project/base_projects.html" %}
{% load admin_modify adminmedia %}
{% block extrahead %}
{{ block.super }}
{{ media }}
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %} change-form{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="/admin">Home</a> &rsaquo;
<a href="{% url admin_projects %}">Projects</a> &rsaquo;
<a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
Edit Project
{{form.ProjectEditForm}}
</div>
{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
{% endblock %}
<form action="#" method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="module aligned {{ fieldset.classes }}">
{% for field in form.visible_fields %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}{{ field }}
{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
</div>
{% endfor %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<div class="form-row">
<label for="project_name">Project Name</label>
<span id="project_name" style="display: block; padding-top: 5px; padding-left: 5px; float: left;"><em>{{projectname}}</em></span>
</div>
<div class="form-row">
<label for="project_description">Description</label>
<span id="project_description" style="display: block; padding-top: 5px; padding-left: 5px; float: left;"><em>{{description}}</em></span>
</div>
<div class="form-row">
<label for="project_manager">Project Manager</label>
<span id="project_manager" style="display: block; padding-top: 5px; padding-left: 5px; float: left;"><em>{{manager}}</em></span>
</div>
</fieldset>
<div class="submit-row">
<p class="deletelink-box">
<a href="{% url delete_project project.projectname %}" class="deletelink">Delete Project</a>
</p>
{# <input type="submit" value="Save" class="default" /> #}
</div>
</form>
<table cellspacing="0" style="margin-top: 20px;">
<thead>
<tr>
<th>Username</th>
<th>Project Roles</th>
<th>Global Roles</th>
</tr>
</thead>
{% for user in users %}
<tr class="{% cycle 'row1' 'row2' %}">
<td>
<a href="{%url project_user project.projectname user.memberId %}">{{user.memberId}} {% if user.memberId == project.projectManagerId %}(<em>project manager</em>){%endif %}</a>
</td>
<td>
{{user.project_roles}}
</td>
<td>
{{user.global_roles}}
</td>
</tr>
{% endfor %}
</table>
<ul class="object-tools">
<li>
<a class="addlink" href="{% url add_project_user project.projectname %}">Add User</a>
</li>
</ul>
</div>
{% endblock %}

View File

@ -0,0 +1,71 @@
{% extends "admin/django_nova/project/base_projects.html" %}
{% load admin_modify adminmedia %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
<script type="text/javascript" charset="utf-8">
django.jQuery(function(){
django.jQuery.each(django.jQuery("#global_users select"), function () {
// "Locations" can be any label you want
SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
});
})
</script>
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %} change-form{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="/admin">Home</a> &rsaquo;
<a href="{% url admin_users_list %}">Global Roles</a> &rsaquo;
{{user.username}}
</div>
{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
{% endblock %}
<form action="." method="post" enctype="multipart/form-data" id="global_users">
{% csrf_token %}
<fieldset class="module aligned {{ fieldset.classes }}">
<div class="form-row">
<label for="id_username">Username</label>
<span>{{user.username}}</span>
</div>
<input type="hidden" name="username" value="{{user.id}}" id="username" />
{% for field in form.visible_fields %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}{{ field }}
{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
</div>
{% endfor %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
</fieldset>
<div class="submit-row">
<p class="deletelink-box">
{# <a href="{% url admin_project_delete_user project.projectname user.username %}" class="deletelink">Delete</a> #}
</p>
<input type="submit" value="Save" class="default" />
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,42 @@
{% extends "admin/django_nova/project/base_projects.html" %}
{% block extrahead %}
{{ block.super }}
{% endblock %}
{% block innercontent %}
<ul class="object-tools">
<li>
<a class="addlink" href="{% url add_project %}">Add Project</a>
</li>
</ul>
<table cellspacing="0" style="margin-top: 20px;">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Project Manager</th>
<th>Send Credentials</th>
<th>Start VPN</th>
</tr>
</thead>
{% for project in projects %}
<tr class="{% cycle 'row1' 'row2' %}">
<td>
<a href="{%url admin_project project.projectname %}">{{project.projectname}}</a>
</td>
<td>
{{project.description}}
</td>
<td>
{{project.projectManagerId}}
</td>
<td>
<a href="{% url admin_project_sendcredentials project.projectname %}">Send Credentials</a>
</td>
<td>
<a href="{% url admin_project_start_vpn project.projectname %}">Start VPN</a>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,76 @@
{% extends "admin/django_nova/project/base_projects.html" %}
{% load admin_modify adminmedia %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
<script type="text/javascript" charset="utf-8">
django.jQuery(function(){
django.jQuery.each(django.jQuery(".edit_user_roles select[multiple]"), function () {
// "Locations" can be any label you want
SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
});
})
</script>
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %} change-form{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="/admin">Home</a> &rsaquo;
<a href="{% url admin_projects %}">Projects</a> &rsaquo;
<a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
User
{{form.ProjectUserForm}}
</div>
{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
{% endblock %}
<form class="edit_user_roles" action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="module aligned {{ fieldset.classes }}">
<div class="form-row">
<label for="id_username">Username</label>
<span>{{user.username}}</span>
</div>
<input type="hidden" name="username" value="{{user.id}}" id="username" />
{% for field in form.visible_fields %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}{{ field }}
{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
</div>
{% endfor %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
</fieldset>
<div class="submit-row">
{% if project.projectManagerId != user.username %}
<p class="deletelink-box">
<a href="{% url admin_project_delete_user project.projectname user.username %}" class="deletelink">Remove User From Project</a>
</p>
{% endif %}
<input type="submit" value="Save" class="default" />
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,87 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_modify adminmedia %}
{% block title %}Send project credentials{{ block.super }}{% endblock %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script type="text/javascript" src="/media/admin/js/jquery.min.js"></script>
<script type="text/javascript" src="/media/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
<script type="text/javascript" charset="utf-8">
django.jQuery(function(){
django.jQuery.each(django.jQuery("#send_credentials select"), function () {
// "Locations" can be any label you want
SelectFilter.init(this.id, "Users", 0, "/media/admin/");
});
})
</script>
<style type="text/css" media="screen">
.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%;}
.successlist {background: #CBFBD7; color: #1E5024; border-color: #6FBA5C;}
</style>
{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="/admin">Home</a> &rsaquo;
<a href="{% url admin_projects %}">Projects</a> &rsaquo;
<a href="{% url admin_project project.projectname %}">{{project.projectname}}</a> &rsaquo;
Send Credentials
</div>
{% endblock %}
{% block content %}
<div id="content-main">
{% if not success %}
<h1>Send Credentials</h1>
<h3>Select which users you would like to send credentials to from the '{{ project.projectname }}' project.</h3>
{% else %}
<h1>Credentials sent successfully</h1>
{% endif %}
<div class="status">
{% if error %}
<span class="errorlist">{{ error }}</span>
{% endif %}
{% if success %}
<span class="successlist">{{ success }}</span>
{% endif %}
</div>
{% if not success %}
<form id="send_credentials" action="{% url admin_project_sendcredentials project.projectname %}" method="post">
{% csrf_token %}
<fieldset class="module aligned">
{% for field in form.visible_fields %}
<div class="form-row">
{{ field.errors }}
{{ field }}
{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
</div>
{% endfor %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
</fieldset>
<div class="submit-row">
<input style="margin-top:20px; margin-left:10px;" type="submit" value="Send Credentials" />
</div>
</form>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,39 @@
{% extends "admin/django_nova/project/base_projects.html" %}
{% block extrahead %}
{{ block.super }}
{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="/admin">Home</a> &rsaquo;
Global Roles
</div>
{% endblock %}
{% block innercontent %}
<h1>Select a User</h1>
<table cellspacing="0" style="margin-top: 20px;">
<thead>
<tr>
<th>Username</th>
<th>Global Roles</th>
<th>Actions</th>
</tr>
</thead>
{% for user in users %}
<tr class="{% cycle 'row1' 'row2' %}">
<td>
{{user.username}}
</td>
<td>
(temporarily hidden)
{#user.roles#}
</td>
<td>
<a href="{%url admin_user_roles user.username %}">Edit</a>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,41 @@
{% for message in messages %}
<div class="message ui-widget">
{% if message.tags == "info" %}
<div class="ui-state-highlight ui-corner-all">
<span class="close ui-icon ui-icon-circle-close"></span>
<p>
<span class="ui-icon ui-icon-info"></span>
{{ message }}
</p>
</div>
{% endif %}
{% if message.tags == "warning" %}
<div class="ui-state-highlight ui-corner-all" >
<span class="close ui-icon ui-icon-circle-close"></span>
<p>
<span class="ui-icon ui-icon-alert"></span>
{{ message }}
</p>
</div>
{% endif %}
{% if message.tags == "success" %}
<div class="ui-state-highlight ui-corner-all success" >
<span class="close ui-icon ui-icon-circle-close"></span>
<p>
<span class="ui-icon ui-icon-check"></span>
{{ message }}
</p>
</div>
{% endif %}
{% if message.tags == "error" %}
<div class="ui-state-error ui-corner-all" >
<span class="close ui-icon ui-icon-circle-close"></span>
<p>
<span class="ui-icon ui-icon-alert"></span>
{{ message }}
</p>
</div>
{% endif %}
</div>
{% endfor %}

View File

@ -0,0 +1,85 @@
{% extends "base-sidebar.html" %}
{% load region_tags %}
{% load project_tags %}
{% block headerjs %}
{{ block.super }}
{% endblock %}
{% block region %}
<div id="region_selector">
{% load_regions %}
<span id="project_name"><strong>Project:</strong> {{ project.projectname }}</span>
<form id="frm_region" method="post" action="{% url region_change %}">
{% csrf_token %}
<fieldset>
<input name="redirect_url" type="hidden" value="{{ request.get_full_path }}" />
<noscript>
<input id="btn_region_change" type="submit" value="Change" />
</noscript>
<div id="region_form">
<label for="sel_region">Region: </label>
<select id="sel_region" name="region">
{% for region in regions %}
<option{% if region.name == current_region.name %} selected{% endif %}>
{{ region.name }}
</option>
{% endfor %}
</select>
</div>
</fieldset>
</form>
</div>
{% endblock %}
{% block nav_projects %}
{% load_projects %}
<li>
<h3 class="active"><a href="/">Projects</a></h3>
<div id="projects">
{% for p in projects %}
<div id="{{ p.projectname }}" class="project{% if p.projectname == project.projectname %} active{% endif %}">
<h4>
<a class="project_link" href="/project/{{ p.projectname }}">{{ p.projectname }}</a>
{% if p.projectManagerId == user.username %}
<a id="manage_project_{{p.projectname}}"
class="manage_link"
href="{% url nova_project_manage p.projectname %}"
title="Manage User Roles">Manage Project</a>
{% endif %}
</h4>
{% if project.projectname == p.projectname %}
<ul>
<li {% if p.projectname == project.projectname and sidebar_selected == "instances" %}class="active"{% endif %}>
<a id="lnk_instances_{{p.projectname}}" href="{% url nova_instances p.projectname %}">Instances</a>
</li>
<li {% if p.projectname == project.projectname and sidebar_selected == "images" %}class="active"{% endif %}>
<a id="lnk_images_{{p.projectname}}" href="{% url nova_images p.projectname %}">Images</a>
</li>
<li {% if p.projectname == project.projectname and sidebar_selected == "keys" %}class="active"{% endif %}>
<a id="lnk_keypairs_{{p.projectname}}" href="{% url nova_keypairs p.projectname %}">Keys</a>
</li>
<li {% if p.projectname == project.projectname and sidebar_selected == "volumes" %}class="active"{% endif %}>
<a id="lnk_volumes_{{p.projectname}}" href="{% url nova_volumes p.projectname %}">Volumes</a>
</li>
</ul>
{% endif %}
</div>
{% endfor %}
</div>
</li>
{% endblock %}
{% block footerjs %}
{{ block.super }}
<script type="text/javascript">
$(function() {
$('#sel_region').change(function() {
$('#frm_region').submit();
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% load django_nova_tags %}
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Expired Token</title>
</head>
<body>
<center>
<h1>The link you clicked has expired.</h1>
<p style="width:460px;">This credentials download link you have reached
is either invalid or has expired. Each link is only good for one use. If
you need to download your credentials again, please contact the
{% site_branding %} support team.</p>
</center>
</body>
</html>

View File

@ -0,0 +1,7 @@
{% for field in form %}
<div class="{% cycle 'odd' 'even'%}">
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
</div>
{% endfor %}

View File

@ -0,0 +1,112 @@
<h3 class="image_list_heading"> {{ heading }} </h3>
{% if images %}
<table id="image_launch">
<tr>
<th>ID</th>
<th>Description</th>
<th colspan="2">Owner</th>
</tr>
{% for image in images %}
{% if image.id == ami.id %}
<td class="detail_wrapper" colspan="4">
<div id="{{ ami.id }}" class="image_detail">
<div class="column">
<div class="image_detail_item">
<span class="label">Owner: </span>
<span class="data">{{ ami.ownerId }}</span>
</div>
<div class="image_detail_item">
<span class="label">Description: </span>
<span class="data">{{ ami.description }}</span>
</div>
<div class="image_detail_item">
<span class="label">Location: </span>
<span class="data">{{ ami.location }}</span>
</div>
</div>
<div class="column">
<div class="image_detail_item">
<span class="label">ID: </span>
<span class="data">{{ ami.id }}</span>
</div>
<div class="image_detail_item">
<span class="label">Name: </span>
<span class="data">{% if ami.displayName %}{{ ami.displayName }}{%else%}{{ ami.id }}{% endif %}</span>
</div>
<div class="image_detail_item">
<span class="label">Type: </span>
<span class="data">{{ ami.type }}</span>
</div>
<div class="image_detail_item">
<span class="label">Architecture: </span>
<span class="data">{{ ami.architecture }}</span>
</div>
</div>
<div id="last" class="column">
{% if ami.is_public %}
<div id="public" class="privacy">Public Image</div>
{% else %}
<div id="private" class="privacy">Private Image</div>
{% endif %}
<a id="launch_{{ image.id }}" class="launch" href="{% url nova_images_launch project.projectname image.id %}" title="Click to launch image">Launch</a>
{% if can_modify or user.username == ami.ownerId %}
<a id="edit_image_link" href="{% url nova_images_update project.projectname ami.id %}">Edit Image</a>
{% endif %}
</div>
{% if can_modify or user.username == ami.ownerId %}
<span class="image_privacy">
<form id="privacy_{{ ami.id }}" action="{% url nova_images_privacy project.projectname ami.id %}" method="post" accept-charset="utf-8">
{% csrf_token %}
{% if ami.is_public %}
<input class="private" type="submit" value="Make Private" />
{% else %}
<input class="public" type="submit" value="Make Public" />
{% endif %}
</form>
</span>
<span class="delete">
<form id="delete_{{ ami.id }}" action="{% url nova_images_remove project.projectname ami.id %}" method="post" accept-charset="utf-8">
{% csrf_token %}
<input type="submit" value="Remove Image" />
</form>
</span>
{% endif %}
</div>
</td>
{% else %}
<tr class="{% cycle 'odd' 'even' %}">
<td class="image_id">
<a href="{% url nova_images_detail project.projectname image.id %}">{% if image.displayName %}{{ image.displayName }}{%else%}{{ image.id }}{% endif %}</a>
</td>
<td class="image_location odd">
{% if image.description %}
{{ image.description }}
{% else %}
{{ image.location }}
{% endif %}
</td>
<td class="image_owner_id">{{ image.ownerId }}</td>
<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>
{#<td class="odd"><a class="ui-state-default ui-corner-all" onclick="$('#dlg_launch').dialog('open');">Launch</a></td>#}
</tr>
{% endif %}
{% endfor %}
</table>
{% else %}
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all">
<p>
<span class="ui-icon ui-icon-info"></span>
No images currently available.
</p>
</div>
</div>
{% endif %}

View File

@ -0,0 +1,7 @@
{% extends "django_nova/base.html" %}
{% load sidebar_tags %}
{% block nav_projects %}
{% sidebar_select images %}
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,207 @@
{% extends "django_nova/images/base.html" %}
{% block title %} - Launch an Image{% endblock %}
{% block headerjs %}
<script type="text/javascript" src="/media/django_nova/js/jquery.form.js"></script>
{% endblock %}
{% block content %}
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Images</h2>
<p id="page_description">Images are snapshots of running systems which can easily be deployed to run one or more instances.</p>
</div>
{% include "django_nova/_messages.html" %}
{% if images %}
<table id="image_launch">
<tr>
<th>ID</th>
<th>Description</th>
<th colspan="2">Owner</th>
</tr>
{% for image in images %}
<tr class="{% cycle 'odd' 'even' %}">
{% if image.id == ami.id %}
<td class="detail_wrapper" colspan="4">
<div id="{{ ami.id }}" class="image_detail">
<div class="column">
<div class="image_detail_item">
<span class="label">Owner: </span>
<span class="data">{{ ami.ownerId }}</span>
</div>
<div class="image_detail_item">
<span class="label">Description: </span>
<span class="data">{{ ami.description }}</span>
</div>
<div class="image_detail_item">
<span class="label">Location: </span>
<span class="data">{{ ami.location }}</span>
</div>
</div>
<div class="column">
<div class="image_detail_item">
<span class="label">ID: </span>
<span class="data">{{ ami.id }}</span>
</div>
<div class="image_detail_item">
<span class="label">Name: </span>
<span class="data">{% if ami.displayName %}{{ ami.displayName }}{%else%}{{ ami.id }}{% endif %}</span>
</div>
<div class="image_detail_item">
<span class="label">Type: </span>
<span class="data">{{ ami.type }}</span>
</div>
<div class="image_detail_item">
<span class="label">Architecture: </span>
<span class="data">{{ ami.architecture }}</span>
</div>
</div>
<div id="last" class="column">
{% if ami.is_public %}
<div id="public" class="privacy">Public Image</div>
{% else %}
<div id="private" class="privacy">Private Image</div>
{% endif %}
<a id="launch_{{ image.id }}" class="launch" href="{% url nova_images_launch project.projectname image.id %}" title="Click to launch image">Launch</a>
{% if can_modify or user.username == ami.ownerId %}
<a id="edit_image_link" href="{% url nova_images_update project.projectname ami.id %}">Edit Image</a>
{% endif %}
</div>
{% if can_modify or user.username == ami.ownerId %}
<span class="image_privacy">
<form id="privacy_{{ ami.id }}" action="{% url nova_images_privacy project.projectname ami.id %}" method="post" accept-charset="utf-8">
{% csrf_token %}
{% if ami.is_public %}
<input class="private" type="submit" value="Make Private" />
{% else %}
<input class="public" type="submit" value="Make Public" />
{% endif %}
</form>
</span>
<span class="delete">
<form id="delete_{{ ami.id }}" action="{% url nova_images_remove project.projectname ami.id %}" method="post" accept-charset="utf-8">
{% csrf_token %}
<input type="submit" value="Remove Image" />
</form>
</span>
{% endif %}
</div>
</td>
{% else %}
<td class="image_id"><a href="{% url nova_images_detail project.projectname image.id %}">{{ image.id }}</a></td>
<td class="image_location odd">
{% if image.description %}
{{ image.description }}
{% else %}
{{ image.location }}
{% endif %}
</td>
<td class="image_owner_id">{{ image.ownerId }}</td>
<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>
{#<td class="odd"><a class="ui-state-default ui-corner-all" onclick="$('#dlg_launch').dialog('open');">Launch</a></td>#}
{% endif %}
</tr>
{% endfor %}
</table>
{% else %}
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all">
<p>
<span class="ui-icon ui-icon-info"></span>
No images currently available.
</p>
</div>
</div>
{% endif %}
</div>
<div id="dlg_launch" title="Launch Instance" style="display:none;">
<form id="frm_launch" action="url nova_images_launch project.projectname" method="post">
{% csrf_token %}
{% include "django_nova/images/_launch_form.html" %}
</form>
</div>
<div id="dlg_confirm" title="Confirm Termination">
<p>Are you sure you wish to unregister the <span id="ami_name"></span> image?</p>
</div>
{% endblock %}
{% block footerjs %}
{{ block.super }}
<script type="text/javascript">
var options = {
success: handleResponse,
beforeSubmit: showRequest,
dataType: 'json'
}
// TODO: On dialog open, reset form and validation.
$(function() {
$('#dlg_launch').dialog({
buttons: {
'Ok': function() {
$('#frm_launch').ajaxSubmit(options);
},
'Cancel': function() {
$(this).dialog('close');
}
},
autoOpen: false,
resizable: false,
width: 400,
height: 400
});
});
function showRequest(formData, jqForm, options) {
var queryString = $.param(formData);
alert('About to submit: \n\n' + queryString);
return true;
}
function handleResponse(data, statusText, xhr, $form) {
alert('status: ' + statusText + '\nsuccess:\n\n' + data.success);
}
$(function(){
$('.delete form').submit(function() {
ami_name = $(this).parent().parent().attr("id");
$('#ami_name').text(ami_name);
$('#dlg_confirm').dialog('open');
return false;
});
$('#dlg_confirm').dialog({
buttons: {
'Ok': onConfirmOK,
'Cancel': function() { $(this).dialog('close'); }
},
autoOpen: false,
resizable: false,
width: 500,
height: 200
});
})
function onConfirmOK() {
$(this).dialog('close');
form = document.getElementById('delete_' + ami_name);
if(form) form.submit();
}
</script>
{% endblock %}

View File

@ -0,0 +1,35 @@
{% extends "django_nova/images/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block headerjs %}
<script type="text/javascript" src="{{ COMMON_MEDIA_PREFIX }}js/jquery.form.js"></script>
{% endblock %}
{% block content %}
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Edit Image</h2>
<p id="page_description">From this page you can edit the name and description of an image that belongs to you.</p>
</div>
<div class="dash_block first">
<h3 class="image_id">Edit Image: {{ ami.id }}</h3>
<form class="edit_image" id="rename_{{ ami.id }}" action="{% url nova_images_update project.projectname ami.id %}" method="post">
<fieldset>
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}
<input type="submit" value="Update Image" />
</fieldset>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,70 @@
{% extends "django_nova/images/base.html" %}
{% block title %} - Launch an Image{% endblock %}
{% block headerjs %}
<script type="text/javascript" src="/media/django_nova/js/jquery.form.js"></script>
{% endblock %}
{% block content %}
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Images</h2>
<p id="page_description">Images are snapshots of running systems which can easily be deployed to run one or more instances.</p>
</div>
{% include "django_nova/_messages.html" %}
{% for heading, images in image_lists.items %}
{% include "django_nova/images/_list.html" %}
{% endfor %}
</div>
<div id="dlg_launch" title="Launch Instance" style="display:none;">
<form id="frm_launch" action="#" method="post">
{% csrf_token %}
{% include "django_nova/images/_launch_form.html" %}
</form>
</div>
{% endblock %}
{% block footerjs %}
{{ block.super }}
<script type="text/javascript">
var options = {
success: handleResponse,
beforeSubmit: showRequest,
dataType: 'json'
}
// TODO: On dialog open, reset form and validation.
$(function() {
$('#dlg_launch').dialog({
buttons: {
'Ok': function() {
$('#frm_launch').ajaxSubmit(options);
},
'Cancel': function() {
$(this).dialog('close');
}
},
autoOpen: false,
resizable: false,
width: 400,
height: 400
});
});
function showRequest(formData, jqForm, options) {
var queryString = $.param(formData);
alert('About to submit: \n\n' + queryString);
return true;
}
function handleResponse(data, statusText, xhr, $form) {
alert('status: ' + statusText + '\nsuccess:\n\n' + data.success);
}
</script>
{% endblock %}

View File

@ -0,0 +1,32 @@
{% extends "django_nova/images/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block headerjs %}
<script type="text/javascript" src="{{ COMMON_MEDIA_PREFIX }}js/jquery.form.js"></script>
{% endblock %}
{% block content %}
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Launch Image</h2>
<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>
</div>
<div class="dash_block first">
<form id="frm_launch" action="{% url nova_images_launch project.projectname ami.id %}" method="post">
{% csrf_token %}
<fieldset>
<h3 class="image_id">Launch Image {{ ami.id }}</h3>
<div class="even">
<label>Location</label>
<span class="image_location">{{ ami.location }}</span>
</div>
{% include "django_nova/images/_launch_form.html" %}
<input id="launch_image" type="submit" value="Launch" />
</fieldset>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,104 @@
{% if instances %}
<table style="width: 100%">
<tr>
<th>ID</th>
<th>Image</th>
<th>Size</th>
<th>IP</th>
<th colspan="2">State</th>
</tr>
{% for instance in instances %}
<tr class="{% cycle 'odd' 'even' %}">
{% if instance.id == selected_instance.id %}
<td class="detail_wrapper" colspan="6">
<div id="{{selected_instance.id}}" class="instance_detail">
<div class="column">
<div class="instance_detail_item">
<span class="label">Instance ID: </span>
<span class="data">{{ selected_instance.id }}</span>
</div>
<div class="instance_detail_item">
<span class="label">Name: </span>
<span class="data">{% if selected_instance.displayName != "" %}{{ selected_instance.displayName }}{% else %} None {% endif %}</span>
</div>
<div class="instance_detail_item">
<span class="label">Description: </span>
<span class="data" id="desc">{{ selected_instance.displayDescription }}</span>
</div>
</div>
<div class="column">
<div class="instance_detail_item">
<span class="label">Region: </span>
<span class="data">{{ selected_instance.region.name }}</span>
</div>
<div class="instance_detail_item">
<span class="label">Size: </span>
<span class="data">{{ selected_instance.instance_type }}</span>
</div>
<div class="instance_detail_item">
<span class="label">State: </span>
<span class="data">{{ selected_instance.state }}</span>
</div>
<div class="instance_detail_item">
<span class="label">Image ID: </span>
<span class="data">{{ selected_instance.image_id }}</span>
</div>
<div class="instance_detail_item">
<span class="label">IP Address: </span>
<span class="data">{{ selected_instance.dns_name }}</span>
</div>
</div>
<div id="last" class="column">
{% if instance.state == "running" %}
<a href="{% url nova_instances_console project.projectname instance.id %}" id="console_{{instance.id}}" class="console" target="_blank">Show Console</a>{% endif %}
<a id="edit_instance_link" href="{% url nova_instance_update project.projectname instance.id %}">Edit Instance</a>
</div>
<span class="delete">
<form id="form_terminate_{{ instance.id }}" class="form-terminate" method="post" action="{% url nova_instances_terminate project.projectname %}" >
<input name="instance_id" type="hidden" value="{{ instance.id }}" />
<input id="terminate_{{instance.id}}" class="terminate" type="submit" value="Terminate" />
{% csrf_token %}
</form>
</span>
</div>
</td>
{% else %}
<td><a href="{% url nova_instances_detail project.projectname instance.id %}">{{ instance.id }} {% if instance.displayName %}({{ instance.displayName }}){% endif %}
</a></td>
<td class="odd">{{ instance.image_id }}</td>
<td>{{ instance.instance_type }}</td>
<td class="odd">{{ instance.dns_name }}</td>
<td>{{ instance.state }}</td>
<td id="actions" class="odd">
<form id="form_terminate_{{ instance.id }}" class="form-terminate" method="post" action="{% url nova_instances_terminate project.projectname %}" >
<input name="instance_id" type="hidden" value="{{ instance.id }}" />
<input id="terminate_{{instance.id}}" class="terminate" type="submit" value="Terminate" />
{% csrf_token %}
</form>
{% if instance.state == "running" %}
<a href="{% url nova_instances_console project.projectname instance.id %}" id="console_{{instance.id}}" class="console" target="_blank">Show Console</a>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% else %}
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all">
<p>
<span class="ui-icon ui-icon-info"></span>
No instances are currently running. You may start a new instance from the <a href="{% url nova_images project.projectname %}">images</a> tab.
</p>
</div>
</div>
{% endif %}

View File

@ -0,0 +1,7 @@
{% extends "django_nova/base.html" %}
{% load sidebar_tags %}
{% block nav_projects %}
{% sidebar_select instances %}
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,33 @@
{% extends "django_nova/instances/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block content %}
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Instance ID: {{ instance.id }}</h2>
<p id="page_description">Here you can see up to the minute performance data about your instance.</p>
</div>
{% include "django_nova/_messages.html" %}
<div class="dash_block first">
<h3 class="image_id">Edit Instance: {{ instance.id }}</h3>
<form class="edit_instance" id="rename_{{ instance.id }}" action="{% url nova_instance_update project.projectname instance.id %}" method="post">
<fieldset>
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}
<input type="submit" value="Update Instance" />
</fieldset>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends "django_nova/instances/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block headerjs %}
<script type="text/javascript" src="{{ COMMON_MEDIA_PREFIX }}js/jquery.form.js"></script>
{% endblock %}
{% block content %}
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Edit Instance</h2>
<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>
</div>
<div class="dash_block first">
<h3 class="image_id">Edit Instance: {{ instance.id }}</h3>
<form class="edit_instance" id="rename_{{ selected_instance.id }}" action="{% url nova_instance_update project.projectname instance.id %}" method="post">
<fieldset>
{% csrf_token %}
{% for field in update_form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}
<input type="submit" value="Update Instance" />
</fieldset>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,98 @@
{% extends "django_nova/instances/base.html" %}
{% block title %} - {{ project.projectname|capfirst }} Instances{% endblock %}
{% block pageclass %}instances{% endblock %}
{% block content %}
<div id="page_head">
<div id="spinner"></div>
<h2 id="page_heading">Instances</h2>
<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>
</div>
{% include "django_nova/_messages.html" %}
<div id="instances">
{% include "django_nova/instances/_instances_list.html" %}
</div>
<div id="dlg_confirm" title="Confirm Termination" style="display:none;">
<p>Are you sure you wish to terminate instance <span id="spn_terminate"></span>?</p>
</div>
<div id="connection_error" style="display:none;" title="Connection Error">
<p><span class="ui-icon ui-icon-alert"></span>A connection error has occurred. Please ensure you are still connected to VPN.</p>
</div>
{% endblock %}
{% block footerjs %}
{{ block.super }}
<script type="text/javascript">
$(function() {
setInterval(function() {
$('#spinner').show();
{% if detail %}
$('#instances').load('{% url nova_instances_refresh_detail project.projectname selected_instance.id %}', onInstancesUpdated);
{% else %}
$('#instances').load('{% url nova_instances_refresh project.projectname %}', onInstancesUpdated);
{% endif %}
}, 15000);
initInstanceForms();
$('#dlg_confirm').dialog({
buttons: {
'Ok': onConfirmOK,
'Cancel': function() { $(this).dialog('close'); }
},
autoOpen: false,
resizable: false,
width: 500,
height: 200
});
});
var _terminateID = null;
function initInstanceForms() {
$('.form-terminate').submit(function() {
_terminateID = $(this).children(':first').val()
$('#spn_terminate').text(_terminateID);
$('#dlg_confirm').dialog('open');
return false;
});
}
function onInstancesUpdated(response, status, xhr) {
$('#spinner').hide();
switch(xhr.status) {
case 200:
initInstanceForms();
break;
case 403:
document.location = '{% url auth_login %}';
break;
default:
$('#connection_error').dialog({
dialogClass: 'alert',
modal: true,
closeOnEscape: true,
buttons:{ "Close": function() { $(this).dialog("close"); } },
});
$('#connection_error').dialog('open');
break;
}
}
function onConfirmOK() {
$(this).dialog('close');
form = document.getElementById('form_terminate_' + _terminateID);
if(form) form.submit();
}
</script>
{% endblock %}

View File

@ -0,0 +1,58 @@
{% extends "django_nova/instances/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block content %}
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Instance ID: {{ instance.id }} Performance</h2>
<p id="page_description">Here you can see up to the minute performance data about your instance.</p>
</div>
{% include "django_nova/_messages.html" %}
<p>
<h1>CPU Usage</h1>
<h3>Today</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "cpu-1d.png" %}" />
</p>
<p>
<h3>This Week</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "cpu-1w.png" %}" />
</p>
<p>
<h3>This Month</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "cpu-1m.png" %}" />
</p>
<p>
<h1>Network Activity</h1>
<h3>Today</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "net-1d.png" %}" />
</p>
<p>
<h3>This Week</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "net-1w.png" %}" />
</p>
<p>
<h3>This Month</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "net-1m.png" %}" />
</p>
<p>
<h1>Disk Activity</h3>
<h3>Today</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "disk-1d.png" %}" />
</p>
<p>
<h3>This Week</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "disk-1w.png" %}" />
</p>
<p>
<h3>This Month</h3>
<img src="{% url nova_instances_graph project.projectname instance.id "disk-1m.png" %}" />
</p>
</div>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% for field in create_form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}

View File

@ -0,0 +1,31 @@
{% if keypairs %}
<table style="width: 100%">
<tr>
<th>Key Pair Name</th>
<th>Fingerprint</th>
<th>&nbsp;</th>
</tr>
{% for keypair in keypairs %}
<tr class="{% cycle 'odd' 'even' %}">
<td>{{ keypair.name }}</td>
<td class="odd">{{ keypair.fingerprint }}</td>
<td>
<form id="form_key_delete_{{keypair.name}}" class="form-key-delete" method="post" action="{% url nova_keypairs_delete project.projectname %}">
<input name="key_name" type="hidden" value="{{ keypair.name }}" />
<input id="keypair_delete_{{keypair.name}}" class="delete" type="submit" value="Delete" />
{% csrf_token %}
</form>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all">
<p>
<span class="ui-icon ui-icon-info"></span>
No key pairs currently exist.
</p>
</div>
</div>
{% endif %}

View File

@ -0,0 +1,7 @@
{% extends "django_nova/base.html" %}
{% load sidebar_tags %}
{% block nav_projects %}
{% sidebar_select keys %}
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,77 @@
{% extends "django_nova/keypairs/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block headerjs %}
{{ block.super }}
<script type="text/javascript" src="/media/dashboard/js/jquery.form.js"></script>
{% endblock %}
{% block content %}
<div id="page_head">
<h2 id="page_heading">Keys</h2>
<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>
</div>
{% include "django_nova/_messages.html" %}
<div id="instances">
{% include "django_nova/keypairs/_list.html" %}
</div>
<div class="dash_block first">
<form id="frm_key_create" action="{% url nova_keypairs_add project.projectname %}" method="post">
{% csrf_token %}
<input id="js" name="js" type="hidden" value="0" />
<fieldset>
<h3>Create New Keypair</h3>
{% include "django_nova/keypairs/_create_form.html" %}
<input id="keypair_create" class="create" type="submit" value="Create" />
</fieldset>
</form>
</div>
<div id="dlg_confirm" title="Confirm Termination">
<p>Are you sure you wish to delete key <span id="spn_delete_key_name"></span>?</p>
</div>
{% endblock %}
{% block footerjs %}
{{ block.super }}
<script type="text/javascript">
$(function() { $('#js').val('1'); });
{% if download_key %}
$(function() { window.location = '{% url nova_keypairs_download project.projectname download_key %}'; });
{% endif %}
$(function() {
$('.form-key-delete').submit(function() {
_key_name = $(this).children(':first').val()
$('#spn_delete_key_name').text(_key_name);
$('#dlg_confirm').dialog('open');
return false;
});
$('#dlg_confirm').dialog({
buttons: {
'Ok': onConfirmOK,
'Cancel': function() { $(this).dialog('close'); }
},
autoOpen: false,
resizable: false,
width: 500,
height: 200
});
});
var _terminateID = null;
function onConfirmOK() {
$(this).dialog('close');
form = document.getElementById('form_key_delete_' + _key_name);
if(form) form.submit();
}
</script>
{% endblock %}

View File

@ -0,0 +1,72 @@
{% extends "django_nova/base.html" %}
{% block title %} - {{ project.projectname|capfirst }} Overview{% endblock %}
{% block pageclass %}overview{% endblock %}
{% block headerjs %}
{{ block.super }}
<script type="text/javascript" src="/media/dashboard/js/django-admin.multiselect.js"></script>
<link rel="stylesheet" type="text/css" href="/media/dashboard/css/django-admin-widgets.css" />
<script type="text/javascript" charset="utf-8">
$(function(){
$.each($("#user_edit form select"), function () {
// "Locations" can be any label you want
SelectFilter.init(this.id, "Roles", 0, "/media/admin/");
});
})
</script>
{% endblock %}
{% block content %}
<div id="page_head">
<h2 id="page_heading">Edit User Roles</h2>
<p id="page_description">From here you can edit multiple user roles.</p>
</div>
{% include "django_nova/_messages.html" %}
<div id="user_edit" class="dash_block first">
{% if user %}
<form action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="module aligned {{ fieldset.classes }}">
<h3 id="edit_{{ user.username }}">Edit Roles for User: {{ user.username }}</h3>
<div class="form-row">
<label>User</label>
<span id="user_name">{{ user.username }}</span>
</div>
<input type="hidden" name="username" value="{{ user.id }}" id="username" />
{% for field in form.visible_fields %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}{{ field }}
{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% endif %}
</div>
{% endfor %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
</fieldset>
<div class="cancel">
<a href="{% url nova_project_manage project.projectname %}">Cancel</a>
</div>
<div class="submit-row">
<input type="submit" value="Save" class="default" />
{# <a href="#" class="deletelink">Remove User</a> #}
</div>
</form>
{% else %}
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all">
<p>
<span class="ui-icon ui-icon-info"></span>
No users are currently associated with this project.
</p>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "django_nova/base.html" %}
{% block title %} - {{ project.projectname|capfirst }} Overview{% endblock %}
{% block pageclass %}overview{% endblock %}
{% block content %}
<div id="page_head">
<h2><span>{{ project.projectname|capfirst }}</span> Overview</h2>
</div>
{% include "django_nova/_messages.html" %}
<div id="welcome">
<p>Welcome to the <span>{{ project.projectname|capfirst }}</span> Overview. From here you can manage your instances, images, keys, and security groups.</p>
<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>
</div>
<div id="resources" class="dash_block">
<h3>Project Resources</h3>
<ul>
<li><a href="{% url nova_download_credentials project.projectname %}">Generate X509 credentials.</a></li>
<li><a href="{% url nova_instances project.projectname %}">View Instances&nbsp;(<strong>{{ instance_count }}</strong> running).</a></li>
<li><a href="{% url nova_images project.projectname %}">View Images.</a></li>
</ul>
</div>
{% endblock %}

View File

@ -0,0 +1,45 @@
{% extends "django_nova/base.html" %}
{% block title %} - {{ project.projectname|capfirst }} Overview{% endblock %}
{% block pageclass %}overview{% endblock %}
{% block content %}
<div id="page_head">
<h2 id="page_heading">Manage Users and Roles</h2>
<p id="page_description">From here you can manage users and roles.</p>
</div>
{% include "django_nova/_messages.html" %}
<div id="users">
{% if members %}
<table style="width: 100%">
<tr>
<th>Username</th>
<th>Project Roles</th>
<th>Global Roles</th>
<th>&nbsp;</th>
</tr>
{% for member in members %}
<tr class="{% cycle 'odd' 'even' %}">
<td>{{ member.memberId }} {% if project.projectManagerId == member.memberId %}(<em>project manager</em>){% endif %}</td>
<td>{{ member.project_roles }}</td>
<td>{{ member.global_roles }}</td>
<td class="odd">
<a href="{% url nova_project_edit_user project.projectname member.memberId%}">Edit</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all">
<p>
<span class="ui-icon ui-icon-info"></span>
No users are currently associated with this project.
</p>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% for field in authorize_form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}

View File

@ -0,0 +1,5 @@
{% for field in create_form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}

View File

@ -0,0 +1,3 @@
<input type="hidden" name="protocol" value="{{ rule.ip_protocol }}" />
<input type="hidden" name="from_port" value="{{ rule.from_port}}" />
<input type="hidden" name="to_port" value="{{ rule.to_port }}" />

View File

@ -0,0 +1,7 @@
{% extends "django_nova/base.html" %}
{% load sidebar_tags %}
{% block nav_projects %}
{% sidebar_select securitygroups %}
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,62 @@
{% extends "django_nova/securitygroups/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block content %}
<div id="dashboard_tabs">
<div id="tabs-1" class="ui-tabs-panel ui-widget-content ui-corner-bottom dash-wrap" style="margin-left:0;min-height:300px;">
<ul id="dashboard_nav">
<li><a id="lnk_overview" href="{% url dashboard_project project.projectname %}">Overview</a></li>
<li><a id="lnk_instances" href="{% url dashboard_instances project.projectname %}">Instances</a></li>
<li><a id="lnk_images" href="{% url dashboard_images project.projectname %}">Images</a></li>
<li><a id="lnk_keypairs" href="{% url dashboard_keypairs project.projectname %}">Keys</a></li>
<li class="active"><a id="lnk_securitygroups" href="{% url dashboard_securitygroups project.projectname %}">Security Groups</a></li>
<li><a id="lnk_volumes" href="{% url dashboard_volumes project.projectname %}">Volumes</a></li>
</ul>
<div id="right_content">
<div id="page_head">
<h2>Security Group: {{ securitygroup.name }}</h2>
<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>
</div>
{% include "django_nova/_messages.html" %}
<table>
<tr>
<th>Protocol</th>
<th>From Port</th>
<th>To Port</th>
<th></th>
</tr>
{% for rule in securitygroup.rules %}
<tr class="{% cycle 'odd' 'even' %}">
<td>{{ rule.ip_protocol }}</td>
<td class="odd">{{ rule.from_port }}</td>
<td>{{ rule.to_port }}</td>
<td class="odd">
<form id="security_groups" method="post" action="{% url dashboard_securitygroups_revoke project.projectname securitygroup.name %}">
{% csrf_token %}
{% include "django_nova/securitygroups/_revoke_form.html" %}
<input class="ui-state-default ui-corner-all" type="submit" value="Revoke" />
</form>
</td>
</tr>
{% endfor %}
</table>
<div class="block">
<h3>Authorize</h3>
<form id="authorize" method="post" action="{% url dashboard_securitygroups_authorize project.projectname securitygroup.name %}">
{% csrf_token %}
<fieldset>
<input type="hidden" name="group" value="{{ securitygroup.name }}" />
{% include "django_nova/securitygroups/_authorize_form.html" %}
<input class="ui-state-default ui-corner-all" type="submit" value="Authorize">
</fieldset>
</form>
</div>
</div>
<div class="clr"></div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,59 @@
{% extends "django_nova/securitygroups/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block content %}
<div id="dashboard_tabs">
<div id="tabs-1" class="ui-tabs-panel ui-widget-content ui-corner-bottom dash-wrap" style="margin-left:0px;min-height:300px;">
<ul id="dashboard_nav">
<li><a id="lnk_overview" href="{% url dashboard_project project.projectname %}">Overview</a></li>
<li><a id="lnk_instances" href="{% url dashboard_instances project.projectname %}">Instances</a></li>
<li><a id="lnk_images" href="{% url dashboard_images project.projectname %}">Images</a></li>
<li><a id="lnk_keypairs" href="{% url dashboard_keypairs project.projectname %}">Keys</a></li>
<li class="active"><a id="lnk_securitygroups" href="{% url dashboard_securitygroups project.projectname %}">Security Groups</a></li>
<li><a id="lnk_volumes" href="{% url dashboard_volumes project.projectname %}">Volumes</a></li>
</ul>
<div id="right_content">
<div id="page_head">
<h2 id="page_heading">Security Groups</h2>
<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>
</div>
{% include "django_nova/_messages.html" %}
<table style="width:100%;">
<tr>
<th>Name</th>
<th style="min-width:60%;">Description</th>
<th>Rules</th>
<th>&nbsp;</th>
</tr>
{% for securitygroup in securitygroups %}
<tr class="{% cycle 'odd' 'even' %}">
<td id="group_{{ securitygroup.id }}"><a href="{% url dashboard_securitygroups_detail project.projectname securitygroup.name %}">{{ securitygroup.name }}</a></td>
<td id="group_{{ securitygroup.id }}_description" class="odd">{{ securitygroup.description }}</td>
<td id="group_{{ securitygroup.id }}_rules">{{ securitygroup.rules|length }}</td>
<td class="odd">
<form id="delete_group_{{ securitygroup.id }}" method="post" action="{% url dashboard_securitygroups_delete project.projectname securitygroup.name %}">
{% csrf_token %}
<input class="ui-state-default ui-corner-all" type="submit" value="Delete">
</form>
</td>
</tr>
{% endfor %}
</table>
<div class="block">
<form id="add_group_form" method="post" action="{% url dashboard_securitygroups_add project.projectname %}">
{% csrf_token %}
<fieldset>
<h3>New Group</h3>
{% include "django_nova/securitygroups/_create_form.html" %}
<label>&nbsp;</label><input class="ui-state-default ui-corner-all" type="submit" value="Create" />
</fieldset>
</form>
</div>
</div>
<div class="clr"></div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% for field in attach_form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}

View File

@ -0,0 +1,5 @@
{% for field in create_form %}
{{ field.label_tag }}
{% if field.errors %}{{ field.errors }}{% endif %}
{{ field }}
{% endfor %}

View File

@ -0,0 +1,7 @@
{% extends "django_nova/base.html" %}
{% load sidebar_tags %}
{% block nav_projects %}
{% sidebar_select volumes %}
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,84 @@
{% extends "django_nova/volumes/base.html" %}
{% block title %} - Cloud Computing{% endblock %}
{% block content %}
<div id="page_head">
<h2 id="page_heading">Volumes</h2>
<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>
</div>
{% include "django_nova/_messages.html" %}
{% if volumes %}
<table style="width: 100%">
<tr>
<th>ID</th>
<th>Size</th>
<th colspan="2">Status</th>
</tr>
{% for volume in volumes %}
<tr class="{% cycle 'odd' 'even' %}">
<td id="volume_{{ volume.id }}">{{ volume.id }} {{ volume.displayName }}</td>
<td id="volume_{{ volume.id }}_size" class="odd">{{ volume.size }}GB</td>
<td id="volume_{{ volume.id }})_status">
{% if volume.status == "in-use" %}
{% if volume.attachment_state == "attached" %}
attached: {{ volume.attach_data.instance_id }}
{% else %}
{{ volume.attachment_state }}
{% endif %}
{% else %}
{{ volume.status }}
{% endif %}
</td>
{% if volume.attachment_state == "attached" %}
<td class="odd">
<form class="volume" action="{% url nova_volumes_detach project.projectname volume.id %}" method="post">
{% csrf_token %}
<input id="detach_{{ volume.id }}" class="detach" type="submit" value="Detach">
</form>
</td>
{% else %}
<td class="odd">
<form class="volume" action="{% url nova_volumes_delete project.projectname volume.id %}" method="post">
{% csrf_token %}
<input id="delete_{{ volume.id }}" class="delete" type="submit" value="Delete">
</form>
</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% else %}
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all">
<p>
<span class="ui-icon ui-icon-info"></span>
No volumes currently exist.
</p>
</div>
</div>
{% endif %}
<div class="dash_block first">
<form id="new_volume_form" method="post" action="{% url nova_volumes_add project.projectname %}">
{% csrf_token %}
<fieldset>
<h3>Create New Volume</h3>
{% include "django_nova/volumes/_create_form.html" %}
<input id="create_volume" class="create" type="submit" value="Create" />
</fieldset>
</form>
</div>
<div class="dash_block">
<form id="new_volume_form" method="post" action="{% url nova_volumes_attach project.projectname %}">
{% csrf_token %}
<fieldset>
<h3>Attach Volume</h3>
{% include "django_nova/volumes/_attach_form.html" %}
<input id="attach_volume" class="attach" type="submit" value="Attach" />
</fieldset>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,50 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Template tags for extending the Django admin interface.
"""
from django.contrib.admin.templatetags.admin_list import items_for_result, result_headers
from django.core.urlresolvers import reverse
from django.template import Library
from django.utils.safestring import mark_safe
register = Library()
def project_result_list(cl):
headers = list(result_headers(cl))
headers.append({'text': mark_safe('&nbsp;')})
results = list()
for project in cl.result_list:
rl = list(items_for_result(cl,project,None))
url = reverse('admin_project_sendcredentials', args=[project.projectname])
content = mark_safe('<td><a href="%s">Send Credentials</a></td>' % url)
rl.append(content)
results.append(rl)
return {
'cl': cl,
'result_headers': headers,
'results': results
}
project_result_list = register.inclusion_tag("admin/change_list_results.html")(project_result_list)

View File

@ -0,0 +1,37 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Template tags for working with django_nova.
"""
from django import template
from django.conf import settings
register = template.Library()
class SiteBrandingNode(template.Node):
def render(self, context):
return settings.SITE_BRANDING
@register.tag
def site_branding(parser, token):
return SiteBrandingNode()

View File

@ -0,0 +1,39 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Template tags for gathering contextual region data.
"""
from django import template
from django_nova.shortcuts import get_projects
register = template.Library()
class ProjectsNode(template.Node):
def render(self, context):
# Store project list in template context.
context['projects'] = get_projects(context['request'].user)
return ''
@register.tag
def load_projects(parser, token):
return ProjectsNode()

View File

@ -0,0 +1,40 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Template tags for gathering contextual region data.
"""
from django import template
from django_nova.shortcuts import get_current_region, get_all_regions
register = template.Library()
class RegionsNode(template.Node):
def render(self, context):
# Store region info in template context.
context['current_region'] = get_current_region(context['request'])
context['regions'] = get_all_regions()
return ''
@register.tag
def load_regions(parser, token):
return RegionsNode()

View File

@ -0,0 +1,46 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Template tags for rendering the sidebar.
"""
from django import template
register = template.Library()
class SidebarSelectNode(template.Node):
def __init__(self, selected):
self.selected = selected
def render(self, context):
# Store page type in template context.
context['sidebar_selected'] = self.selected
return ''
@register.tag
def sidebar_select(parser, token):
try:
tag_name, selected = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
return SidebarSelectNode(str(selected))

View File

@ -0,0 +1,31 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Template tags for truncating strings.
"""
from django import template
register = template.Library()
@register.filter("truncate")
def truncate(value, size):
if len(value) > size and size > 3:
return value[0:(size-3)] + '...'
else:
return value[0:size]

View File

@ -0,0 +1 @@
from view_tests import *

View File

@ -0,0 +1,36 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
URL patterns for testing django-nova views.
"""
from django.conf.urls.defaults import *
from django.conf.urls.defaults import *
urlpatterns = patterns('',
url(r'^projects/', include('django_nova.urls.project')),
url(r'^region/', include('django_nova.urls.region')),
url(r'^admin/projects/', include('django_nova.urls.admin_project')),
url(r'^admin/roles/', include('django_nova.urls.admin_roles')),
url(r'^credentials/download/(?P<auth_token>\w+)/$',
'django_nova.views.credentials.authorize_credentials',
name='nova_credentials_authorize'),
)

View File

@ -0,0 +1,7 @@
from credential_tests import *
from image_tests import *
from instance_tests import *
from keypair_tests import *
from region_tests import *
from volume_tests import *

View File

@ -0,0 +1,90 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Base classes for view based unit tests.
"""
import mox
from django import test
from django.conf import settings
from django.contrib.auth import models as auth_models
from django_nova import adminclient
from django_nova import manager
from django_nova import shortcuts
TEST_PROJECT = 'test'
TEST_USER = 'test'
TEST_REGION = 'test'
class BaseViewTests(test.TestCase):
def setUp(self):
self.mox = mox.Mox()
def tearDown(self):
self.mox.UnsetStubs()
def assertRedirectsNoFollow(self, response, expected_url):
self.assertEqual(response._headers['location'],
('Location', settings.TESTSERVER + expected_url))
self.assertEqual(response.status_code, 302)
def authenticateTestUser(self):
user = auth_models.User.objects.create_user(TEST_USER,
'test@test.com',
password='test')
login = self.client.login(username=TEST_USER, password='test')
self.failUnless(login, 'Unable to login')
return user
class BaseProjectViewTests(BaseViewTests):
def setUp(self):
super(BaseProjectViewTests, self).setUp()
project = adminclient.ProjectInfo()
project.projectname = TEST_PROJECT
project.projectManagerId = TEST_USER
self.user = self.authenticateTestUser()
self.region = adminclient.RegionInfo(name=TEST_REGION,
endpoint='http://test:8773/')
self.project = manager.ProjectManager(self.user.username,
project,
self.region)
self.mox.StubOutWithMock(shortcuts, 'get_project_or_404')
shortcuts.get_project_or_404(mox.IgnoreArg(),
'test').AndReturn(self.project)
def create_key_pair_choices(self, key_names):
return [(k, k) for k in key_names]
def create_instance_type_choices(self):
return [('m1.medium', 'm1.medium'),
('m1.large', 'm1.large')]
def create_instance_choices(self, instance_ids):
return [(id, id) for id in instance_ids]
def create_available_volume_choices(self, volumes):
return [(v.id, '%s %s - %dGB' % (v.id, v.displayName, v.size)) \
for v in volumes]

View File

@ -0,0 +1,70 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit tests for credential views.
"""
import mox
from django.conf import settings
from django.core.urlresolvers import reverse
from django_nova import models
from django_nova.tests.view_tests.base import BaseViewTests
class CredentialViewTests(BaseViewTests):
def test_download_expired_credentials(self):
auth_token = 'expired'
self.mox.StubOutWithMock(models.CredentialsAuthorization,
'get_by_token')
models.CredentialsAuthorization.get_by_token(auth_token) \
.AndReturn(None)
self.mox.ReplayAll()
res = self.client.get(reverse('nova_credentials_authorize',
args=[auth_token]))
self.assertTemplateUsed(res, 'django_nova/credentials/expired.html')
self.mox.VerifyAll()
def test_download_good_credentials(self):
auth_token = 'good'
creds = models.CredentialsAuthorization()
creds.username = 'test'
creds.project = 'test'
creds.auth_token = auth_token
self.mox.StubOutWithMock(models.CredentialsAuthorization,
'get_by_token')
self.mox.StubOutWithMock(creds, 'get_zip')
models.CredentialsAuthorization.get_by_token(auth_token) \
.AndReturn(creds)
creds.get_zip().AndReturn('zip')
self.mox.ReplayAll()
res = self.client.get(reverse('nova_credentials_authorize',
args=[auth_token]))
self.assertEqual(res.status_code, 200)
self.assertEqual(res['Content-Disposition'],
'attachment; filename=%s-test-test-x509.zip' %
settings.SITE_NAME)
self.assertContains(res, 'zip')
self.mox.VerifyAll()

View File

@ -0,0 +1,232 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit tests for image views.
"""
import boto.ec2.image
import boto.ec2.instance
import mox
from django.core.urlresolvers import reverse
from django_nova import forms
from django_nova import shortcuts
from django_nova.tests.view_tests.base import BaseProjectViewTests, TEST_PROJECT
TEST_IMAGE_ID = 'ami_test'
TEST_INSTANCE_ID = 'i-abcdefg'
TEST_KEY = 'foo'
class ImageViewTests(BaseProjectViewTests):
def setUp(self):
self.ami = boto.ec2.image.Image()
self.ami.id = TEST_IMAGE_ID
setattr(self.ami, 'displayName', TEST_IMAGE_ID)
setattr(self.ami, 'description', TEST_IMAGE_ID)
super(ImageViewTests, self).setUp()
def test_index(self):
self.mox.StubOutWithMock(self.project, 'get_images')
self.mox.StubOutWithMock(forms, 'get_key_pair_choices')
self.mox.StubOutWithMock(forms, 'get_instance_type_choices')
self.project.get_images().AndReturn([])
forms.get_key_pair_choices(self.project).AndReturn([])
forms.get_instance_type_choices().AndReturn([])
self.mox.ReplayAll()
res = self.client.get(reverse('nova_images', args=[TEST_PROJECT]))
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'django_nova/images/index.html')
self.assertEqual(len(res.context['image_lists']), 3)
self.mox.VerifyAll()
def test_launch_form(self):
self.mox.StubOutWithMock(self.project, 'get_image')
self.mox.StubOutWithMock(forms, 'get_key_pair_choices')
self.mox.StubOutWithMock(forms, 'get_instance_type_choices')
self.project.get_image(TEST_IMAGE_ID).AndReturn(self.ami)
forms.get_key_pair_choices(self.project).AndReturn([])
forms.get_instance_type_choices().AndReturn([])
self.mox.ReplayAll()
args = [TEST_PROJECT, TEST_IMAGE_ID]
res = self.client.get(reverse('nova_images_launch', args=args))
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'django_nova/images/launch.html')
self.assertEqual(res.context['ami'].id, TEST_IMAGE_ID)
self.mox.VerifyAll()
def test_launch(self):
instance = boto.ec2.instance.Instance()
instance.id = TEST_INSTANCE_ID
instance.image_id = TEST_IMAGE_ID
reservation = boto.ec2.instance.Reservation()
reservation.instances = [instance]
self.mox.StubOutWithMock(forms, 'get_key_pair_choices')
self.mox.StubOutWithMock(forms, 'get_instance_type_choices')
self.mox.StubOutWithMock(self.project, 'run_instances')
forms.get_key_pair_choices(self.project).AndReturn(
self.create_key_pair_choices([TEST_KEY]))
forms.get_instance_type_choices().AndReturn(
self.create_instance_type_choices())
self.project.run_instances(TEST_IMAGE_ID,
addressing_type=mox.IgnoreArg(),
key_name=TEST_KEY,
user_data='',
instance_type='m1.medium',
min_count='1',
max_count='1').AndReturn(reservation)
self.mox.ReplayAll()
url = reverse('nova_images_launch', args=[TEST_PROJECT, TEST_IMAGE_ID])
data = {'key_name': TEST_KEY,
'count': '1',
'size': 'm1.medium',
'user_data': ''}
res = self.client.post(url, data)
self.assertRedirectsNoFollow(res, reverse('nova_instances',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_detail(self):
self.mox.StubOutWithMock(self.project, 'get_images')
self.mox.StubOutWithMock(self.project, 'get_image')
self.mox.StubOutWithMock(shortcuts, 'get_user_image_permissions')
self.mox.StubOutWithMock(forms, 'get_key_pair_choices')
self.mox.StubOutWithMock(forms, 'get_instance_type_choices')
self.project.get_images().AndReturn([self.ami])
self.project.get_image(TEST_IMAGE_ID).AndReturn(self.ami)
shortcuts.get_user_image_permissions(mox.IgnoreArg(),
TEST_PROJECT).AndReturn(True)
forms.get_key_pair_choices(self.project).AndReturn(
self.create_key_pair_choices([TEST_KEY]))
forms.get_instance_type_choices().AndReturn(
self.create_instance_type_choices())
self.mox.ReplayAll()
res = self.client.get(reverse('nova_images_detail',
args=[TEST_PROJECT, TEST_IMAGE_ID]))
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'django_nova/images/index.html')
self.assertEqual(res.context['ami'].id, TEST_IMAGE_ID)
self.mox.VerifyAll()
def test_remove_form(self):
self.mox.StubOutWithMock(self.project, 'get_image')
self.project.get_image(TEST_IMAGE_ID).AndReturn(self.ami)
self.mox.ReplayAll()
res = self.client.get(reverse('nova_images_remove',
args=[TEST_PROJECT, TEST_IMAGE_ID]))
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'django_nova/images/detail_list.html')
self.assertEqual(res.context['ami'].id, TEST_IMAGE_ID)
self.mox.VerifyAll()
def test_remove(self):
self.mox.StubOutWithMock(self.project, 'deregister_image')
self.project.deregister_image(TEST_IMAGE_ID).AndReturn(True)
self.mox.ReplayAll()
res = self.client.post(reverse('nova_images_remove',
args=[TEST_PROJECT, TEST_IMAGE_ID]))
self.assertRedirectsNoFollow(res, reverse('nova_images',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_make_public(self):
self.mox.StubOutWithMock(self.project, 'get_image')
self.mox.StubOutWithMock(self.project, 'modify_image_attribute')
self.ami.is_public = False
self.project.get_image(TEST_IMAGE_ID).AndReturn(self.ami)
self.project.modify_image_attribute(TEST_IMAGE_ID,
attribute='launchPermission',
operation='add').AndReturn(True)
self.mox.ReplayAll()
res = self.client.post(reverse('nova_images_privacy',
args=[TEST_PROJECT, TEST_IMAGE_ID]))
self.assertRedirectsNoFollow(res, reverse('nova_images_detail',
args=[TEST_PROJECT, TEST_IMAGE_ID]))
self.mox.VerifyAll()
def test_make_private(self):
self.mox.StubOutWithMock(self.project, 'get_image')
self.mox.StubOutWithMock(self.project, 'modify_image_attribute')
self.ami.is_public = True
self.project.get_image(TEST_IMAGE_ID).AndReturn(self.ami)
self.project.modify_image_attribute(TEST_IMAGE_ID,
attribute='launchPermission',
operation='remove').AndReturn(True)
self.mox.ReplayAll()
args = [TEST_PROJECT, TEST_IMAGE_ID]
res = self.client.post(reverse('nova_images_privacy', args=args))
self.assertRedirectsNoFollow(res, reverse('nova_images_detail',
args=args))
self.mox.VerifyAll()
def test_update_form(self):
self.mox.StubOutWithMock(self.project, 'get_image')
self.project.get_image(TEST_IMAGE_ID).AndReturn(self.ami)
self.mox.ReplayAll()
args = [TEST_PROJECT, TEST_IMAGE_ID]
res = self.client.get(reverse('nova_images_update', args=args))
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'django_nova/images/edit.html')
self.assertEqual(res.context['ami'].id, TEST_IMAGE_ID)
self.mox.VerifyAll()
def test_update(self):
self.mox.StubOutWithMock(self.project, 'get_image')
self.mox.StubOutWithMock(self.project, 'update_image')
self.project.get_image(TEST_IMAGE_ID).AndReturn(self.ami)
self.project.update_image(TEST_IMAGE_ID, 'test', 'test').AndReturn(True)
self.mox.ReplayAll()
args = [TEST_PROJECT, TEST_IMAGE_ID]
data = {'nickname': 'test',
'description': 'test'}
url = reverse('nova_images_update', args=args)
res = self.client.post(url, data)
expected_url = reverse('nova_images_detail', args=args)
self.assertRedirectsNoFollow(res, expected_url)
self.mox.VerifyAll()

View File

@ -0,0 +1,67 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit tests for instance views.
"""
import boto.ec2.instance
import mox
from django.core.urlresolvers import reverse
from django_nova.tests.view_tests.base import BaseProjectViewTests, TEST_PROJECT
TEST_INSTANCE_ID = 'i-abcdefgh'
class InstanceViewTests(BaseProjectViewTests):
def test_index(self):
self.mox.StubOutWithMock(self.project, 'get_instances')
self.project.get_instances().AndReturn([])
self.mox.ReplayAll()
res = self.client.get(reverse('nova_instances', args=[TEST_PROJECT]))
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'django_nova/instances/index.html')
self.assertEqual(len(res.context['instances']), 0)
self.mox.VerifyAll()
def test_detail(self):
instance = boto.ec2.instance.Instance()
instance.id = TEST_INSTANCE_ID
instance.displayName = instance.id
instance.displayDescription = instance.id
self.mox.StubOutWithMock(self.project, 'get_instance')
self.project.get_instance(instance.id).AndReturn(instance)
self.mox.StubOutWithMock(self.project, 'get_instances')
self.project.get_instances().AndReturn([instance])
self.mox.ReplayAll()
res = self.client.get(reverse('nova_instances_detail',
args=[TEST_PROJECT, TEST_INSTANCE_ID]))
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'django_nova/instances/index.html')
self.assertEqual(res.context['selected_instance'].id, instance.id)
self.mox.VerifyAll()

View File

@ -0,0 +1,93 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit tests for key pair views.
"""
import boto.ec2.keypair
import mox
from django.core.urlresolvers import reverse
from django_nova.tests.view_tests.base import (BaseProjectViewTests,
TEST_PROJECT)
TEST_KEY = 'test_key'
class KeyPairViewTests(BaseProjectViewTests):
def test_index(self):
self.mox.StubOutWithMock(self.project, 'get_key_pairs')
self.project.get_key_pairs().AndReturn([])
self.mox.ReplayAll()
response = self.client.get(reverse('nova_keypairs',
args=[TEST_PROJECT]))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'django_nova/keypairs/index.html')
self.assertEqual(len(response.context['keypairs']), 0)
self.mox.VerifyAll()
def test_add_keypair(self):
key = boto.ec2.keypair.KeyPair()
key.name = TEST_KEY
self.mox.StubOutWithMock(self.project, 'create_key_pair')
self.project.create_key_pair(key.name).AndReturn(key)
self.mox.StubOutWithMock(self.project, 'has_key_pair')
self.project.has_key_pair(key.name).AndReturn(False)
self.mox.ReplayAll()
url = reverse('nova_keypairs_add', args=[TEST_PROJECT])
data = {'js': '0', 'name': key.name}
res = self.client.post(url, data)
self.assertEqual(res.status_code, 200)
self.assertEqual(res['Content-Type'], 'application/binary')
self.mox.VerifyAll()
def test_delete_keypair(self):
self.mox.StubOutWithMock(self.project, 'delete_key_pair')
self.project.delete_key_pair(TEST_KEY).AndReturn(None)
self.mox.ReplayAll()
data = {'key_name': TEST_KEY}
url = reverse('nova_keypairs_delete', args=[TEST_PROJECT])
res = self.client.post(url, data)
self.assertRedirectsNoFollow(res, reverse('nova_keypairs',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_download_keypair(self):
material = 'abcdefgh'
session = self.client.session
session['key.%s' % TEST_KEY] = material
session.save()
res = self.client.get(reverse('nova_keypairs_download',
args=['test', TEST_KEY]))
self.assertEqual(res.status_code, 200)
self.assertEqual(res['Content-Type'], 'application/binary')
self.assertContains(res, material)

View File

@ -0,0 +1,43 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit tests for region views.
"""
from django.core.urlresolvers import reverse
from django_nova.tests.view_tests.base import BaseViewTests
from django_nova import shortcuts
TEST_REGION = 'one'
class RegionViewTests(BaseViewTests):
def test_change(self):
self.authenticateTestUser()
session = self.client.session
session['region'] = 'two'
session.save()
data = {'redirect_url': '/',
'region': TEST_REGION}
res = self.client.post(reverse('region_change'), data)
self.assertEqual(self.client.session['region'], TEST_REGION)
self.assertRedirectsNoFollow(res, '/')

View File

@ -0,0 +1,170 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit tests for volume views.
"""
import boto.ec2.volume
import mox
from django.core.urlresolvers import reverse
from django_nova import forms
from django_nova.tests.view_tests.base import (BaseProjectViewTests,
TEST_PROJECT)
TEST_VOLUME = 'vol-0000001'
class VolumeTests(BaseProjectViewTests):
def test_index(self):
instance_id = 'i-abcdefgh'
volume = boto.ec2.volume.Volume()
volume.id = TEST_VOLUME
volume.displayName = TEST_VOLUME
volume.size = 1
self.mox.StubOutWithMock(self.project, 'get_volumes')
self.mox.StubOutWithMock(forms, 'get_available_volume_choices')
self.mox.StubOutWithMock(forms, 'get_instance_choices')
self.project.get_volumes().AndReturn([])
forms.get_available_volume_choices(mox.IgnoreArg()).AndReturn(
self.create_available_volume_choices([volume]))
forms.get_instance_choices(mox.IgnoreArg()).AndReturn(
self.create_instance_choices([instance_id]))
self.mox.ReplayAll()
response = self.client.get(reverse('nova_volumes',
args=[TEST_PROJECT]))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'django_nova/volumes/index.html')
self.assertEqual(len(response.context['volumes']), 0)
self.mox.VerifyAll()
def test_add_get(self):
self.mox.ReplayAll()
res = self.client.get(reverse('nova_volumes_add', args=[TEST_PROJECT]))
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_add_post(self):
vol = boto.ec2.volume.Volume()
vol.name = TEST_VOLUME
vol.displayName = TEST_VOLUME
vol.size = 1
self.mox.StubOutWithMock(self.project, 'create_volume')
self.project.create_volume(vol.size, vol.name, vol.name).AndReturn(vol)
self.mox.ReplayAll()
url = reverse('nova_volumes_add', args=[TEST_PROJECT])
data = {'size': '1',
'nickname': TEST_VOLUME,
'description': TEST_VOLUME}
res = self.client.post(url, data)
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_delete_get(self):
self.mox.ReplayAll()
res = self.client.get(reverse('nova_volumes_delete',
args=[TEST_PROJECT, TEST_VOLUME]))
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_delete_post(self):
self.mox.StubOutWithMock(self.project, 'delete_volume')
self.project.delete_volume(TEST_VOLUME).AndReturn(True)
self.mox.ReplayAll()
res = self.client.post(reverse('nova_volumes_delete',
args=[TEST_PROJECT, TEST_VOLUME]))
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_attach_get(self):
self.mox.ReplayAll()
res = self.client.get(reverse('nova_volumes_attach',
args=[TEST_PROJECT]))
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_attach_post(self):
volume = boto.ec2.volume.Volume()
volume.id = TEST_VOLUME
volume.displayName = TEST_VOLUME
volume.size = 1
instance_id = 'i-abcdefgh'
device = '/dev/vdb'
self.mox.StubOutWithMock(self.project, 'attach_volume')
self.mox.StubOutWithMock(forms, 'get_available_volume_choices')
self.mox.StubOutWithMock(forms, 'get_instance_choices')
self.project.attach_volume(TEST_VOLUME, instance_id, device) \
.AndReturn(True)
forms.get_available_volume_choices(mox.IgnoreArg()).AndReturn(
self.create_available_volume_choices([volume]))
forms.get_instance_choices(mox.IgnoreArg()).AndReturn(
self.create_instance_choices([instance_id]))
self.mox.ReplayAll()
url = reverse('nova_volumes_attach', args=[TEST_PROJECT])
data = {'volume': TEST_VOLUME,
'instance': instance_id,
'device': device}
res = self.client.post(url, data)
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_detach_get(self):
self.mox.ReplayAll()
res = self.client.get(reverse('nova_volumes_detach',
args=[TEST_PROJECT, TEST_VOLUME]))
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()
def test_detach_post(self):
self.mox.StubOutWithMock(self.project, 'detach_volume')
self.project.detach_volume(TEST_VOLUME).AndReturn(True)
self.mox.ReplayAll()
res = self.client.post(reverse('nova_volumes_detach',
args=[TEST_PROJECT, TEST_VOLUME]))
self.assertRedirectsNoFollow(res, reverse('nova_volumes',
args=[TEST_PROJECT]))
self.mox.VerifyAll()

View File

@ -0,0 +1,21 @@
import os
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
DEBUG = True
TESTSERVER = 'http://testserver'
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = '/tmp/django-nova.db'
INSTALLED_APPS = ['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django_nova']
ROOT_URLCONF = 'django_nova.tests.urls'
TEMPLATE_DIRS = (
os.path.join(ROOT_PATH, 'tests', 'templates')
)
SITE_BRANDING = 'OpenStack'
SITE_NAME = 'openstack'
NOVA_DEFAULT_ENDPOINT = 'none'
NOVA_DEFAULT_REGION = 'test'
NOVA_ACCESS_KEY = 'test'
NOVA_SECRET_KEY = 'test'

View File

@ -0,0 +1,55 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
URL patterns for managing Nova projects through the Django admin interface.
"""
from django.conf.urls.defaults import *
#TODO(devcamcar): Standardize url names admin_project_*.
urlpatterns = patterns('',
url(r'^$',
'django_nova.views.admin.projects_list',
name='admin_projects'),
url(r'^add/$',
'django_nova.views.admin.add_project',
name='add_project'),
url(r'^(?P<project_name>[^/]+)/$',
'django_nova.views.admin.project_view',
name='admin_project'),
url(r'^(?P<project_name>[^/]+)/user/(?P<project_user>[^/]+)/delete/',
'django_nova.views.admin.delete_project_user',
name='admin_project_delete_user'),
url(r'^(?P<project_name>[^/]+)/delete/$',
'django_nova.views.admin.delete_project',
name='delete_project'),
url(r'^(?P<project_name>[^/]+)/user/add/$',
'django_nova.views.admin.add_project_user',
name='add_project_user'),
url(r'^(?P<project_name>[^/]+)/user/(?P<project_user>[^/]+)/$',
'django_nova.views.admin.project_user',
name='project_user'),
url(r'^(?P<project_id>[^/]+)/sendcredentials/$',
'django_nova.views.admin.project_sendcredentials',
name='admin_project_sendcredentials'),
url(r'^(?P<project_id>[^/]+)/start_vpn/$',
'django_nova.views.admin.project_start_vpn',
name='admin_project_start_vpn'),
)

View File

@ -0,0 +1,32 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
URL patterns for managing Nova user roles through the Django admin interface.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('',
url(r'^(?P<user_id>[^/]+)/$',
'django_nova.views.admin.user_roles',
name='admin_user_roles'),
url(r'^$',
'django_nova.views.admin.users_list',
name='admin_users_list'),
)

View File

@ -0,0 +1,129 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
URL patterns for managing Nova projects.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('',
url(r'^(?P<project_id>[^/]+)/$',
'django_nova.views.projects.detail',
name='nova_project'),
url(r'^(?P<project_id>[^/]+)/manage/(?P<username>[^/]+)/',
'django_nova.views.projects.edit_user',
name='nova_project_edit_user'),
url(r'^(?P<project_id>[^/]+)/manage$',
'django_nova.views.projects.manage',
name='nova_project_manage'),
url(r'^(?P<project_id>[^/]+)/download/credentials$',
'django_nova.views.projects.download_credentials',
name='nova_download_credentials'),
url(r'^(?P<project_id>[^/]+)/images$',
'django_nova.views.images.index',
name='nova_images'),
url(r'^(?P<project_id>[^/]+)/images/(?P<image_id>[^/]+)/launch$',
'django_nova.views.images.launch',
name='nova_images_launch'),
url(r'^(?P<project_id>[^/]+)/images/(?P<image_id>[^/]+)/remove$',
'django_nova.views.images.remove',
name='nova_images_remove'),
url(r'^(?P<project_id>[^/]+)/images/(?P<image_id>[^/]+)/update$',
'django_nova.views.images.update',
name='nova_images_update'),
url(r'^(?P<project_id>[^/]+)/images/(?P<image_id>[^/]+)/detail$',
'django_nova.views.images.detail',
name='nova_images_detail'),
url(r'^(?P<project_id>[^/]+)/images/(?P<image_id>[^/]+)$',
'django_nova.views.images.privacy',
name='nova_images_privacy'),
url(r'^(?P<project_id>[^/]+)/instances$',
'django_nova.views.instances.index',
name='nova_instances'),
url(r'^(?P<project_id>[^/]+)/instances/refresh$',
'django_nova.views.instances.refresh',
name='nova_instances_refresh'),
url(r'^(?P<project_id>[^/]+)/instances/(?P<instance_id>[^/]+)/refresh$',
'django_nova.views.instances.refresh_detail',
name='nova_instances_refresh_detail'),
url(r'^(?P<project_id>[^/]+)/instances/terminate$',
'django_nova.views.instances.terminate',
name='nova_instances_terminate'),
url(r'^(?P<project_id>[^/]+)/instances/(?P<instance_id>[^/]+)$',
'django_nova.views.instances.detail',
name='nova_instances_detail'),
url(r'^(?P<project_id>[^/]+)/instances/(?P<instance_id>[^/]+)/performance$',
'django_nova.views.instances.performance',
name='nova_instances_performance'),
url(r'^(?P<project_id>[^/]+)/instances/(?P<instance_id>[^/]+)/console$',
'django_nova.views.instances.console',
name='nova_instances_console'),
url(r'^(?P<project_id>[^/]+)/instances/(?P<instance_id>.*)/update$',
'django_nova.views.instances.update',
name='nova_instance_update'),
url(r'^(?P<project_id>[^/]+)/instances/(?P<instance_id>[^/]+)/graph/(?P<graph_name>[^/]+)$',
'django_nova.views.instances.graph',
name='nova_instances_graph'),
url(r'^(?P<project_id>[^/]+)/keys$',
'django_nova.views.keypairs.index',
name='nova_keypairs'),
url(r'^(?P<project_id>[^/]+)/keys/add$',
'django_nova.views.keypairs.add',
name='nova_keypairs_add'),
url(r'^(?P<project_id>[^/]+)/keys/delete$',
'django_nova.views.keypairs.delete',
name='nova_keypairs_delete'),
url(r'^(?P<project_id>[^/]+)/keys/(?P<key_name>.*)/download$',
'django_nova.views.keypairs.download',
name='nova_keypairs_download'),
#url(r'^(?P<project_id>[^/]+)/securitygroups/$',
# 'django_nova.views.securitygroups.index',
# name='nova_securitygroups'),
#url(r'^(?P<project_id>[^/]+)/securitygroups/add$',
# 'django_nova.views.securitygroups.add',
# name='nova_securitygroups_add'),
#url(r'^(?P<project_id>[^/]+)/securitygroups/(?P<group_name>[^/]+)$',
# 'django_nova.views.securitygroups.detail',
# name='nova_securitygroups_detail'),
#url(r'^(?P<project_id>[^/]+)/securitygroups/(?P<group_name>[^/]+)/authorize/$',
# 'django_nova.views.securitygroups.authorize',
# name='nova_securitygroups_authorize'),
#url(r'^(?P<project_id>[^/]+)/securitygroups/(?P<group_name>[^/]+)/delete/$',
# 'django_nova.views.securitygroups.delete',
# name='nova_securitygroups_delete'),
#url(r'^(?P<project_id>[^/]+)/securitygroups/(?P<group_name>.*)/revoke/$',
# 'django_nova.views.securitygroups.revoke',
# name='nova_securitygroups_revoke'),
url(r'^(?P<project_id>[^/]+)/volumes/$',
'django_nova.views.volumes.index',
name='nova_volumes'),
url(r'^(?P<project_id>[^/]+)/volumes/add$',
'django_nova.views.volumes.add',
name='nova_volumes_add'),
url(r'^(?P<project_id>[^/]+)/volumes/attach$',
'django_nova.views.volumes.attach',
name='nova_volumes_attach'),
url(r'^(?P<project_id>[^/]+)/volumes/(?P<volume_id>[^/]+)/detach$',
'django_nova.views.volumes.detach',
name='nova_volumes_detach'),
url(r'^(?P<project_id>[^/]+)/volumes/(?P<volume_id>[^/]+)/delete$',
'django_nova.views.volumes.delete',
name='nova_volumes_delete'),
)

View File

@ -0,0 +1,29 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
URL patterns for managing Nova regions.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('',
url(r'^change/$',
'django_nova.views.regions.change',
name='region_change'),
)

View File

@ -0,0 +1,326 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova through the Django admin interface.
"""
import boto.exception
from django import http
from django import template
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth import models as auth_models
from django.shortcuts import redirect, render_to_response
from django_nova import forms
from django_nova import models
from django_nova.connection import get_nova_admin_connection
@staff_member_required
def project_sendcredentials(request, project_id):
nova = get_nova_admin_connection()
project = nova.get_project(project_id)
users = [user.memberId for user in nova.get_project_members(project_id)]
form = forms.SendCredentialsForm(query_list=users)
if project == None:
raise http.Http404()
if request.method == 'POST':
if len(request.POST.getlist('users')) < 1:
msg = "Please select a user to send credentials to."
return render_to_response('admin/django_nova/project/send_credentials.html', {
'project' : project,
'form' : form,
'users' : users,
'error': msg,
}, context_instance = template.RequestContext(request))
else:
for username in request.POST.getlist('users'):
models.CredentialsAuthorization.authorize(username, project_id)
msg = "Credentials were successfully sent."
return render_to_response('admin/django_nova/project/send_credentials.html', {
'project' : project,
'form' : form,
'users' : users,
'success': msg,
}, context_instance = template.RequestContext(request))
return render_to_response('admin/django_nova/project/send_credentials.html', {
'project' : project,
'form' : form,
'users' : users,
}, context_instance = template.RequestContext(request))
@staff_member_required
def project_start_vpn(request, project_id):
nova = get_nova_admin_connection()
project = nova.get_project(project_id)
if project == None:
raise http.Http404()
try:
nova.start_vpn(project_id)
messages.success(request,
'Successfully started VPN for project %s.' %
project_id)
except boto.exception.EC2ResponseError, e:
messages.error(request,
'Unable to start VPN for the project %s: %s - %s' %
(project_id, e.code, e.error_message))
return redirect('admin_projects')
@staff_member_required
def projects_list(request):
nova = get_nova_admin_connection()
projects = nova.get_projects()
return render_to_response('admin/django_nova/project/project_list.html', {
'projects' : projects
}, context_instance = template.RequestContext(request))
@staff_member_required
def project_view(request, project_name):
nova = get_nova_admin_connection()
project = nova.get_project(project_name)
users = nova.get_project_members(project_name)
try:
manager = auth_models.User.objects.get(username=project.projectManagerId)
except auth_models.User.DoesNotExist:
manager = None
for user in users:
project_role = [str(role.role) for role in nova.get_user_roles(user.memberId, project_name)]
global_role = [str(role.role) for role in nova.get_user_roles(user.memberId, project=False)]
user.project_roles = ", ".join(project_role)
user.global_roles = ", ".join(global_role)
return render_to_response('admin/django_nova/project/edit_project.html', {
'project' : project,
'users' : users,
'projectname': project.projectname,
'manager': manager,
'description': project.description,
}, context_instance = template.RequestContext(request))
@staff_member_required
def add_project(request):
nova = get_nova_admin_connection()
if request.method == 'POST':
form = forms.ProjectForm(request.POST)
if form.is_valid():
manager = form.cleaned_data["manager"]
nova.create_project(form.cleaned_data["projectname"],
manager.username,
form.cleaned_data["description"])
return redirect('admin_project', request.POST["projectname"])
else:
form = forms.ProjectForm()
return render_to_response('admin/django_nova/project/add_project.html', {
'form' : form,
}, context_instance = template.RequestContext(request))
@staff_member_required
def delete_project(request, project_name):
nova = get_nova_admin_connection()
if request.method == 'POST':
nova.delete_project(project_name)
return redirect('admin_projects')
project = nova.get_project(project_name)
return render_to_response('admin/django_nova/project/delete_project.html', {
'project' : project,
}, context_instance = template.RequestContext(request))
def remove_project_roles(username, project):
nova = get_nova_admin_connection()
userroles = nova.get_user_roles(username, project)
roles = [str(role.role) for role in userroles]
for role in roles:
if role == "developer":
nova.remove_user_role(username, "developer", project)
if role == "sysadmin":
nova.remove_user_role(username, "sysadmin", project)
if role == "netadmin":
nova.remove_user_role(username, "netadmin", project)
def remove_global_roles(username):
nova = get_nova_admin_connection()
userroles = nova.get_user_roles(username)
roles = [str(role.role) for role in userroles]
for role in roles:
if role == "developer":
nova.remove_user_role(username, "developer")
if role == "sysadmin":
nova.remove_user_role(username, "sysadmin")
if role == "netadmin":
nova.remove_user_role(username, "netadmin")
if role == "cloudadmin":
nova.remove_user_role(username, "cloudadmin")
if role == "itsec":
nova.remove_user_role(username, "itsec")
@staff_member_required
def project_user(request, project_name, project_user):
nova = get_nova_admin_connection()
userroles = nova.get_user_roles(project_user, project_name)
try:
modeluser = auth_models.User.objects.get(username = project_user)
except auth_models.User.DoesNotExist:
modeluser = None
if request.method == 'POST':
form = forms.ProjectUserForm(request.POST)
if form.is_valid():
username = project_user
# hacky work around to interface correctly with multiple select form
remove_project_roles(username, project_name)
roleform = request.POST.getlist("role")
for role in roleform:
nova.add_user_role(username, str(role), project_name)
return redirect('admin_project', project_name)
else:
roles = [str(role.role) for role in userroles]
form = forms.ProjectUserForm({
'role': roles,
'user': modeluser,
})
project = nova.get_project(project_name)
return render_to_response('admin/django_nova/project/project_user.html', {
'form' : form,
'project' : project,
'user': modeluser,
}, context_instance = template.RequestContext(request))
@staff_member_required
def add_project_user(request, project_name):
nova = get_nova_admin_connection()
if request.method == 'POST':
form = forms.AddProjectUserForm(request.POST, project=project_name)
if form.is_valid():
username = form.cleaned_data["username"].username
nova.add_project_member(username, project_name,)
roleform = request.POST.getlist("role")
for role in roleform:
nova.add_user_role(username, str(role), project_name)
return redirect('admin_project', project_name)
else:
form = forms.AddProjectUserForm(project=project_name)
project = nova.get_project(project_name)
return render_to_response('admin/django_nova/project/add_project_user.html', {
'form' : form,
'project' : project,
}, context_instance = template.RequestContext(request))
@staff_member_required
def delete_project_user(request, project_name, project_user):
nova = get_nova_admin_connection()
if request.method == 'POST':
nova.remove_project_member(project_user, project_name)
return redirect('admin_project', project_name)
project = nova.get_project(project_name)
user = nova.get_user(project_user)
return render_to_response('admin/django_nova/project/delete_project_user.html', {
'user' : user,
'project' : project,
}, context_instance = template.RequestContext(request))
@staff_member_required
def users_list(request):
nova = get_nova_admin_connection()
users = nova.get_users()
for user in users:
# NOTE(devcamcar): Temporarily disabled for performance reasons.
#roles = [str(role.role) for role in nova.get_user_roles(user.username)]
roles = []
user.roles = ", ".join(roles)
return render_to_response('admin/django_nova/project/user_list.html', {
'users' : users
}, context_instance = template.RequestContext(request))
@staff_member_required
def user_roles(request, user_id):
nova = get_nova_admin_connection()
userroles = nova.get_user_roles(user_id)
try:
modeluser = auth_models.User.objects.get(username=user_id)
except auth_models.User.DoesNotExist:
modeluser = None
if request.method == 'POST':
form = forms.GlobalRolesForm(request.POST)
if form.is_valid():
username = user_id
# hacky work around to interface correctly with multiple select form
remove_global_roles(username)
roleform = request.POST.getlist("role")
for role in roleform:
nova.add_user_role(username, str(role))
return redirect('admin_user_roles', user_id)
else:
roles = [str(role.role) for role in userroles]
form = forms.GlobalRolesForm({
'username': modeluser and modeluser.id or None,
'role': roles,
})
return render_to_response('admin/django_nova/project/global_edit_user.html', {
'form' : form,
'user' : modeluser,
}, context_instance = template.RequestContext(request))

View File

@ -0,0 +1,46 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for downloading X509 credentials. Useful when using an invitation
style system for configuring first time users.
"""
from django import http
from django.conf import settings
from django.shortcuts import render_to_response
from django_nova import models
def authorize_credentials(request, auth_token):
"""Sends X509 credentials to user if their auth token is valid."""
auth_token = auth_token.lower()
credentials = models.CredentialsAuthorization.get_by_token(auth_token)
# NOTE(devcamcar): If nothing returned, then token was bad or has expired.
if not credentials:
return render_to_response('django_nova/credentials/expired.html')
response = http.HttpResponse(mimetype='application/zip')
response['Content-Disposition'] = \
'attachment; filename=%s-%s-%s-x509.zip' % \
(settings.SITE_NAME, credentials.project, credentials.username)
response.write(credentials.get_zip())
return response

View File

@ -0,0 +1,229 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova images.
"""
import boto.exception
import re
from django import http
from django import template
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render_to_response
from django_nova import exceptions
from django_nova import forms
from django_nova import shortcuts
from django_nova.exceptions import handle_nova_error
def _image_lists(images, project_id):
def image_is_project(i):
return i.ownerId == project_id
def image_is_admin(i):
return i.ownerId in ['admin']
def image_is_community(i):
return (not image_is_admin(i)) and (not image_is_project(i))
return {'Project Images': filter(image_is_project, images),
'%s Images' % settings.SITE_BRANDING: filter(image_is_admin,
images),
'Community Images': filter(image_is_community, images)}
@login_required
@handle_nova_error
def index(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
images = project.get_images()
return render_to_response('django_nova/images/index.html', {
'form': forms.LaunchInstanceForm(project),
'region': project.region,
'project': project,
'image_lists': _image_lists(images, project_id),
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def launch(request, project_id, image_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
form = forms.LaunchInstanceForm(project, request.POST)
if form.is_valid():
try:
reservation = project.run_instances(
image_id,
addressing_type='private',
key_name=form.cleaned_data['key_name'],
#security_groups=[form.cleaned_data['security_group']],
user_data=re.sub('\r\n', '\n',
form.cleaned_data['user_data']),
instance_type=form.cleaned_data['size'],
min_count=form.cleaned_data['count'],
max_count=form.cleaned_data['count']
)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to launch: %s' % e.message)
else:
for instance in reservation.instances:
messages.success(request,
'Instance %s launched.' % instance.id)
return redirect('nova_instances', project_id)
else:
form = forms.LaunchInstanceForm(project)
ami = project.get_image(image_id)
return render_to_response('django_nova/images/launch.html', {
'form': form,
'region': project.region,
'project': project,
'ami': ami,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def detail(request, project_id, image_id):
project = shortcuts.get_project_or_404(request, project_id)
images = project.get_images()
ami = project.get_image(image_id)
can_modify = shortcuts.get_user_image_permissions(request.user.username,
project_id)
if not ami:
raise http.Http404()
return render_to_response('django_nova/images/index.html', {
'form': forms.LaunchInstanceForm(project),
'region': project.region,
'project': project,
'images': images,
'image_lists': _image_lists(images, project_id),
'ami': ami,
'can_modify': can_modify,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def remove(request, project_id, image_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
try:
project.deregister_image(image_id)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to deregister image: %s' % e.message)
else:
messages.success(request,
'Image %s has been successfully deregistered.' %
image_id)
return redirect('nova_images', project_id)
else:
ami = project.get_image(image_id)
#FIXME - is the code below used? if we reach here should we
#just redirect? (anthony)
return render_to_response('django_nova/images/detail_list.html', {
'region': project.region,
'project': project,
'ami': ami,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def privacy(request, project_id, image_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
ami = project.get_image(image_id)
if ami.is_public:
try:
project.modify_image_attribute(image_id,
attribute='launchPermission',
operation='remove')
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to make image private: %s' % e.message)
else:
try:
project.modify_image_attribute(image_id,
attribute='launchPermission',
operation='add')
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to make image public: %s' % e.message)
return redirect('nova_images_detail', project_id, image_id)
@login_required
@handle_nova_error
def update(request, project_id, image_id):
project = shortcuts.get_project_or_404(request, project_id)
ami = project.get_image(image_id)
if request.method == 'POST':
form = forms.UpdateImageForm(ami, request.POST)
if form.is_valid():
try:
project.update_image(image_id,
form.cleaned_data['nickname'],
form.cleaned_data['description'])
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to update image: %s' % e.message)
else:
messages.success(request,
'Image %s has been updated.' % image_id)
return redirect('nova_images_detail', project_id, image_id)
# TODO(devcamcar): This needs to be cleaned up. Can make
# one of the render_to_response blocks go away.
else:
form = forms.UpdateImageForm(ami)
return render_to_response('django_nova/images/edit.html', {
'form': form,
'region': project.region,
'project': project,
'ami': ami,
}, context_instance = template.RequestContext(request))
else:
form = forms.UpdateImageForm(ami)
return render_to_response('django_nova/images/edit.html', {
'form': form,
'region': project.region,
'project': project,
'ami': ami,
}, context_instance = template.RequestContext(request))

View File

@ -0,0 +1,203 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova instances.
"""
from django import http
from django import template
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render_to_response
from django_nova import exceptions
from django_nova import forms as nova_forms
from django_nova import shortcuts
from django_nova.exceptions import handle_nova_error
@login_required
@handle_nova_error
def index(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
instances = sorted(project.get_instances(), key=lambda k: k.public_dns_name)
return render_to_response('django_nova/instances/index.html', {
'region': project.region,
'project': project,
'instances': instances,
'detail' : False,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def detail(request, project_id, instance_id):
project = shortcuts.get_project_or_404(request, project_id)
instance = project.get_instance(instance_id)
instances = sorted(project.get_instances(), key=lambda k: k.public_dns_name)
if not instance:
raise http.Http404()
return render_to_response('django_nova/instances/index.html', {
'region': project.region,
'project': project,
'selected_instance': instance,
'instances': instances,
'update_form': nova_forms.UpdateInstanceForm(instance),
'detail' : True,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def performance(request, project_id, instance_id):
project = shortcuts.get_project_or_404(request, project_id)
instance = project.get_instance(instance_id)
if not instance:
raise http.Http404()
return render_to_response('django_nova/instances/performance.html', {
'region': project.region,
'project': project,
'instance': instance,
'update_form': nova_forms.UpdateInstanceForm(instance),
}, context_instance = template.RequestContext(request))
# TODO(devcamcar): Wrap this in an @ajax decorator.
def refresh(request, project_id):
# TODO(devcamcar): This logic belongs in decorator.
if not request.user.is_authenticated():
return http.HttpResponseForbidden()
project = shortcuts.get_project_or_404(request, project_id)
instances = sorted(project.get_instances(), key=lambda k: k.public_dns_name)
return render_to_response('django_nova/instances/_instances_list.html', {
'project': project,
'instances': instances,
}, context_instance = template.RequestContext(request))
@handle_nova_error
def refresh_detail(request, project_id, instance_id):
# TODO(devcamcar): This logic belongs in decorator.
if not request.user.is_authenticated():
return http.HttpResponseForbidden()
project = shortcuts.get_project_or_404(request, project_id)
instance = project.get_instance(instance_id)
instances = sorted(project.get_instances(), key=lambda k: k.public_dns_name)
return render_to_response('django_nova/instances/_instances_list.html', {
'project': project,
'selected_instance': instance,
'instances': instances,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def terminate(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
instance_id = request.POST['instance_id']
try:
project.terminate_instance(instance_id)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to terminate %s: %s' %
(instance_id, e.message,))
else:
messages.success(request,
'Instance %s has been terminated.' % instance_id)
return redirect('nova_instances', project_id)
@login_required
@handle_nova_error
def console(request, project_id, instance_id):
project = shortcuts.get_project_or_404(request, project_id)
conn = project.get_nova_connection()
console = conn.get_console_output(instance_id)
response = http.HttpResponse(mimetype='text/plain')
response.write(console.output)
response.flush()
return response
@login_required
@handle_nova_error
def graph(request, project_id, instance_id, graph_name):
project = shortcuts.get_project_or_404(request, project_id)
graph = project.get_instance_graph(instance_id, graph_name)
if graph is None:
raise http.Http404()
response = http.HttpResponse(mimetype='image/png')
response.write(graph)
return response
@login_required
@handle_nova_error
def update(request, project_id, instance_id):
project = shortcuts.get_project_or_404(request, project_id)
instance = project.get_instance(instance_id)
if not instance:
raise http.Http404()
if request.method == 'POST':
form = nova_forms.UpdateInstanceForm(instance, request.POST)
if form.is_valid():
try:
project.update_instance(instance_id, form.cleaned_data)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to update instance %s: %s' %
(instance_id, e.message,))
else:
messages.success(request,
'Instance %s has been updated.' % instance_id)
return redirect('nova_instances', project_id)
else:
return render_to_response('django_nova/instances/edit.html', {
'region': project.region,
'project': project,
'instance': instance,
'update_form': form,
}, context_instance = template.RequestContext(request))
else:
return render_to_response('django_nova/instances/edit.html', {
'region': project.region,
'project': project,
'instance': instance,
'update_form': nova_forms.UpdateInstanceForm(instance),
}, context_instance = template.RequestContext(request))

View File

@ -0,0 +1,122 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova keypairs.
"""
from django import http
from django import template
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render_to_response
from django_nova import exceptions
from django_nova import forms
from django_nova import shortcuts
from django_nova.exceptions import handle_nova_error
@login_required
@handle_nova_error
def index(request, project_id, download_key=None):
project = shortcuts.get_project_or_404(request, project_id)
keypairs = project.get_key_pairs()
return render_to_response('django_nova/keypairs/index.html', {
'create_form': forms.CreateKeyPairForm(project),
'region': project.region,
'project': project,
'keypairs': keypairs,
'download_key': download_key
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def add(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
form = forms.CreateKeyPairForm(project, request.POST)
if form.is_valid():
try:
keypair = project.create_key_pair(form.cleaned_data['name'])
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to create key: %s' % e.message)
else:
if request.POST['js'] == '1':
request.session['key.%s' % keypair.name] = keypair.material
return index(request,
project_id,
download_key=keypair.name)
else:
response = http.HttpResponse(mimetype='application/binary')
response['Content-Disposition'] = \
'attachment; filename=%s.pem' % \
form.cleaned_data['name']
response.write(keypair.material)
return response
else:
keypairs = project.get_key_pairs()
return render_to_response('django_nova/keypairs/index.html', {
'create_form': form,
'region': project.region,
'project': project,
'keypairs': keypairs,
}, context_instance = template.RequestContext(request))
return redirect('nova_keypairs', project_id)
@login_required
@handle_nova_error
def delete(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
key_name = request.POST['key_name']
try:
project.delete_key_pair(key_name)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to delete key: %s' % e.message)
else:
messages.success(request,
'Key %s has been successfully deleted.' % \
key_name)
return redirect('nova_keypairs', project_id)
@login_required
@handle_nova_error
def download(request, project_id, key_name):
# Ensure the project exists.
shortcuts.get_project_or_404(request, project_id)
try:
material = request.session.pop('key.%s' % key_name)
except KeyError:
return redirect('nova_keypairs', project_id)
response = http.HttpResponse(mimetype='application/binary')
response['Content-Disposition'] = 'attachment; filename=%s.pem' % key_name
response.write(material)
return response

View File

@ -0,0 +1,107 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova projects.
"""
from django import http
from django import template
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render_to_response
from django_nova import forms as nova_forms
from django_nova.connection import get_nova_admin_connection
from django_nova.exceptions import handle_nova_error
from django_nova.shortcuts import get_project_or_404
@login_required
@handle_nova_error
def detail(request, project_id):
project = get_project_or_404(request, project_id)
return render_to_response('django_nova/projects/index.html', {
'project': project,
'instance_count': project.get_instance_count(),
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def manage(request, project_id):
project = get_project_or_404(request, project_id)
if project.projectManagerId != request.user.username:
return redirect('login')
nova = get_nova_admin_connection()
members = nova.get_project_members(project_id)
for member in members:
project_role = [str(role.role) for role in nova.get_user_roles(member.memberId, project_id)]
global_role = [str(role.role) for role in nova.get_user_roles(member.memberId, project=False)]
member.project_roles = ", ".join(project_role)
member.global_roles = ", ".join(global_role)
return render_to_response('django_nova/projects/manage.html', {
'project': project,
'members': members,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def edit_user(request, project_id, username):
nova = get_nova_admin_connection()
project = get_project_or_404(request, project_id)
user = nova.get_user(username)
if project.projectManagerId != request.user.username:
return redirect('login')
if request.method == 'POST':
form = nova_forms.ProjectUserForm(project, user, request.POST)
if form.is_valid():
form.save()
return redirect('nova_project_manage', project_id)
else:
form = nova_forms.ProjectUserForm(project, user)
return render_to_response('django_nova/projects/edit_user.html', {
'form' : form,
'project': project,
'user' : user,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def download_credentials(request, project_id):
project = get_project_or_404(request, project_id)
response = http.HttpResponse(mimetype='application/zip')
response['Content-Disposition'] = \
'attachment; filename=%s-%s-%s-x509.zip' % \
(settings.SITE_NAME, project.projectname, request.user)
response.write(project.get_zip())
return response

View File

@ -0,0 +1,36 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova regions.
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django_nova.shortcuts import set_current_region
@login_required
def change(request):
region = request.POST['region']
redirect_url = request.POST['redirect_url']
set_current_region(request, region)
messages.success(request, 'You are now using the region "%s".' % region)
return redirect(redirect_url)

View File

@ -0,0 +1,180 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova security groups.
"""
from django import http
from django import template
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render_to_response
from django_nova import exceptions
from django_nova import forms
from django_nova.exceptions import handle_nova_error
from django_nova.shortcuts import get_project_or_404
@login_required
@handle_nova_error
def index(request, project_id):
project = get_project_or_404(request, project_id)
securitygroups = project.get_security_groups()
return render_to_response('django_nova/securitygroups/index.html', {
'create_form': forms.CreateSecurityGroupForm(project),
'project': project,
'securitygroups': securitygroups,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def detail(request, project_id, group_name):
project = get_project_or_404(request, project_id)
securitygroup = project.get_security_group(group_name)
if not securitygroup:
raise http.Http404
return render_to_response('django_nova/securitygroups/detail.html', {
'authorize_form': forms.AuthorizeSecurityGroupRuleForm(),
'project': project,
'securitygroup': securitygroup,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def add(request, project_id):
project = get_project_or_404(request, project_id)
if request.method == 'POST':
form = forms.CreateSecurityGroupForm(project, request.POST)
if form.is_valid():
try:
project.create_security_group(
form.cleaned_data['name'],
form.cleaned_data['description'])
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to create security group: %s' % e.message)
else:
messages.success(
request,
'Security Group %s has been succesfully created.' % \
form.cleaned_data['name'])
else:
securitygroups = project.get_security_groups()
return render_to_response('django_nova/securitygroups/index.html', {
'create_form': form,
'project': project,
'securitygroups': securitygroups,
}, context_instance = template.RequestContext(request))
return redirect('nova_securitygroups', project_id)
@login_required
@handle_nova_error
def authorize(request, project_id, group_name):
project = get_project_or_404(request, project_id)
if request.method == 'POST':
form = forms.AuthorizeSecurityGroupRuleForm(request.POST)
if form.is_valid():
try:
project.authorize_security_group(
group_name = group_name,
ip_protocol = form.cleaned_data['protocol'],
from_port = form.cleaned_data['from_port'],
to_port = form.cleaned_data['to_port'])
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to authorize: %s' % e.message)
else:
messages.success(
request,
'Security Group %s: Access to %s ports %d - %d'
' has been authorized.' %
(group_name,
form.cleaned_data['protocol'],
form.cleaned_data['from_port'],
form.cleaned_data['to_port']))
else:
securitygroup = project.get_security_group(group_name)
if not securitygroup:
raise http.Http404
return render_to_response('django_nova/securitygroups/detail.html', {
'authorize_form': form,
'project': project,
'securitygroup': securitygroup,
}, context_instance = template.RequestContext(request))
return redirect('nova_securitygroups_detail', project_id, group_name)
@login_required
@handle_nova_error
def revoke(request, project_id, group_name):
project = get_project_or_404(request, project_id)
if request.method == 'POST':
try:
project.revoke_security_group(
group_name = group_name,
ip_protocol = request.POST['protocol'],
from_port = request.POST['from_port'],
to_port = request.POST['to_port'])
except exceptions.NovaApiError, e:
messages.error(request, 'Unable to revoke: %s' % e.message)
else:
messages.success(
request,
'Security Group %s: Access to %s ports %s - %s '
'has been revoked.' %
(group_name,
request.POST['protocol'],
request.POST['from_port'],
request.POST['to_port']))
return redirect('nova_securitygroups_detail', project_id, group_name)
@login_required
@handle_nova_error
def delete(request, project_id, group_name):
project = get_project_or_404(request, project_id)
if request.method == 'POST':
try:
project.delete_security_group(name=group_name)
except exceptions.NovaApiError, e:
messages.error(
request,
'Unable to delete security group: %s' % e.message)
else:
messages.success(request,
'Security Group %s was successfully deleted.' %
group_name)
return redirect('nova_securitygroups', project_id)

View File

@ -0,0 +1,151 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing Nova volumes.
"""
from django import template
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render_to_response
from django_nova import exceptions
from django_nova import forms
from django_nova import shortcuts
from django_nova.exceptions import handle_nova_error
@login_required
@handle_nova_error
def index(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
volumes = project.get_volumes()
return render_to_response('django_nova/volumes/index.html', {
'create_form': forms.CreateVolumeForm(),
'attach_form': forms.AttachVolumeForm(project),
'region': project.region,
'project': project,
'volumes': volumes,
}, context_instance = template.RequestContext(request))
@login_required
@handle_nova_error
def add(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
form = forms.CreateVolumeForm(request.POST)
if form.is_valid():
try:
volume = project.create_volume(form.cleaned_data['size'],
form.cleaned_data['nickname'],
form.cleaned_data['description'])
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to create volume: %s' % e.message)
else:
messages.success(request,
'Volume %s %s has been successfully created.' %
(volume.id, volume.displayName))
else:
volumes = project.get_volumes()
return render_to_response('django_nova/volumes/index.html', {
'create_form': form,
'attach_form': forms.AttachVolumeForm(project),
'region': project.region,
'project': project,
'volumes': volumes,
}, context_instance = template.RequestContext(request))
return redirect('nova_volumes', project_id)
@login_required
@handle_nova_error
def delete(request, project_id, volume_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
try:
project.delete_volume(volume_id)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to delete volume: %s' % e.message)
else:
messages.success(request,
'Volume %s has been successfully deleted.'
% volume_id)
return redirect('nova_volumes', project_id)
@login_required
@handle_nova_error
def attach(request, project_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
form = forms.AttachVolumeForm(project, request.POST)
if form.is_valid():
try:
project.attach_volume(
form.cleaned_data['volume'],
form.cleaned_data['instance'],
form.cleaned_data['device']
)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to attach volume: %s' % e.message)
else:
messages.success(request,
'Volume %s has been successfully attached.' %
form.cleaned_data['volume'])
else:
volumes = project.get_volumes()
return render_to_response('django_nova/volumes/index.html', {
'create_form': forms.CreateVolumeForm(),
'attach_form': form,
'region': project.region,
'project': project,
'volumes': volumes,
}, context_instance = template.RequestContext(request))
return redirect('nova_volumes', project_id)
@login_required
@handle_nova_error
def detach(request, project_id, volume_id):
project = shortcuts.get_project_or_404(request, project_id)
if request.method == 'POST':
try:
project.detach_volume(volume_id)
except exceptions.NovaApiError, e:
messages.error(request,
'Unable to detach volume: %s' % e.message)
else:
messages.success(request,
'Volume %s has been successfully detached.' %
volume_id)
return redirect('nova_volumes', project_id)

View File

@ -0,0 +1,49 @@
OpenStack Dashboard
-------------------
The OpenStack Dashboard is a reference implementation of a Django site that
uses the Django-Nova project to provide web based interactions with the
OpenStack Nova cloud controller.
For more information about the Django-Nova project, please visit:
http://launchpad.net/django-nova
Getting Started
---------------
The first step is to obtain a local copy of the django-nova project:
$ mkdir django-nova
$ cd django-nova
$ bzr init-repo .
$ bzr branch lp:django-nova/trunk
Next we will create the virtualenv for local development. A tool is included to
create one for you:
$ python tools/install_venv.py
Now that the virtualenv is created, you need to configure your local
environment. To do this, create a local_settings.py file in the local/
directory. There is a local_settings.py.example file there that may be used
as a template.
Finally, issue the django syncdb command:
$ tools/with_venv.sh dashboard/manage.py syncdb
If after you have specified the admin user the script appears to hang, it
probably means the installation of Nova being referred to in local_settings.py
is unavailable.
If all is well you should now able to run the server locally:
$ tools/with_venv.sh dashboard/manage.py runserver

Some files were not shown because too many files have changed in this diff Show More