Add django-nova-syspanel, a holistic operations view of the cloud.
This commit is contained in:
commit
5e4afc76a4
@ -5,6 +5,7 @@ django-nova/downloads/
|
||||
django-nova/eggs/
|
||||
django-nova/parts/
|
||||
django-nova/src/django_nova.egg-info
|
||||
django-nova-syspanel/src/django_nova_syspanel.egg-info
|
||||
openstack-dashboard/.dashboard-venv
|
||||
openstack-dashboard/local/dashboard_openstack.sqlite3
|
||||
openstack-dashboard/local/local_settings.py
|
||||
|
176
django-nova-syspanel/LICENSE
Normal file
176
django-nova-syspanel/LICENSE
Normal 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.
|
||||
|
38
django-nova-syspanel/README
Normal file
38
django-nova-syspanel/README
Normal file
@ -0,0 +1,38 @@
|
||||
OpenStack Django-Nova-Syspanel
|
||||
------------------------------
|
||||
|
||||
The Django-Nova-Syspanel package provides a django app that interacts
|
||||
with the OpenStack Nova cloud controller to view the health of a
|
||||
running OpenStack installation.
|
||||
|
||||
This is packaged, and intended to be used inside of, OpenStack
|
||||
Dashboard, which is located at
|
||||
|
||||
http://launchpad.net/openstack-dashboard
|
||||
|
||||
Development against this application is best done inside this reference
|
||||
implementation, though in practice any django app can install this
|
||||
application.
|
||||
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
Django-Nova-Syspanel 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-syspanel 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-syspanel/bootstrap.py
Normal file
260
django-nova-syspanel/bootstrap.py
Normal 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-syspanel/buildout.cfg
Normal file
19
django-nova-syspanel/buildout.cfg
Normal file
@ -0,0 +1,19 @@
|
||||
[buildout]
|
||||
parts = python django
|
||||
develop = .
|
||||
eggs = django-nova-syspanel
|
||||
|
||||
[python]
|
||||
recipe = zc.recipe.egg
|
||||
interpreter = python
|
||||
eggs = ${buildout:eggs}
|
||||
|
||||
[django]
|
||||
recipe = djangorecipe
|
||||
version = 1.2.4
|
||||
project = django_nova_syspanel
|
||||
projectegg = django_nova_syspanel
|
||||
settings = testsettings
|
||||
test = django_nova_syspanel
|
||||
eggs = ${buildout:eggs}
|
||||
|
29
django-nova-syspanel/setup.py
Normal file
29
django-nova-syspanel/setup.py
Normal 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-syspanel",
|
||||
version = "0.1",
|
||||
url = 'https://launchpad.net/openstack-dashboard/',
|
||||
license = 'Apache 2.0',
|
||||
description = "A Django interface for OpenStack Nova.",
|
||||
long_description = read('README'),
|
||||
author = 'Todd Willey',
|
||||
author_email = 'xtoddx@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',
|
||||
]
|
||||
)
|
||||
|
9
django-nova-syspanel/src/django_nova_syspanel/forms.py
Normal file
9
django-nova-syspanel/src/django_nova_syspanel/forms.py
Normal file
@ -0,0 +1,9 @@
|
||||
from django import forms
|
||||
|
||||
|
||||
class DisableProject(forms.Form):
|
||||
project_name = forms.CharField()
|
||||
|
||||
|
||||
class DisableIpAddress(forms.Form):
|
||||
cidr = forms.CharField()
|
418
django-nova-syspanel/src/django_nova_syspanel/models.py
Normal file
418
django-nova-syspanel/src/django_nova_syspanel/models.py
Normal file
@ -0,0 +1,418 @@
|
||||
import sys
|
||||
import boto
|
||||
import boto.exception
|
||||
import boto.s3
|
||||
from boto.ec2.volume import Volume
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models.signals import post_save
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from xml.dom import minidom
|
||||
from nova_adminclient import NovaAdminClient
|
||||
|
||||
project_permissions = ('admin',)
|
||||
|
||||
class NovaResponseError(Exception):
|
||||
def __init__(self, ec2error):
|
||||
if ec2error.reason == 'Forbidden':
|
||||
self.code = 'Forbidden'
|
||||
self.message = 'Forbidden'
|
||||
return
|
||||
|
||||
dom = minidom.parseString(ec2error.body)
|
||||
err = dom.getElementsByTagName('Errors')[0]
|
||||
|
||||
try:
|
||||
self.code = err.getElementsByTagName('Code')[0].childNodes[0].data
|
||||
except IndexError:
|
||||
self.code = 'Unknown'
|
||||
|
||||
try:
|
||||
self.message = err.getElementsByTagName('Message')[0].childNodes[0].data
|
||||
except IndexError:
|
||||
self.message = 'An unexpected error occurred. Please try your request again.'
|
||||
|
||||
dom.unlink()
|
||||
|
||||
def __str__(self):
|
||||
return '%s (%s)' % (self.message, self.code)
|
||||
|
||||
|
||||
def handle_nova_error(fn):
|
||||
def decorator(view_func):
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return view_func(*args, **kwargs)
|
||||
except boto.exception.EC2ResponseError, e:
|
||||
if e.reason == 'Forbidden':
|
||||
raise PermissionDenied()
|
||||
else:
|
||||
raise NovaResponseError(e)
|
||||
return wrapper
|
||||
return decorator(fn)
|
||||
|
||||
def get_nova_admin_connection():
|
||||
"""
|
||||
Returns a Nova administration connection.
|
||||
"""
|
||||
return NovaAdminClient (
|
||||
clc_url=settings.NOVA_DEFAULT_ENDPOINT,
|
||||
region=settings.NOVA_DEFAULT_REGION,
|
||||
access_key=settings.NOVA_ACCESS_KEY,
|
||||
secret_key=settings.NOVA_SECRET_KEY
|
||||
)
|
||||
|
||||
class VolumeInfo(object):
|
||||
"""Foo"""
|
||||
|
||||
def __init__(self, connection=None, username=None, endpoint=None):
|
||||
print "VOLUME"
|
||||
print "=================================="
|
||||
print "=================================="
|
||||
print "=================================="
|
||||
self.connection = connection
|
||||
self.username = username
|
||||
self.endpoint = endpoint
|
||||
|
||||
self.id = None
|
||||
self.create_time = None
|
||||
self.status = None
|
||||
self.size = None
|
||||
self.snapshot_id = None
|
||||
self.attach_data = None
|
||||
self.zone = None
|
||||
|
||||
self.display_name = None
|
||||
self.display_description = None
|
||||
|
||||
def startElement(self, name, attrs, connection):
|
||||
return None
|
||||
|
||||
def endElement(self, name, value, connection):
|
||||
if name == 'volumeId':
|
||||
self.id = str(value)
|
||||
elif name == 'size':
|
||||
self.size = int(value)
|
||||
elif name == 'status':
|
||||
self.status = str(value)
|
||||
elif name == 'displayName':
|
||||
self.display_name = str(value)
|
||||
elif name == 'displayDescription':
|
||||
self.display_description = str(value)
|
||||
else:
|
||||
print "*** UNKNOWN *** %s ***" % name
|
||||
setattr(self, name, str(value))
|
||||
print repr(self)
|
||||
|
||||
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
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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 S3ResponseError, e:
|
||||
if e.code == "NoSuchBucket":
|
||||
return None
|
||||
else:
|
||||
raise e
|
||||
|
||||
key = bucket.get_key(graph_name)
|
||||
|
||||
return key.read()
|
||||
|
||||
@handle_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])
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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) != None
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_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'
|
||||
)
|
||||
|
||||
@handle_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'
|
||||
)
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_nova_error
|
||||
def get_volumes(self):
|
||||
"""
|
||||
Returns all volumes in this project.
|
||||
"""
|
||||
conn = self.get_nova_connection()
|
||||
return conn.get_all_volumes()
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_nova_error
|
||||
def delete_volume(self, volume_id):
|
||||
conn = self.get_nova_connection()
|
||||
return conn.delete_volume(volume_id)
|
||||
|
||||
@handle_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)
|
||||
|
||||
@handle_nova_error
|
||||
def detach_volume(self, volume_id):
|
||||
conn = self.get_nova_connection()
|
||||
return conn.detach_volume(volume_id)
|
||||
|
||||
def user_post_save(sender, instance, created, *args, **kwargs):
|
||||
"""
|
||||
Creates a Nova User when a new Django User is created.
|
||||
"""
|
||||
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, User, dispatch_uid='django.contrib.auth.models.User.post_save')
|
@ -0,0 +1,45 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block breadcrumb %}{{ block.super }}<li><a href="{% url syspanel_cloudview %}">Cloud View</a></li>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="page_description">
|
||||
<h2 class="page_heading">Cloud View</h2>
|
||||
<p>View servers that participate in your cloud. See statistics about
|
||||
running services and served objects.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Hostname</th>
|
||||
<th>Compute Status</th>
|
||||
<th>Instance Count</th>
|
||||
<th>Volume Status</th>
|
||||
<th>Volume Count</th>
|
||||
</tr>
|
||||
|
||||
{% for node in nodes %}
|
||||
<tr class="{% cycle 'odd' 'even' %}">
|
||||
<td class="odd {% if node.compute == "Down" and node.volume == "Down" %}down{% else %}{% if node.compute == "Down" and node.volume == "Up" or node.compute == "Up" and node.volume == "Down" %}caution{% else %}running{% endif %}{% endif %}">{{node.hostname}}</td>
|
||||
{% if node.compute %}
|
||||
<td class="{% if node.compute == "Down" %}down{% else %}running{% endif %}">{{node.compute}}</td>
|
||||
<td class="odd {% if node.compute == "Down" %}down{% else %}running{% endif %}">{{node.instance_count}}</td>
|
||||
{% else %}
|
||||
<td class="unavailable">N/A</td>
|
||||
<td class="unavailable">N/A</td>
|
||||
{% endif %}
|
||||
|
||||
{% if node.volume %}
|
||||
<td class="{% if node.volume == "Down" %}down{% else %}running{% endif %}">{{node.volume}}</td>
|
||||
<td class="odd {% if node.volume == "Down" %}down{% else %}running{% endif %}">{{node.volume_count}}</td>
|
||||
{% else %}
|
||||
<td class="unavailable">N/A</td>
|
||||
<td class="unavailable">N/A</td>
|
||||
{% endif %}
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,45 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="page_heading">NASA Nebula System Panel</h2>
|
||||
|
||||
<div class="block">
|
||||
<h3 class="half-heading">Manage Instances</h3>
|
||||
<p>View all running instances other than VPNs. Schedule instances for
|
||||
reboot or termination. Download logs.</p>
|
||||
<a class="more" href="{% url syspanel_instances %}" >Manage Instances →</a>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<h3 class="half-heading">Manage Volumes</h3>
|
||||
<p>View all volumes. See size, status, and mount point. Destroy volumes.</p>
|
||||
<a class="more" href="{% url syspanel_volumes %}" >Manage Volumes →</a>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<h3 class="half-heading">Manage VPN</h3>
|
||||
<p>View all projects VPN status. Schedule launch, termination, and reboot.
|
||||
Send credentials.</p>
|
||||
<a class="more" href="{% url syspanel_vpns %}" >Manage VPN →</a>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<h3 class="half-heading">Cloud View</h3>
|
||||
<p>View servers that participate in your cloud. See statistics about
|
||||
running services and served objects.</p>
|
||||
<a class="more" href="{% url syspanel_cloudview %}" >Cloud View →</a>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<h3 class="half-heading">Security</h3>
|
||||
<p>Perform operations to deal with rogue users, limit or prevent compromise,
|
||||
and deal with external threats.</p>
|
||||
<a class="more" href="{% url syspanel_security %}" >Security →</a>
|
||||
</div>
|
||||
|
||||
{# <div class="block"> #}
|
||||
{# <h3 class="half-heading">Resource Monitors</h3> #}
|
||||
{# <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec venenatis, libero ac auctor laoreet, nunc dolor volutpat purus, sit amet posuere turpis diam sit amet tellus. Praesent nec pede. </p> #}
|
||||
{# <a class="more" href="#" >Resource Monitors →</a> #}
|
||||
{# </div> #}
|
||||
{% endblock %}
|
@ -0,0 +1,73 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block headerjs %}
|
||||
|
||||
<link rel="stylesheet" href="/media/systempanel/css/flexigrid.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script src="/media/systempanel/js/flexigrid.pack.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
|
||||
$(function(){
|
||||
$("table").flexigrid({
|
||||
height: "auto"
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}{{ block.super }}<li><a href="{% url syspanel_instances %}">Instances</a></li>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="page_description">
|
||||
<h2 class="page_heading">Manage Instances</h2>
|
||||
<p>View all running instances other than VPNs. Schedule instances for
|
||||
reboot or termination. Download logs.</p>
|
||||
</div>
|
||||
<div class="message">
|
||||
{% if status == "success" %}
|
||||
<div class="success">{{message}}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if status == "error" %}
|
||||
<div class="error">{{message}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="refresh">
|
||||
<a href="{% url syspanel_instances %}">Refresh List</a>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>User</th>
|
||||
<th>Node</th>
|
||||
<th>Created</th>
|
||||
<th>Image</th>
|
||||
<th>IP</th>
|
||||
<th>Public IP</th>
|
||||
<th>Volumes</th>
|
||||
<th>State</th>
|
||||
<th>Operations</th>
|
||||
</tr>
|
||||
{% for instance in instances %}
|
||||
<tr id="{{instance.id}}" class="even">
|
||||
<td class="odd">{{instance.id}}</td>
|
||||
<td>{{instance.owner_id}}</td>
|
||||
<td class="odd">{{instance.host_name}}</td>
|
||||
<td>{{instance.launch_time}}</td>
|
||||
<td class="odd" title="bucket/image.manifest.xml">{{instance.image_id}}</td>
|
||||
<td>{{instance.public_dns_name}}</td>
|
||||
<td class="odd">{{instance.private_ip_address}}</td>
|
||||
<td><a href="volume_list.html#vol-12324">vol-12324</a></td>
|
||||
<td class="odd">{{instance.state}}</td>
|
||||
<td>
|
||||
<a href="{% url syspanel_instance_terminate instance.id %}">Terminate</a> |
|
||||
<a href="{% url syspanel_instance_restart instance.id %}">Restart</a> |
|
||||
<a href="{% url syspanel_instance_console instance.id %}">Nova Log</a> |
|
||||
<a href="logs_console.txt">Console Log</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endblock %}
|
@ -0,0 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block breadcrumb %}{{ block.super }}<li><a href="{% url syspanel_security %}">Security</a></li>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="page_heading">{{project}} Disabled</h2>
|
||||
|
||||
<p>
|
||||
If a user's credentials were comprimised, you will need to use
|
||||
`nova-manage user revoke USERNAME`, and the restart all VPNs for projects
|
||||
they are a member of.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The VPN for {{project}} is currently offline. You can turn it back on
|
||||
using <a href="{% url syspanel_vpns %}">VPN overview</a>.
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,80 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block breadcrumb %}{{ block.super }}<li><a href="{% url syspanel_security %}">Security</a></li>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="page_heading">Security</h2>
|
||||
|
||||
<h2>Use the following tools to secure the cloud during a security event</h2>
|
||||
|
||||
<div id="scenerios">
|
||||
<div class="scenerio">
|
||||
<div class="description">
|
||||
<p><strong>Scenario:</strong> Project account credentials have been compromised.</p>
|
||||
<p><strong>Action:</strong> Disable the user or project. This should invalidate the credentials, also deny the specific port assigned to the user.</p>
|
||||
</div>
|
||||
|
||||
<form id="disable_project_credentials" action="{% url syspanel_security_disable_project_credentials %}" method="post" accept-charset="utf-8">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
{% for field in project_form %}
|
||||
{{ field.label_tag }}
|
||||
{% if field.errors %}{{ field.errors }}{% endif %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
|
||||
<input type="submit" value="Turn off user/project" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="scenerio">
|
||||
<div class="description">
|
||||
<p><strong>Scenario:</strong> The issue pin points to be coming in from a specific external ip or ip range.</p>
|
||||
<p><strong>Action:</strong> Block the IP or IP range. This should manipulate ip tables to block the ip or ip range.</p>
|
||||
</div>
|
||||
|
||||
<form id="disable_ip_range" action="{% url syspanel_security_disable_ip_range %}" method="post" accept-charset="utf-8">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
{% for field in ip_form %}
|
||||
{{ field.label_tag }}
|
||||
{% if field.errors %}{{ field.errors }}{% endif %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
|
||||
<input type="submit" value="Block IP Range" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scenerio">
|
||||
<div class="description">
|
||||
<p><strong>Scenario:</strong> Issues affecting multiple public entry points or could not determine to be a specific one.</p>
|
||||
<p><strong>Action:</strong> Disable all public IPs on all VMs. This should unplumb the device the public ip was on.</p>
|
||||
</div>
|
||||
|
||||
<form action="{% url syspanel_security_disable_public_ips %}" method="post" accept-charset="utf-8">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<input name="turn_off_ips" type="submit" value="Turn off Public IPs" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="scenerio">
|
||||
<div class="description">
|
||||
<p><strong>Scenario:</strong> Issue related to VPN service affecting multiple customers.</p>
|
||||
<p><strong>Action:</strong> Disable VPN service. This should turn off the VPN ip.</p>
|
||||
</div>
|
||||
|
||||
<form action="{% url syspanel_security_disable_vpn %}" method="post" accept-charset="utf-8">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<input name="block_vpn" type="submit" value="Turn off VPNs" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,52 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block headerjs %}
|
||||
|
||||
<link rel="stylesheet" href="/media/systempanel/css/flexigrid.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script src="/media/systempanel/js/flexigrid.pack.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
|
||||
$(function(){
|
||||
$("table").flexigrid({
|
||||
height: "auto"
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}{{ block.super }}<li><a href="{% url syspanel_volumes %}">Volumes</a></li>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="page_description">
|
||||
<h2 class="page_heading">Manage Volumes</h2>
|
||||
<p>View all volumes. See size, status, and mount point. Destroy volumes.</p>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Volume ID</th>
|
||||
<th>Size</th>
|
||||
<th>Status</th>
|
||||
<th>Device</th>
|
||||
<th>Operations</th>
|
||||
</tr>
|
||||
|
||||
{% for volume in volumes %}
|
||||
<tr class="odd">
|
||||
<td class="odd">{{volume.id}}</td>
|
||||
<td>{{volume.size}}GB</td>
|
||||
<td class="odd">
|
||||
{% if volume.attach_data.status != None %}
|
||||
Attached to Instance: {{volume.instance}}
|
||||
{% else %}
|
||||
{{volume.status_str}}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{volume.attach_data.device}}</td>
|
||||
<td class="odd"><a href="{% url syspanel_delete_volume volume.id %}">Destroy</a></td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
@ -0,0 +1,99 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block headerjs %}
|
||||
|
||||
<link rel="stylesheet" href="/media/systempanel/css/flexigrid.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script src="/media/systempanel/js/flexigrid.pack.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
|
||||
$(function(){
|
||||
$("table").flexigrid({
|
||||
height: "auto"
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block breadcrumb %}{{ block.super }}<li><a href="{% url syspanel_vpns %}">VPNs</a></li>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="page_description">
|
||||
<h2 class="page_heading">Manage VPNs</h2>
|
||||
<p>View all projects VPN status. Schedule launch, termination, and reboot.
|
||||
Send credentials.</p>
|
||||
</div>
|
||||
<div class="message">
|
||||
{% if status == "success" %}
|
||||
<div class="success">{{message}}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if status == "error" %}
|
||||
<div class="error">{{message}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Project</th>
|
||||
<th>Instance Id</th>
|
||||
<th>Created</th>
|
||||
<th>IP</th>
|
||||
<th>Public</th>
|
||||
<th>State</th>
|
||||
<th>Operations</th>
|
||||
</tr>
|
||||
{% for vpn in vpns %}
|
||||
<tr id="{{vpn.instance_id}}" class="even">
|
||||
<td class="odd">{{vpn.project_id}}</td>
|
||||
{% if vpn.instance_id %}
|
||||
<td>{{vpn.instance_id}}</td>
|
||||
{% else %}
|
||||
<td>N/A</td>
|
||||
{% endif %}
|
||||
|
||||
{% if vpn.created_at %}
|
||||
<td class="odd">{{vpn.created_at}}</td>
|
||||
{% else %}
|
||||
<td class="odd">N/A</td>
|
||||
{% endif %}
|
||||
|
||||
{% if vpn.internal_ip %}
|
||||
<td>{{vpn.internal_ip}}</td>
|
||||
{% else %}
|
||||
<td>N/A</td>
|
||||
{% endif %}
|
||||
|
||||
{% if vpn.public_ip %}
|
||||
<td class="odd">{{vpn.public_ip}}:{{vpn.public_port}}</td>
|
||||
{% else %}
|
||||
<td class="odd">N/A</td>
|
||||
{% endif %}
|
||||
|
||||
{% ifnotequal vpn.state "pending" %}
|
||||
{% ifequal vpn.state "running" %}
|
||||
<td>{{vpn.state}}</td>
|
||||
{% else %}
|
||||
<td class="down">{{vpn.state}}</td>
|
||||
{% endifequal %}
|
||||
{% else %}
|
||||
<td> </td>
|
||||
{% endifnotequal %}
|
||||
|
||||
<td>
|
||||
{% if vpn.instance_id %}
|
||||
<a href="{% url syspanel_vpn_terminate vpn.project_id %}">Terminate</a> |
|
||||
<a href="{% url syspanel_vpn_restart vpn.project_id %}">Restart</a> |
|
||||
<a href="{% url syspanel_vpn_console vpn.project_id %}">Console Log</a> |
|
||||
<a href="{% url syspanel_vpn_send_credentials vpn.project_id %}">Resend Credentials</a> |
|
||||
{% else %}
|
||||
<a href="{% url syspanel_vpn_launch vpn.project_id %}">Launch</a> |
|
||||
<a href="{% url syspanel_vpn_send_credentials vpn.project_id %}">Resend Credentials</a> |
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endblock %}
|
35
django-nova-syspanel/src/django_nova_syspanel/urls.py
Normal file
35
django-nova-syspanel/src/django_nova_syspanel/urls.py
Normal file
@ -0,0 +1,35 @@
|
||||
from django.conf.urls.defaults import *
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', 'django_nova_syspanel.views.home.index', name='syspanel_index'),
|
||||
url(r'^instances/$', 'django_nova_syspanel.views.instances.index', name='syspanel_instances'),
|
||||
url(r'^instances/(?P<instance_id>[^/]+)/terminate$', 'django_nova_syspanel.views.instances.terminate', name='syspanel_instance_terminate'),
|
||||
url(r'^instances/(?P<instance_id>[^/]+)/restart$', 'django_nova_syspanel.views.instances.restart', name='syspanel_instance_restart'),
|
||||
url(r'^instances/(?P<instance_id>[^/]+)/console_log$', 'django_nova_syspanel.views.instances.console', name='syspanel_instance_console'),
|
||||
|
||||
url(r'^volumes/$', 'django_nova_syspanel.views.volumes.index', name='syspanel_volumes'),
|
||||
url(r'^volumes/(?P<volume_id>[^/]+)/delete$', 'django_nova_syspanel.views.volumes.delete', name='syspanel_delete_volume'),
|
||||
|
||||
url(r'^security/$', 'django_nova_syspanel.views.security.index', name='syspanel_security'),
|
||||
url(r'^security/disable_project_credentials/$', 'django_nova_syspanel.views.security.disable_project_credentials', name='syspanel_security_disable_project_credentials'),
|
||||
url(r'^security/disable_ip_range/$', 'django_nova_syspanel.views.security.disable_ip', name='syspanel_security_disable_ip_range'),
|
||||
url(r'^security/disable_public_ips/$', 'django_nova_syspanel.views.security.disable_public_ips', name='syspanel_security_disable_public_ips'),
|
||||
url(r'^security/disable_vpn/$', 'django_nova_syspanel.views.security.disable_vpn', name='syspanel_security_disable_vpn'),
|
||||
|
||||
url(r'^vpns/$', 'django_nova_syspanel.views.vpns.index', name='syspanel_vpns'),
|
||||
url(r'^vpns/(?P<project_id>[^/]+)/launch$', 'django_nova_syspanel.views.vpns.launch', name='syspanel_vpn_launch'),
|
||||
url(r'^vpns/(?P<project_id>[^/]+)/send_credentials$', 'django_nova_syspanel.views.vpns.send_credentials', name='syspanel_vpn_send_credentials'),
|
||||
url(r'^vpns/(?P<project_id>[^/]+)/terminate$', 'django_nova_syspanel.views.vpns.terminate', name='syspanel_vpn_terminate'),
|
||||
url(r'^vpns/(?P<project_id>[^/]+)/restart$', 'django_nova_syspanel.views.vpns.restart', name='syspanel_vpn_restart'),
|
||||
url(r'^vpns/(?P<project_id>[^/]+)/console_log$', 'django_nova_syspanel.views.vpns.console', name='syspanel_vpn_console'),
|
||||
|
||||
|
||||
url(r'^cloudview/$', 'django_nova_syspanel.views.cloud.index', name='syspanel_cloudview'),
|
||||
|
||||
|
||||
)
|
||||
|
14
django-nova-syspanel/src/django_nova_syspanel/views/cloud.py
Normal file
14
django-nova-syspanel/src/django_nova_syspanel/views/cloud.py
Normal file
@ -0,0 +1,14 @@
|
||||
from django import template
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render_to_response
|
||||
|
||||
from django_nova_syspanel.models import get_nova_admin_connection
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
nova = get_nova_admin_connection()
|
||||
nodes = nova.get_hosts()
|
||||
return render_to_response('django_nova_syspanel/cloudview/index.html', {
|
||||
'nodes': nodes,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
@ -0,0 +1,8 @@
|
||||
from django import template
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render_to_response
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
return render_to_response('django_nova_syspanel/index.html', {
|
||||
}, context_instance = template.RequestContext(request))
|
@ -0,0 +1,81 @@
|
||||
from django import template
|
||||
from django import http
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render_to_response
|
||||
from django_nova_syspanel.models import *
|
||||
|
||||
def _reservations_to_instances(reservation_list):
|
||||
rv = []
|
||||
for r in reservation_list:
|
||||
for i in r.instances:
|
||||
i2 = r.__dict__.copy()
|
||||
i2.update(i.__dict__)
|
||||
i2['host_name'] = i2['key_name'].split(', ')[1][:-1]
|
||||
rv.append(i2)
|
||||
return rv
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for("admin", "admin")
|
||||
reservations = conn.get_all_instances()
|
||||
instances = _reservations_to_instances(reservations)
|
||||
return render_to_response('django_nova_syspanel/instances/index.html',{
|
||||
'instances': instances,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def terminate(request, instance_id):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for("admin", "admin")
|
||||
reservations = conn.get_all_instances()
|
||||
instances = _reservations_to_instances(reservations)
|
||||
try:
|
||||
conn.terminate_instances([instance_id])
|
||||
message = "Instance %s has been scheduled for termination." % \
|
||||
instance_id
|
||||
status = "success"
|
||||
except:
|
||||
message = "There were issues trying to terminate instance %s. " \
|
||||
"Please try again." % instance_id
|
||||
status = "error"
|
||||
# reload instances, maybe get new state
|
||||
reservations = conn.get_all_instances()
|
||||
instances = _reservations_to_instances(reservations)
|
||||
return render_to_response('django_nova_syspanel/instances/index.html',{
|
||||
'instances': instances,
|
||||
'message': message,
|
||||
'status': status,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def console(request, instance_id):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for("admin", "admin")
|
||||
console = conn.get_console_output(instance_id)
|
||||
response = http.HttpResponse(mimetype='text/plain')
|
||||
response.write(console.output)
|
||||
response.flush()
|
||||
|
||||
return response
|
||||
|
||||
@login_required
|
||||
def restart(request, instance_id):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for("admin", "admin")
|
||||
try:
|
||||
conn.reboot_instances([instance_id])
|
||||
message = "Instance %s has been scheduled for reboot." % \
|
||||
instance_id
|
||||
status = "success"
|
||||
except:
|
||||
message = "There were issues trying to reboot instance %s. " \
|
||||
"Please try again." % instance_id
|
||||
status = "error"
|
||||
reservations = conn.get_all_instances()
|
||||
instances = _reservations_to_instances(reservations)
|
||||
return render_to_response('django_nova_syspanel/instances/index.html',{
|
||||
'instances': instances,
|
||||
'message': message,
|
||||
'status': status,
|
||||
}, context_instance = template.RequestContext(request))
|
111
django-nova-syspanel/src/django_nova_syspanel/views/security.py
Normal file
111
django-nova-syspanel/src/django_nova_syspanel/views/security.py
Normal file
@ -0,0 +1,111 @@
|
||||
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_syspanel.forms import DisableProject
|
||||
from django_nova_syspanel.forms import DisableIpAddress
|
||||
from django_nova_syspanel.models import NovaResponseError, get_nova_admin_connection
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
disable_project_form = DisableProject()
|
||||
disable_ip_form = DisableIpAddress()
|
||||
return render_to_response('django_nova_syspanel/security/index.html',{
|
||||
'project_form':disable_project_form,
|
||||
'ip_form':disable_ip_form,
|
||||
},context_instance = template.RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def disable_project_credentials(request):
|
||||
if request.method == "POST":
|
||||
nova = get_nova_admin_connection()
|
||||
form = DisableProject(request.POST)
|
||||
if form.is_valid():
|
||||
name = form.cleaned_data['project_name']
|
||||
conn = nova.connection_for(nova.access, name)
|
||||
vpn = [x for x in nova.get_vpns() if x.project_id == name]
|
||||
if vpn:
|
||||
# NOTE(todd): Check, because it could already be shut-off
|
||||
vpn = vpn[0]
|
||||
try:
|
||||
nova.disable_project_credentials(name)
|
||||
if vpn and vpn.instance_id:
|
||||
conn.terminate_instances([vpn.instance_id])
|
||||
except NovaResponseError, e:
|
||||
messages.error(request,
|
||||
'Unable to disable project %s: %s - %s' %
|
||||
(name, e.code, e.message))
|
||||
return redirect('syspanel_security')
|
||||
else:
|
||||
messages.success(request,
|
||||
'Project %s has been successfully disabled.' %
|
||||
form.cleaned_data['project_name'])
|
||||
return render_to_response(
|
||||
'django_nova_syspanel/security/disable_project_credentials.html',
|
||||
context_instance = template.RequestContext(request))
|
||||
else:
|
||||
messages.error(request, 'Invalid form data')
|
||||
return redirect('syspanel_security')
|
||||
else:
|
||||
return redirect('syspanel_security')
|
||||
|
||||
@login_required
|
||||
def disable_ip(request):
|
||||
if request.method == "POST":
|
||||
conn = get_nova_admin_connection()
|
||||
form = DisableIpAddress(request.POST)
|
||||
if form.is_valid():
|
||||
try:
|
||||
conn.block_ips(form.cleaned_data['cidr'])
|
||||
except NovaResponseError, e:
|
||||
messages.error(request,
|
||||
'Unable to block IPs range %s: %s %s' %
|
||||
(form.cleaned_data['cidr'], e.code, e.message))
|
||||
else:
|
||||
messages.success(request,
|
||||
'IPs range %shas been successfully blocked' %
|
||||
form.cleaned_data['cidr'])
|
||||
|
||||
return redirect('syspanel_security')
|
||||
|
||||
|
||||
@login_required
|
||||
def disable_public_ips(request):
|
||||
if request.method == "POST":
|
||||
try:
|
||||
nova = get_nova_admin_connection()
|
||||
nova.disable_all_floating_ips()
|
||||
except NovaResponseError, e:
|
||||
messages.error(request,
|
||||
'Unable to shut off public IPs: %s - %s' %
|
||||
(e.code, e.message,))
|
||||
else:
|
||||
messages.success(request, 'Public IPs have been turned off.')
|
||||
return redirect('syspanel_security')
|
||||
|
||||
|
||||
@login_required
|
||||
def disable_vpn(request):
|
||||
if request.method == "POST":
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for("admin", "admin")
|
||||
try:
|
||||
collector = []
|
||||
for vpn in nova.get_vpns():
|
||||
if not vpn.instance_id:
|
||||
continue
|
||||
collector.append(vpn)
|
||||
if len(collector) >= 5:
|
||||
conn.terminate_instances([x.instance_id for x in collector])
|
||||
collector = []
|
||||
if collector:
|
||||
conn.terminate_instances([x.instance_id for x in collector])
|
||||
except NovaResponseError, e:
|
||||
messages.error(request, 'Unable to shut off all VPNs: %s - %s' %
|
||||
(e.code, e.message,))
|
||||
else:
|
||||
messages.success(request, 'VPNs have been successfully turned off.')
|
||||
return redirect('syspanel_security')
|
||||
else:
|
||||
return redirect('syspanel_security')
|
@ -0,0 +1,55 @@
|
||||
from boto.exception import EC2ResponseError
|
||||
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_syspanel.models import *
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for('admin', 'admin')
|
||||
volumes = conn.get_all_volumes()
|
||||
|
||||
for volume in volumes:
|
||||
statusstr = str(volume.status)[:-1]
|
||||
instance = statusstr.split(', ')[-2]
|
||||
device = statusstr.split(', ')[-1]
|
||||
status = statusstr.split(' ')[0]
|
||||
|
||||
volume.device = device
|
||||
volume.instance = instance
|
||||
volume.status_str = status
|
||||
|
||||
return render_to_response('django_nova_syspanel/volumes/index.html',{
|
||||
'volumes': volumes,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def delete(request, volume_id):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for('admin', 'admin')
|
||||
try:
|
||||
conn.delete_volume(volume_id)
|
||||
except EC2ResponseError, e:
|
||||
messages.error(request, 'Unable to delete volume %s: %s' % \
|
||||
(volume_id, e.error_message))
|
||||
else:
|
||||
messages.success(request,
|
||||
'Volume %s has been successfully deleted.' %
|
||||
volume_id)
|
||||
|
||||
volumes = conn.get_all_volumes()
|
||||
for volume in volumes:
|
||||
statusstr = str(volume.status)[:-1]
|
||||
instance = statusstr.split(', ')[-2]
|
||||
device = statusstr.split(', ')[-1]
|
||||
status = statusstr.split(' ')[0]
|
||||
|
||||
volume.device = device
|
||||
volume.instance = instance
|
||||
volume.status_str = status
|
||||
return redirect('syspanel_volumes')
|
||||
|
59
django-nova-syspanel/src/django_nova_syspanel/views/vpns.py
Normal file
59
django-nova-syspanel/src/django_nova_syspanel/views/vpns.py
Normal file
@ -0,0 +1,59 @@
|
||||
from django import http
|
||||
from django import template
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import redirect, render_to_response
|
||||
from django_nova_syspanel.models import get_nova_admin_connection
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
nova = get_nova_admin_connection()
|
||||
vpns = nova.get_vpns()
|
||||
return render_to_response('django_nova_syspanel/vpns/index.html',{
|
||||
'vpns': vpns,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def console(request, project_id):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for(nova.access, project_id)
|
||||
vpn = [x for x in nova.get_vpns() if x.project_id == project_id][0]
|
||||
console = conn.get_console_output(vpn.instance_id)
|
||||
response = http.HttpResponse(mimetype='text/plain')
|
||||
response.write(console.output)
|
||||
response.flush()
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def restart(request, project_id):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for(nova.access, project_id)
|
||||
vpn = [x for x in nova.get_vpns() if x.project_id == project_id][0]
|
||||
conn.reboot_instances([vpn.instance_id])
|
||||
return redirect('django_nova_syspanel/vpns')
|
||||
|
||||
|
||||
@login_required
|
||||
def terminate(request, project_id):
|
||||
nova = get_nova_admin_connection()
|
||||
conn = nova.connection_for(nova.access, project_id)
|
||||
vpn = [x for x in nova.get_vpns() if x.project_id == project_id][0]
|
||||
conn.terminate_instances([vpn.instance_id])
|
||||
return redirect('django_nova_syspanel/vpns')
|
||||
|
||||
|
||||
@login_required
|
||||
def launch(request, project_id):
|
||||
nova = get_nova_admin_connection()
|
||||
nova.start_vpn(project_id)
|
||||
return redirect('django_nova_syspanel/vpns')
|
||||
|
||||
|
||||
@login_required
|
||||
def send_credentials(request, project_id, user_id):
|
||||
# TODO we need to pass a user id as well
|
||||
nova = get_nova_admin_connection()
|
||||
nova.get_zip(user_id, project_id)
|
||||
return redirect('django_nova_syspanel/vpns')
|
@ -65,6 +65,7 @@ INSTALLED_APPS = (
|
||||
'django.contrib.syndication',
|
||||
'django_nose',
|
||||
'django_nova',
|
||||
'django_nova_syspanel',
|
||||
'registration',
|
||||
)
|
||||
|
||||
|
@ -40,6 +40,7 @@ urlpatterns = patterns('',
|
||||
url(r'^admin/project/', include('django_nova.urls.admin_project')),
|
||||
url(r'^admin/roles/', include('django_nova.urls.admin_roles')),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^syspanel/', include('django_nova_syspanel.urls')),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django.views.generic.simple',
|
||||
|
@ -105,6 +105,12 @@ def install_django_nova():
|
||||
run_command([WITH_VENV, 'python', 'setup.py', 'develop'], cwd=path)
|
||||
|
||||
|
||||
def install_django_nova_syspanel():
|
||||
print 'Installing django_nova_syspanel in development mode...'
|
||||
path = os.path.join(ROOT, '..', 'django-nova-syspanel')
|
||||
run_command([WITH_VENV, 'python', 'setup.py', 'develop'], cwd=path)
|
||||
|
||||
|
||||
def print_summary():
|
||||
summary = """
|
||||
OpenStack Dashboard development environment setup is complete.
|
||||
@ -122,6 +128,7 @@ def main():
|
||||
create_virtualenv()
|
||||
install_dependencies()
|
||||
install_django_nova()
|
||||
install_django_nova_syspanel()
|
||||
print_summary()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
x
Reference in New Issue
Block a user