trunk merged. conflict resolved.
This commit is contained in:
@@ -5,13 +5,7 @@ _trial_temp
|
|||||||
keys
|
keys
|
||||||
networks
|
networks
|
||||||
nova.sqlite
|
nova.sqlite
|
||||||
CA/cacert.pem
|
CA
|
||||||
CA/crl.pem
|
|
||||||
CA/index.txt*
|
|
||||||
CA/openssl.cnf
|
|
||||||
CA/serial*
|
|
||||||
CA/newcerts/*.pem
|
|
||||||
CA/private/cakey.pem
|
|
||||||
nova/vcsversion.py
|
nova/vcsversion.py
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
.project
|
.project
|
||||||
|
|||||||
1
.mailmap
1
.mailmap
@@ -4,6 +4,7 @@
|
|||||||
<anotherjesse@gmail.com> <jesse@dancelamb>
|
<anotherjesse@gmail.com> <jesse@dancelamb>
|
||||||
<anotherjesse@gmail.com> <jesse@gigantor.local>
|
<anotherjesse@gmail.com> <jesse@gigantor.local>
|
||||||
<anotherjesse@gmail.com> <jesse@ubuntu>
|
<anotherjesse@gmail.com> <jesse@ubuntu>
|
||||||
|
<anotherjesse@gmail.com> <jesse@aire.local>
|
||||||
<ant@openstack.org> <amesserl@rackspace.com>
|
<ant@openstack.org> <amesserl@rackspace.com>
|
||||||
<Armando.Migliaccio@eu.citrix.com> <armando.migliaccio@citrix.com>
|
<Armando.Migliaccio@eu.citrix.com> <armando.migliaccio@citrix.com>
|
||||||
<brian.lamar@rackspace.com> <brian.lamar@gmail.com>
|
<brian.lamar@rackspace.com> <brian.lamar@gmail.com>
|
||||||
|
|||||||
3
Authors
3
Authors
@@ -31,7 +31,9 @@ Jay Pipes <jaypipes@gmail.com>
|
|||||||
Jesse Andrews <anotherjesse@gmail.com>
|
Jesse Andrews <anotherjesse@gmail.com>
|
||||||
Joe Heck <heckj@mac.com>
|
Joe Heck <heckj@mac.com>
|
||||||
Joel Moore <joelbm24@gmail.com>
|
Joel Moore <joelbm24@gmail.com>
|
||||||
|
Johannes Erdfelt <johannes.erdfelt@rackspace.com>
|
||||||
John Dewey <john@dewey.ws>
|
John Dewey <john@dewey.ws>
|
||||||
|
John Tran <jtran@attinteractive.com>
|
||||||
Jonathan Bryce <jbryce@jbryce.com>
|
Jonathan Bryce <jbryce@jbryce.com>
|
||||||
Jordan Rinke <jordan@openstack.org>
|
Jordan Rinke <jordan@openstack.org>
|
||||||
Josh Durgin <joshd@hq.newdream.net>
|
Josh Durgin <joshd@hq.newdream.net>
|
||||||
@@ -72,5 +74,6 @@ Trey Morris <trey.morris@rackspace.com>
|
|||||||
Tushar Patil <tushar.vitthal.patil@gmail.com>
|
Tushar Patil <tushar.vitthal.patil@gmail.com>
|
||||||
Vasiliy Shlykov <vash@vasiliyshlykov.org>
|
Vasiliy Shlykov <vash@vasiliyshlykov.org>
|
||||||
Vishvananda Ishaya <vishvananda@gmail.com>
|
Vishvananda Ishaya <vishvananda@gmail.com>
|
||||||
|
Yoshiaki Tamura <yoshi@midokura.jp>
|
||||||
Youcef Laribi <Youcef.Laribi@eu.citrix.com>
|
Youcef Laribi <Youcef.Laribi@eu.citrix.com>
|
||||||
Zhixue Wu <Zhixue.Wu@citrix.com>
|
Zhixue Wu <Zhixue.Wu@citrix.com>
|
||||||
|
|||||||
11
CA/.gitignore
vendored
11
CA/.gitignore
vendored
@@ -1,11 +0,0 @@
|
|||||||
index.txt
|
|
||||||
index.txt.old
|
|
||||||
index.txt.attr
|
|
||||||
index.txt.attr.old
|
|
||||||
cacert.pem
|
|
||||||
serial
|
|
||||||
serial.old
|
|
||||||
openssl.cnf
|
|
||||||
private/*
|
|
||||||
newcerts/*
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
# $1 is the id of the project and $2 is the subject of the cert
|
|
||||||
NAME=$1
|
|
||||||
SUBJ=$2
|
|
||||||
mkdir -p projects/$NAME
|
|
||||||
cd projects/$NAME
|
|
||||||
cp ../../openssl.cnf.tmpl openssl.cnf
|
|
||||||
sed -i -e s/%USERNAME%/$NAME/g openssl.cnf
|
|
||||||
mkdir certs crl newcerts private
|
|
||||||
openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
|
|
||||||
echo "10" > serial
|
|
||||||
touch index.txt
|
|
||||||
# NOTE(vish): Disabling intermediate ca's because we don't actually need them.
|
|
||||||
# It makes more sense to have each project have its own root ca.
|
|
||||||
# openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes
|
|
||||||
# openssl req -new -sha256 -key private/cakey.pem -out ../../reqs/inter$NAME.csr -batch -subj "$SUBJ"
|
|
||||||
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
|
|
||||||
if [ "`id -u`" != "`grep nova /etc/passwd | cut -d':' -f3`" ]; then
|
|
||||||
sudo chown -R nova:nogroup .
|
|
||||||
fi
|
|
||||||
# cd ../../
|
|
||||||
# openssl ca -extensions v3_ca -days 365 -out INTER/$NAME/cacert.pem -in reqs/inter$NAME.csr -config openssl.cnf -batch
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
if [ -f "cacert.pem" ];
|
|
||||||
then
|
|
||||||
echo "Not installing, it's already done."
|
|
||||||
else
|
|
||||||
cp openssl.cnf.tmpl openssl.cnf
|
|
||||||
sed -i -e s/%USERNAME%/ROOT/g openssl.cnf
|
|
||||||
openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
|
|
||||||
touch index.txt
|
|
||||||
echo "10" > serial
|
|
||||||
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
|
|
||||||
fi
|
|
||||||
36
CA/genvpn.sh
36
CA/genvpn.sh
@@ -1,36 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
# This gets zipped and run on the cloudpipe-managed OpenVPN server
|
|
||||||
NAME=$1
|
|
||||||
SUBJ=$2
|
|
||||||
|
|
||||||
mkdir -p projects/$NAME
|
|
||||||
cd projects/$NAME
|
|
||||||
|
|
||||||
# generate a server priv key
|
|
||||||
openssl genrsa -out server.key 2048
|
|
||||||
|
|
||||||
# generate a server CSR
|
|
||||||
openssl req -new -key server.key -out server.csr -batch -subj "$SUBJ"
|
|
||||||
|
|
||||||
novauid=`getent passwd nova | awk -F: '{print $3}'`
|
|
||||||
if [ ! -z "${novauid}" ] && [ "`id -u`" != "${novauid}" ]; then
|
|
||||||
sudo chown -R nova:nogroup .
|
|
||||||
fi
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
#
|
|
||||||
# OpenSSL configuration file.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Establish working directory.
|
|
||||||
|
|
||||||
dir = .
|
|
||||||
|
|
||||||
[ ca ]
|
|
||||||
default_ca = CA_default
|
|
||||||
|
|
||||||
[ CA_default ]
|
|
||||||
serial = $dir/serial
|
|
||||||
database = $dir/index.txt
|
|
||||||
new_certs_dir = $dir/newcerts
|
|
||||||
certificate = $dir/cacert.pem
|
|
||||||
private_key = $dir/private/cakey.pem
|
|
||||||
unique_subject = no
|
|
||||||
default_crl_days = 365
|
|
||||||
default_days = 365
|
|
||||||
default_md = md5
|
|
||||||
preserve = no
|
|
||||||
email_in_dn = no
|
|
||||||
nameopt = default_ca
|
|
||||||
certopt = default_ca
|
|
||||||
policy = policy_match
|
|
||||||
|
|
||||||
[ policy_match ]
|
|
||||||
countryName = match
|
|
||||||
stateOrProvinceName = match
|
|
||||||
organizationName = optional
|
|
||||||
organizationalUnitName = optional
|
|
||||||
commonName = supplied
|
|
||||||
emailAddress = optional
|
|
||||||
|
|
||||||
|
|
||||||
[ req ]
|
|
||||||
default_bits = 1024 # Size of keys
|
|
||||||
default_keyfile = key.pem # name of generated keys
|
|
||||||
default_md = md5 # message digest algorithm
|
|
||||||
string_mask = nombstr # permitted characters
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
|
|
||||||
[ req_distinguished_name ]
|
|
||||||
# Variable name Prompt string
|
|
||||||
#---------------------- ----------------------------------
|
|
||||||
0.organizationName = Organization Name (company)
|
|
||||||
organizationalUnitName = Organizational Unit Name (department, division)
|
|
||||||
emailAddress = Email Address
|
|
||||||
emailAddress_max = 40
|
|
||||||
localityName = Locality Name (city, district)
|
|
||||||
stateOrProvinceName = State or Province Name (full name)
|
|
||||||
countryName = Country Name (2 letter code)
|
|
||||||
countryName_min = 2
|
|
||||||
countryName_max = 2
|
|
||||||
commonName = Common Name (hostname, IP, or your name)
|
|
||||||
commonName_max = 64
|
|
||||||
|
|
||||||
# Default values for the above, for consistency and less typing.
|
|
||||||
# Variable name Value
|
|
||||||
#------------------------------ ------------------------------
|
|
||||||
0.organizationName_default = NOVA %USERNAME%
|
|
||||||
localityName_default = Mountain View
|
|
||||||
stateOrProvinceName_default = California
|
|
||||||
countryName_default = US
|
|
||||||
|
|
||||||
[ v3_ca ]
|
|
||||||
basicConstraints = CA:TRUE
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
authorityKeyIdentifier = keyid:always,issuer:always
|
|
||||||
|
|
||||||
[ v3_req ]
|
|
||||||
basicConstraints = CA:FALSE
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
1
CA/projects/.gitignore
vendored
1
CA/projects/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
*
|
|
||||||
1
CA/reqs/.gitignore
vendored
1
CA/reqs/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
*
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
include HACKING LICENSE run_tests.py run_tests.sh
|
include HACKING LICENSE run_tests.py run_tests.sh
|
||||||
include README builddeb.sh exercise_rsapi.py
|
include README builddeb.sh exercise_rsapi.py
|
||||||
include ChangeLog MANIFEST.in pylintrc Authors
|
include ChangeLog MANIFEST.in pylintrc Authors
|
||||||
graft CA
|
graft nova/CA
|
||||||
graft doc
|
graft doc
|
||||||
graft smoketests
|
graft smoketests
|
||||||
graft tools
|
graft tools
|
||||||
|
|||||||
141
bin/nova-manage
141
bin/nova-manage
@@ -570,6 +570,49 @@ class NetworkCommands(object):
|
|||||||
class VmCommands(object):
|
class VmCommands(object):
|
||||||
"""Class for mangaging VM instances."""
|
"""Class for mangaging VM instances."""
|
||||||
|
|
||||||
|
def list(self, host=None):
|
||||||
|
"""Show a list of all instances
|
||||||
|
|
||||||
|
:param host: show all instance on specified host.
|
||||||
|
:param instance: show specificed instance.
|
||||||
|
"""
|
||||||
|
print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
|
||||||
|
" %-10s %-10s %-10s %-5s" % (
|
||||||
|
_('instance'),
|
||||||
|
_('node'),
|
||||||
|
_('type'),
|
||||||
|
_('state'),
|
||||||
|
_('launched'),
|
||||||
|
_('image'),
|
||||||
|
_('kernel'),
|
||||||
|
_('ramdisk'),
|
||||||
|
_('project'),
|
||||||
|
_('user'),
|
||||||
|
_('zone'),
|
||||||
|
_('index'))
|
||||||
|
|
||||||
|
if host == None:
|
||||||
|
instances = db.instance_get_all(context.get_admin_context())
|
||||||
|
else:
|
||||||
|
instances = db.instance_get_all_by_host(
|
||||||
|
context.get_admin_context(), host)
|
||||||
|
|
||||||
|
for instance in instances:
|
||||||
|
print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
|
||||||
|
" %-10s %-10s %-10s %-5d" % (
|
||||||
|
instance['hostname'],
|
||||||
|
instance['host'],
|
||||||
|
instance['instance_type'],
|
||||||
|
instance['state_description'],
|
||||||
|
instance['launched_at'],
|
||||||
|
instance['image_id'],
|
||||||
|
instance['kernel_id'],
|
||||||
|
instance['ramdisk_id'],
|
||||||
|
instance['project_id'],
|
||||||
|
instance['user_id'],
|
||||||
|
instance['availability_zone'],
|
||||||
|
instance['launch_index'])
|
||||||
|
|
||||||
def live_migration(self, ec2_id, dest):
|
def live_migration(self, ec2_id, dest):
|
||||||
"""Migrates a running instance to a new machine.
|
"""Migrates a running instance to a new machine.
|
||||||
|
|
||||||
@@ -701,15 +744,6 @@ class ServiceCommands(object):
|
|||||||
{"method": "update_available_resource"})
|
{"method": "update_available_resource"})
|
||||||
|
|
||||||
|
|
||||||
class LogCommands(object):
|
|
||||||
def request(self, request_id, logfile='/var/log/nova.log'):
|
|
||||||
"""Show all fields in the log for the given request. Assumes you
|
|
||||||
haven't changed the log format too much.
|
|
||||||
ARGS: request_id [logfile]"""
|
|
||||||
lines = utils.execute("cat %s | grep '\[%s '" % (logfile, request_id))
|
|
||||||
print re.sub('#012', "\n", "\n".join(lines))
|
|
||||||
|
|
||||||
|
|
||||||
class DbCommands(object):
|
class DbCommands(object):
|
||||||
"""Class for managing the database."""
|
"""Class for managing the database."""
|
||||||
|
|
||||||
@@ -725,49 +759,6 @@ class DbCommands(object):
|
|||||||
print migration.db_version()
|
print migration.db_version()
|
||||||
|
|
||||||
|
|
||||||
class InstanceCommands(object):
|
|
||||||
"""Class for managing instances."""
|
|
||||||
|
|
||||||
def list(self, host=None, instance=None):
|
|
||||||
"""Show a list of all instances"""
|
|
||||||
print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
|
|
||||||
" %-10s %-10s %-10s %-5s" % (
|
|
||||||
_('instance'),
|
|
||||||
_('node'),
|
|
||||||
_('type'),
|
|
||||||
_('state'),
|
|
||||||
_('launched'),
|
|
||||||
_('image'),
|
|
||||||
_('kernel'),
|
|
||||||
_('ramdisk'),
|
|
||||||
_('project'),
|
|
||||||
_('user'),
|
|
||||||
_('zone'),
|
|
||||||
_('index'))
|
|
||||||
|
|
||||||
if host == None:
|
|
||||||
instances = db.instance_get_all(context.get_admin_context())
|
|
||||||
else:
|
|
||||||
instances = db.instance_get_all_by_host(
|
|
||||||
context.get_admin_context(), host)
|
|
||||||
|
|
||||||
for instance in instances:
|
|
||||||
print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
|
|
||||||
" %-10s %-10s %-10s %-5d" % (
|
|
||||||
instance['hostname'],
|
|
||||||
instance['host'],
|
|
||||||
instance['instance_type'],
|
|
||||||
instance['state_description'],
|
|
||||||
instance['launched_at'],
|
|
||||||
instance['image_id'],
|
|
||||||
instance['kernel_id'],
|
|
||||||
instance['ramdisk_id'],
|
|
||||||
instance['project_id'],
|
|
||||||
instance['user_id'],
|
|
||||||
instance['availability_zone'],
|
|
||||||
instance['launch_index'])
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeCommands(object):
|
class VolumeCommands(object):
|
||||||
"""Methods for dealing with a cloud in an odd state"""
|
"""Methods for dealing with a cloud in an odd state"""
|
||||||
|
|
||||||
@@ -878,7 +869,7 @@ class InstanceTypeCommands(object):
|
|||||||
elif name == "--all":
|
elif name == "--all":
|
||||||
inst_types = instance_types.get_all_types(True)
|
inst_types = instance_types.get_all_types(True)
|
||||||
else:
|
else:
|
||||||
inst_types = instance_types.get_instance_type(name)
|
inst_types = instance_types.get_instance_type_by_name(name)
|
||||||
except exception.DBError, e:
|
except exception.DBError, e:
|
||||||
_db_error(e)
|
_db_error(e)
|
||||||
if isinstance(inst_types.values()[0], dict):
|
if isinstance(inst_types.values()[0], dict):
|
||||||
@@ -894,20 +885,17 @@ class ImageCommands(object):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.image_service = utils.import_object(FLAGS.image_service)
|
self.image_service = utils.import_object(FLAGS.image_service)
|
||||||
|
|
||||||
def _register(self, image_type, disk_format, container_format,
|
def _register(self, container_format, disk_format,
|
||||||
path, owner, name=None, is_public='T',
|
path, owner, name=None, is_public='T',
|
||||||
architecture='x86_64', kernel_id=None, ramdisk_id=None):
|
architecture='x86_64', kernel_id=None, ramdisk_id=None):
|
||||||
meta = {'is_public': True,
|
meta = {'is_public': (is_public == 'T'),
|
||||||
'name': name,
|
'name': name,
|
||||||
'disk_format': disk_format,
|
|
||||||
'container_format': container_format,
|
'container_format': container_format,
|
||||||
|
'disk_format': disk_format,
|
||||||
'properties': {'image_state': 'available',
|
'properties': {'image_state': 'available',
|
||||||
'owner_id': owner,
|
'project_id': owner,
|
||||||
'type': image_type,
|
|
||||||
'architecture': architecture,
|
'architecture': architecture,
|
||||||
'image_location': 'local',
|
'image_location': 'local'}}
|
||||||
'is_public': (is_public == 'T')}}
|
|
||||||
print image_type, meta
|
|
||||||
if kernel_id:
|
if kernel_id:
|
||||||
meta['properties']['kernel_id'] = int(kernel_id)
|
meta['properties']['kernel_id'] = int(kernel_id)
|
||||||
if ramdisk_id:
|
if ramdisk_id:
|
||||||
@@ -932,16 +920,18 @@ class ImageCommands(object):
|
|||||||
ramdisk_id = self.ramdisk_register(ramdisk, owner, None,
|
ramdisk_id = self.ramdisk_register(ramdisk, owner, None,
|
||||||
is_public, architecture)
|
is_public, architecture)
|
||||||
self.image_register(image, owner, name, is_public,
|
self.image_register(image, owner, name, is_public,
|
||||||
architecture, kernel_id, ramdisk_id)
|
architecture, 'ami', 'ami',
|
||||||
|
kernel_id, ramdisk_id)
|
||||||
|
|
||||||
def image_register(self, path, owner, name=None, is_public='T',
|
def image_register(self, path, owner, name=None, is_public='T',
|
||||||
architecture='x86_64', kernel_id=None, ramdisk_id=None,
|
architecture='x86_64', container_format='bare',
|
||||||
disk_format='ami', container_format='ami'):
|
disk_format='raw', kernel_id=None, ramdisk_id=None):
|
||||||
"""Uploads an image into the image_service
|
"""Uploads an image into the image_service
|
||||||
arguments: path owner [name] [is_public='T'] [architecture='x86_64']
|
arguments: path owner [name] [is_public='T'] [architecture='x86_64']
|
||||||
|
[container_format='bare'] [disk_format='raw']
|
||||||
[kernel_id=None] [ramdisk_id=None]
|
[kernel_id=None] [ramdisk_id=None]
|
||||||
[disk_format='ami'] [container_format='ami']"""
|
"""
|
||||||
return self._register('machine', disk_format, container_format, path,
|
return self._register(container_format, disk_format, path,
|
||||||
owner, name, is_public, architecture,
|
owner, name, is_public, architecture,
|
||||||
kernel_id, ramdisk_id)
|
kernel_id, ramdisk_id)
|
||||||
|
|
||||||
@@ -950,7 +940,7 @@ class ImageCommands(object):
|
|||||||
"""Uploads a kernel into the image_service
|
"""Uploads a kernel into the image_service
|
||||||
arguments: path owner [name] [is_public='T'] [architecture='x86_64']
|
arguments: path owner [name] [is_public='T'] [architecture='x86_64']
|
||||||
"""
|
"""
|
||||||
return self._register('kernel', 'aki', 'aki', path, owner, name,
|
return self._register('aki', 'aki', path, owner, name,
|
||||||
is_public, architecture)
|
is_public, architecture)
|
||||||
|
|
||||||
def ramdisk_register(self, path, owner, name=None, is_public='T',
|
def ramdisk_register(self, path, owner, name=None, is_public='T',
|
||||||
@@ -958,7 +948,7 @@ class ImageCommands(object):
|
|||||||
"""Uploads a ramdisk into the image_service
|
"""Uploads a ramdisk into the image_service
|
||||||
arguments: path owner [name] [is_public='T'] [architecture='x86_64']
|
arguments: path owner [name] [is_public='T'] [architecture='x86_64']
|
||||||
"""
|
"""
|
||||||
return self._register('ramdisk', 'ari', 'ari', path, owner, name,
|
return self._register('ari', 'ari', path, owner, name,
|
||||||
is_public, architecture)
|
is_public, architecture)
|
||||||
|
|
||||||
def _lookup(self, old_image_id):
|
def _lookup(self, old_image_id):
|
||||||
@@ -975,16 +965,17 @@ class ImageCommands(object):
|
|||||||
'ramdisk': 'ari'}
|
'ramdisk': 'ari'}
|
||||||
container_format = mapping[old['type']]
|
container_format = mapping[old['type']]
|
||||||
disk_format = container_format
|
disk_format = container_format
|
||||||
|
if container_format == 'ami' and not old.get('kernelId'):
|
||||||
|
container_format = 'bare'
|
||||||
|
disk_format = 'raw'
|
||||||
new = {'disk_format': disk_format,
|
new = {'disk_format': disk_format,
|
||||||
'container_format': container_format,
|
'container_format': container_format,
|
||||||
'is_public': True,
|
'is_public': old['isPublic'],
|
||||||
'name': old['imageId'],
|
'name': old['imageId'],
|
||||||
'properties': {'image_state': old['imageState'],
|
'properties': {'image_state': old['imageState'],
|
||||||
'owner_id': old['imageOwnerId'],
|
'project_id': old['imageOwnerId'],
|
||||||
'architecture': old['architecture'],
|
'architecture': old['architecture'],
|
||||||
'type': old['type'],
|
'image_location': old['imageLocation']}}
|
||||||
'image_location': old['imageLocation'],
|
|
||||||
'is_public': old['isPublic']}}
|
|
||||||
if old.get('kernelId'):
|
if old.get('kernelId'):
|
||||||
new['properties']['kernel_id'] = self._lookup(old['kernelId'])
|
new['properties']['kernel_id'] = self._lookup(old['kernelId'])
|
||||||
if old.get('ramdiskId'):
|
if old.get('ramdiskId'):
|
||||||
@@ -1049,13 +1040,11 @@ CATEGORIES = [
|
|||||||
('network', NetworkCommands),
|
('network', NetworkCommands),
|
||||||
('vm', VmCommands),
|
('vm', VmCommands),
|
||||||
('service', ServiceCommands),
|
('service', ServiceCommands),
|
||||||
('log', LogCommands),
|
|
||||||
('db', DbCommands),
|
('db', DbCommands),
|
||||||
('volume', VolumeCommands),
|
('volume', VolumeCommands),
|
||||||
('instance_type', InstanceTypeCommands),
|
('instance_type', InstanceTypeCommands),
|
||||||
('image', ImageCommands),
|
('image', ImageCommands),
|
||||||
('flavor', InstanceTypeCommands),
|
('flavor', InstanceTypeCommands)]
|
||||||
('instance', InstanceCommands)]
|
|
||||||
|
|
||||||
|
|
||||||
def lazy_match(name, key_value_tuples):
|
def lazy_match(name, key_value_tuples):
|
||||||
|
|||||||
@@ -1,473 +0,0 @@
|
|||||||
# 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'
|
|
||||||
|
|
||||||
|
|
||||||
class UserInfo(object):
|
|
||||||
"""
|
|
||||||
Information about a Nova user, as parsed through SAX.
|
|
||||||
|
|
||||||
**Fields Include**
|
|
||||||
|
|
||||||
* username
|
|
||||||
* accesskey
|
|
||||||
* secretkey
|
|
||||||
* file (optional) containing zip of 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.
|
|
||||||
|
|
||||||
**Fields Include**
|
|
||||||
|
|
||||||
* Hostname
|
|
||||||
* Compute service status
|
|
||||||
* Volume service status
|
|
||||||
* Instance count
|
|
||||||
* Volume count
|
|
||||||
"""
|
|
||||||
|
|
||||||
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):
|
|
||||||
fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name))
|
|
||||||
setattr(self, fixed_name, 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 NovaAdminClient(object):
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
clc_url=DEFAULT_CLC_URL,
|
|
||||||
region=DEFAULT_REGION,
|
|
||||||
access_key=None,
|
|
||||||
secret_key=None,
|
|
||||||
**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."""
|
|
||||||
user = self.apiconn.get_object('DescribeUser',
|
|
||||||
{'Name': name},
|
|
||||||
UserInfo)
|
|
||||||
if user.username != None:
|
|
||||||
return user
|
|
||||||
|
|
||||||
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 modify_project(self, projectname, manager_user=None, description=None):
|
|
||||||
"""Modifies an existing project."""
|
|
||||||
params = {'Name': projectname,
|
|
||||||
'ManagerUser': manager_user,
|
|
||||||
'Description': description}
|
|
||||||
return self.apiconn.get_status('ModifyProject', params)
|
|
||||||
|
|
||||||
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)])
|
|
||||||
@@ -36,11 +36,13 @@ from nova import rpc
|
|||||||
from nova import service
|
from nova import service
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
from nova import exception
|
||||||
from nova.auth import manager
|
from nova.auth import manager
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
from nova.api.ec2 import cloud
|
from nova.api.ec2 import cloud
|
||||||
from nova.api.ec2 import ec2utils
|
from nova.api.ec2 import ec2utils
|
||||||
from nova.image import local
|
from nova.image import local
|
||||||
|
from nova.exception import NotFound
|
||||||
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@@ -71,7 +73,8 @@ class CloudTestCase(test.TestCase):
|
|||||||
host = self.network.get_network_host(self.context.elevated())
|
host = self.network.get_network_host(self.context.elevated())
|
||||||
|
|
||||||
def fake_show(meh, context, id):
|
def fake_show(meh, context, id):
|
||||||
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
|
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
|
||||||
|
'type': 'machine'}}
|
||||||
|
|
||||||
self.stubs.Set(local.LocalImageService, 'show', fake_show)
|
self.stubs.Set(local.LocalImageService, 'show', fake_show)
|
||||||
self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
|
self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
|
||||||
@@ -216,6 +219,66 @@ class CloudTestCase(test.TestCase):
|
|||||||
db.service_destroy(self.context, comp1['id'])
|
db.service_destroy(self.context, comp1['id'])
|
||||||
db.service_destroy(self.context, comp2['id'])
|
db.service_destroy(self.context, comp2['id'])
|
||||||
|
|
||||||
|
def test_describe_images(self):
|
||||||
|
describe_images = self.cloud.describe_images
|
||||||
|
|
||||||
|
def fake_detail(meh, context):
|
||||||
|
return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
|
||||||
|
'type': 'machine'}}]
|
||||||
|
|
||||||
|
def fake_show_none(meh, context, id):
|
||||||
|
raise NotFound
|
||||||
|
|
||||||
|
self.stubs.Set(local.LocalImageService, 'detail', fake_detail)
|
||||||
|
# list all
|
||||||
|
result1 = describe_images(self.context)
|
||||||
|
result1 = result1['imagesSet'][0]
|
||||||
|
self.assertEqual(result1['imageId'], 'ami-00000001')
|
||||||
|
# provided a valid image_id
|
||||||
|
result2 = describe_images(self.context, ['ami-00000001'])
|
||||||
|
self.assertEqual(1, len(result2['imagesSet']))
|
||||||
|
# provide more than 1 valid image_id
|
||||||
|
result3 = describe_images(self.context, ['ami-00000001',
|
||||||
|
'ami-00000002'])
|
||||||
|
self.assertEqual(2, len(result3['imagesSet']))
|
||||||
|
# provide an non-existing image_id
|
||||||
|
self.stubs.UnsetAll()
|
||||||
|
self.stubs.Set(local.LocalImageService, 'show', fake_show_none)
|
||||||
|
self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show_none)
|
||||||
|
self.assertRaises(NotFound, describe_images,
|
||||||
|
self.context, ['ami-fake'])
|
||||||
|
|
||||||
|
def test_describe_image_attribute(self):
|
||||||
|
describe_image_attribute = self.cloud.describe_image_attribute
|
||||||
|
|
||||||
|
def fake_show(meh, context, id):
|
||||||
|
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
|
||||||
|
'type': 'machine'}, 'is_public': True}
|
||||||
|
|
||||||
|
self.stubs.Set(local.LocalImageService, 'show', fake_show)
|
||||||
|
self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
|
||||||
|
result = describe_image_attribute(self.context, 'ami-00000001',
|
||||||
|
'launchPermission')
|
||||||
|
self.assertEqual([{'group': 'all'}], result['launchPermission'])
|
||||||
|
|
||||||
|
def test_modify_image_attribute(self):
|
||||||
|
modify_image_attribute = self.cloud.modify_image_attribute
|
||||||
|
|
||||||
|
def fake_show(meh, context, id):
|
||||||
|
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
|
||||||
|
'type': 'machine'}, 'is_public': False}
|
||||||
|
|
||||||
|
def fake_update(meh, context, image_id, metadata, data=None):
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
self.stubs.Set(local.LocalImageService, 'show', fake_show)
|
||||||
|
self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
|
||||||
|
self.stubs.Set(local.LocalImageService, 'update', fake_update)
|
||||||
|
result = modify_image_attribute(self.context, 'ami-00000001',
|
||||||
|
'launchPermission', 'add',
|
||||||
|
user_group=['all'])
|
||||||
|
self.assertEqual(True, result['is_public'])
|
||||||
|
|
||||||
def test_console_output(self):
|
def test_console_output(self):
|
||||||
instance_type = FLAGS.default_instance_type
|
instance_type = FLAGS.default_instance_type
|
||||||
max_count = 1
|
max_count = 1
|
||||||
@@ -310,6 +373,19 @@ class CloudTestCase(test.TestCase):
|
|||||||
LOG.debug(_("Terminating instance %s"), instance_id)
|
LOG.debug(_("Terminating instance %s"), instance_id)
|
||||||
rv = self.compute.terminate_instance(instance_id)
|
rv = self.compute.terminate_instance(instance_id)
|
||||||
|
|
||||||
|
def test_terminate_instances(self):
|
||||||
|
inst1 = db.instance_create(self.context, {'reservation_id': 'a',
|
||||||
|
'image_id': 1,
|
||||||
|
'host': 'host1'})
|
||||||
|
terminate_instances = self.cloud.terminate_instances
|
||||||
|
# valid instance_id
|
||||||
|
result = terminate_instances(self.context, ['i-00000001'])
|
||||||
|
self.assertTrue(result)
|
||||||
|
# non-existing instance_id
|
||||||
|
self.assertRaises(exception.InstanceNotFound, terminate_instances,
|
||||||
|
self.context, ['i-2'])
|
||||||
|
db.instance_destroy(self.context, inst1['id'])
|
||||||
|
|
||||||
def test_update_of_instance_display_fields(self):
|
def test_update_of_instance_display_fields(self):
|
||||||
inst = db.instance_create(self.context, {})
|
inst = db.instance_create(self.context, {})
|
||||||
ec2_id = ec2utils.id_to_ec2_id(inst['id'])
|
ec2_id = ec2utils.id_to_ec2_id(inst['id'])
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ class ComputeTestCase(test.TestCase):
|
|||||||
inst['launch_time'] = '10'
|
inst['launch_time'] = '10'
|
||||||
inst['user_id'] = self.user.id
|
inst['user_id'] = self.user.id
|
||||||
inst['project_id'] = self.project.id
|
inst['project_id'] = self.project.id
|
||||||
inst['instance_type'] = 'm1.tiny'
|
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||||
|
inst['instance_type_id'] = type_id
|
||||||
inst['mac_address'] = utils.generate_mac()
|
inst['mac_address'] = utils.generate_mac()
|
||||||
inst['ami_launch_index'] = 0
|
inst['ami_launch_index'] = 0
|
||||||
inst.update(params)
|
inst.update(params)
|
||||||
@@ -132,7 +133,7 @@ class ComputeTestCase(test.TestCase):
|
|||||||
cases = [dict(), dict(display_name=None)]
|
cases = [dict(), dict(display_name=None)]
|
||||||
for instance in cases:
|
for instance in cases:
|
||||||
ref = self.compute_api.create(self.context,
|
ref = self.compute_api.create(self.context,
|
||||||
FLAGS.default_instance_type, None, **instance)
|
instance_types.get_default_instance_type(), None, **instance)
|
||||||
try:
|
try:
|
||||||
self.assertNotEqual(ref[0]['display_name'], None)
|
self.assertNotEqual(ref[0]['display_name'], None)
|
||||||
finally:
|
finally:
|
||||||
@@ -143,7 +144,7 @@ class ComputeTestCase(test.TestCase):
|
|||||||
group = self._create_group()
|
group = self._create_group()
|
||||||
ref = self.compute_api.create(
|
ref = self.compute_api.create(
|
||||||
self.context,
|
self.context,
|
||||||
instance_type=FLAGS.default_instance_type,
|
instance_type=instance_types.get_default_instance_type(),
|
||||||
image_id=None,
|
image_id=None,
|
||||||
security_group=['testgroup'])
|
security_group=['testgroup'])
|
||||||
try:
|
try:
|
||||||
@@ -161,7 +162,7 @@ class ComputeTestCase(test.TestCase):
|
|||||||
|
|
||||||
ref = self.compute_api.create(
|
ref = self.compute_api.create(
|
||||||
self.context,
|
self.context,
|
||||||
instance_type=FLAGS.default_instance_type,
|
instance_type=instance_types.get_default_instance_type(),
|
||||||
image_id=None,
|
image_id=None,
|
||||||
security_group=['testgroup'])
|
security_group=['testgroup'])
|
||||||
try:
|
try:
|
||||||
@@ -177,7 +178,7 @@ class ComputeTestCase(test.TestCase):
|
|||||||
|
|
||||||
ref = self.compute_api.create(
|
ref = self.compute_api.create(
|
||||||
self.context,
|
self.context,
|
||||||
instance_type=FLAGS.default_instance_type,
|
instance_type=instance_types.get_default_instance_type(),
|
||||||
image_id=None,
|
image_id=None,
|
||||||
security_group=['testgroup'])
|
security_group=['testgroup'])
|
||||||
|
|
||||||
@@ -359,8 +360,9 @@ class ComputeTestCase(test.TestCase):
|
|||||||
instance_id = self._create_instance()
|
instance_id = self._create_instance()
|
||||||
|
|
||||||
self.compute.run_instance(self.context, instance_id)
|
self.compute.run_instance(self.context, instance_id)
|
||||||
|
inst_type = instance_types.get_instance_type_by_name('m1.xlarge')
|
||||||
db.instance_update(self.context, instance_id,
|
db.instance_update(self.context, instance_id,
|
||||||
{'instance_type': 'm1.xlarge'})
|
{'instance_type_id': inst_type['id']})
|
||||||
|
|
||||||
self.assertRaises(exception.ApiError, self.compute_api.resize,
|
self.assertRaises(exception.ApiError, self.compute_api.resize,
|
||||||
context, instance_id, 1)
|
context, instance_id, 1)
|
||||||
@@ -380,8 +382,8 @@ class ComputeTestCase(test.TestCase):
|
|||||||
self.compute.terminate_instance(context, instance_id)
|
self.compute.terminate_instance(context, instance_id)
|
||||||
|
|
||||||
def test_get_by_flavor_id(self):
|
def test_get_by_flavor_id(self):
|
||||||
type = instance_types.get_by_flavor_id(1)
|
type = instance_types.get_instance_type_by_flavor_id(1)
|
||||||
self.assertEqual(type, 'm1.tiny')
|
self.assertEqual(type['name'], 'm1.tiny')
|
||||||
|
|
||||||
def test_resize_same_source_fails(self):
|
def test_resize_same_source_fails(self):
|
||||||
"""Ensure instance fails to migrate when source and destination are
|
"""Ensure instance fails to migrate when source and destination are
|
||||||
@@ -664,4 +666,5 @@ class ComputeTestCase(test.TestCase):
|
|||||||
|
|
||||||
instances = db.instance_get_all(context.get_admin_context())
|
instances = db.instance_get_all(context.get_admin_context())
|
||||||
LOG.info(_("After force-killing instances: %s"), instances)
|
LOG.info(_("After force-killing instances: %s"), instances)
|
||||||
self.assertEqual(len(instances), 0)
|
self.assertEqual(len(instances), 1)
|
||||||
|
self.assertEqual(power_state.SHUTOFF, instances[0]['state'])
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class ConsoleTestCase(test.TestCase):
|
|||||||
inst['launch_time'] = '10'
|
inst['launch_time'] = '10'
|
||||||
inst['user_id'] = self.user.id
|
inst['user_id'] = self.user.id
|
||||||
inst['project_id'] = self.project.id
|
inst['project_id'] = self.project.id
|
||||||
inst['instance_type'] = 'm1.tiny'
|
inst['instance_type_id'] = 1
|
||||||
inst['mac_address'] = utils.generate_mac()
|
inst['mac_address'] = utils.generate_mac()
|
||||||
inst['ami_launch_index'] = 0
|
inst['ami_launch_index'] = 0
|
||||||
return db.instance_create(self.context, inst)['id']
|
return db.instance_create(self.context, inst)['id']
|
||||||
|
|||||||
@@ -40,7 +40,11 @@ class InstanceTypeTestCase(test.TestCase):
|
|||||||
max_flavorid = session.query(models.InstanceTypes).\
|
max_flavorid = session.query(models.InstanceTypes).\
|
||||||
order_by("flavorid desc").\
|
order_by("flavorid desc").\
|
||||||
first()
|
first()
|
||||||
|
max_id = session.query(models.InstanceTypes).\
|
||||||
|
order_by("id desc").\
|
||||||
|
first()
|
||||||
self.flavorid = max_flavorid["flavorid"] + 1
|
self.flavorid = max_flavorid["flavorid"] + 1
|
||||||
|
self.id = max_id["id"] + 1
|
||||||
self.name = str(int(time.time()))
|
self.name = str(int(time.time()))
|
||||||
|
|
||||||
def test_instance_type_create_then_delete(self):
|
def test_instance_type_create_then_delete(self):
|
||||||
@@ -53,7 +57,7 @@ class InstanceTypeTestCase(test.TestCase):
|
|||||||
'instance type was not created')
|
'instance type was not created')
|
||||||
instance_types.destroy(self.name)
|
instance_types.destroy(self.name)
|
||||||
self.assertEqual(1,
|
self.assertEqual(1,
|
||||||
instance_types.get_instance_type(self.name)["deleted"])
|
instance_types.get_instance_type(self.id)["deleted"])
|
||||||
self.assertEqual(starting_inst_list, instance_types.get_all_types())
|
self.assertEqual(starting_inst_list, instance_types.get_all_types())
|
||||||
instance_types.purge(self.name)
|
instance_types.purge(self.name)
|
||||||
self.assertEqual(len(starting_inst_list),
|
self.assertEqual(len(starting_inst_list),
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ class SimpleDriverTestCase(test.TestCase):
|
|||||||
inst['reservation_id'] = 'r-fakeres'
|
inst['reservation_id'] = 'r-fakeres'
|
||||||
inst['user_id'] = self.user.id
|
inst['user_id'] = self.user.id
|
||||||
inst['project_id'] = self.project.id
|
inst['project_id'] = self.project.id
|
||||||
inst['instance_type'] = 'm1.tiny'
|
inst['instance_type_id'] = '1'
|
||||||
inst['mac_address'] = utils.generate_mac()
|
inst['mac_address'] = utils.generate_mac()
|
||||||
inst['vcpus'] = kwargs.get('vcpus', 1)
|
inst['vcpus'] = kwargs.get('vcpus', 1)
|
||||||
inst['ami_launch_index'] = 0
|
inst['ami_launch_index'] = 0
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
'vcpus': 2,
|
'vcpus': 2,
|
||||||
'project_id': 'fake',
|
'project_id': 'fake',
|
||||||
'bridge': 'br101',
|
'bridge': 'br101',
|
||||||
'instance_type': 'm1.small'}
|
'instance_type_id': '5'} # m1.small
|
||||||
|
|
||||||
def lazy_load_library_exists(self):
|
def lazy_load_library_exists(self):
|
||||||
"""check if libvirt is available."""
|
"""check if libvirt is available."""
|
||||||
@@ -479,7 +479,7 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
|
|
||||||
fake_timer = FakeTime()
|
fake_timer = FakeTime()
|
||||||
|
|
||||||
self.create_fake_libvirt_mock(nwfilterLookupByName=fake_raise)
|
self.create_fake_libvirt_mock()
|
||||||
instance_ref = db.instance_create(self.context, self.test_instance)
|
instance_ref = db.instance_create(self.context, self.test_instance)
|
||||||
|
|
||||||
# Start test
|
# Start test
|
||||||
@@ -488,6 +488,7 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
conn = libvirt_conn.LibvirtConnection(False)
|
conn = libvirt_conn.LibvirtConnection(False)
|
||||||
conn.firewall_driver.setattr('setup_basic_filtering', fake_none)
|
conn.firewall_driver.setattr('setup_basic_filtering', fake_none)
|
||||||
conn.firewall_driver.setattr('prepare_instance_filter', fake_none)
|
conn.firewall_driver.setattr('prepare_instance_filter', fake_none)
|
||||||
|
conn.firewall_driver.setattr('instance_filter_exists', fake_none)
|
||||||
conn.ensure_filtering_rules_for_instance(instance_ref,
|
conn.ensure_filtering_rules_for_instance(instance_ref,
|
||||||
time=fake_timer)
|
time=fake_timer)
|
||||||
except exception.Error, e:
|
except exception.Error, e:
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class VolumeTestCase(test.TestCase):
|
|||||||
inst['launch_time'] = '10'
|
inst['launch_time'] = '10'
|
||||||
inst['user_id'] = 'fake'
|
inst['user_id'] = 'fake'
|
||||||
inst['project_id'] = 'fake'
|
inst['project_id'] = 'fake'
|
||||||
inst['instance_type'] = 'm1.tiny'
|
inst['instance_type_id'] = '2' # m1.tiny
|
||||||
inst['mac_address'] = utils.generate_mac()
|
inst['mac_address'] = utils.generate_mac()
|
||||||
inst['ami_launch_index'] = 0
|
inst['ami_launch_index'] = 0
|
||||||
instance_id = db.instance_create(self.context, inst)['id']
|
instance_id = db.instance_create(self.context, inst)['id']
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
|||||||
'image_id': 1,
|
'image_id': 1,
|
||||||
'kernel_id': 2,
|
'kernel_id': 2,
|
||||||
'ramdisk_id': 3,
|
'ramdisk_id': 3,
|
||||||
'instance_type': 'm1.large',
|
'instance_type_id': '3', # m1.large
|
||||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||||
'os_type': 'linux'}
|
'os_type': 'linux'}
|
||||||
|
|
||||||
@@ -289,11 +289,11 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
'enabled':'1'}],
|
'enabled':'1'}],
|
||||||
'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff',
|
'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff',
|
||||||
'netmask': '120',
|
'netmask': '120',
|
||||||
'enabled': '1',
|
'enabled': '1'}],
|
||||||
'gateway': 'fe80::a00:1'}],
|
|
||||||
'mac': 'aa:bb:cc:dd:ee:ff',
|
'mac': 'aa:bb:cc:dd:ee:ff',
|
||||||
'dns': ['10.0.0.2'],
|
'dns': ['10.0.0.2'],
|
||||||
'gateway': '10.0.0.1'})
|
'gateway': '10.0.0.1',
|
||||||
|
'gateway6': 'fe80::a00:1'})
|
||||||
|
|
||||||
def check_vm_params_for_windows(self):
|
def check_vm_params_for_windows(self):
|
||||||
self.assertEquals(self.vm['platform']['nx'], 'true')
|
self.assertEquals(self.vm['platform']['nx'], 'true')
|
||||||
@@ -328,7 +328,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
self.assertEquals(self.vm['HVM_boot_policy'], '')
|
self.assertEquals(self.vm['HVM_boot_policy'], '')
|
||||||
|
|
||||||
def _test_spawn(self, image_id, kernel_id, ramdisk_id,
|
def _test_spawn(self, image_id, kernel_id, ramdisk_id,
|
||||||
instance_type="m1.large", os_type="linux",
|
instance_type_id="3", os_type="linux",
|
||||||
instance_id=1, check_injection=False):
|
instance_id=1, check_injection=False):
|
||||||
stubs.stubout_loopingcall_start(self.stubs)
|
stubs.stubout_loopingcall_start(self.stubs)
|
||||||
values = {'id': instance_id,
|
values = {'id': instance_id,
|
||||||
@@ -337,7 +337,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
'image_id': image_id,
|
'image_id': image_id,
|
||||||
'kernel_id': kernel_id,
|
'kernel_id': kernel_id,
|
||||||
'ramdisk_id': ramdisk_id,
|
'ramdisk_id': ramdisk_id,
|
||||||
'instance_type': instance_type,
|
'instance_type_id': instance_type_id,
|
||||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||||
'os_type': os_type}
|
'os_type': os_type}
|
||||||
instance = db.instance_create(self.context, values)
|
instance = db.instance_create(self.context, values)
|
||||||
@@ -349,7 +349,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
FLAGS.xenapi_image_service = 'glance'
|
FLAGS.xenapi_image_service = 'glance'
|
||||||
self.assertRaises(Exception,
|
self.assertRaises(Exception,
|
||||||
self._test_spawn,
|
self._test_spawn,
|
||||||
1, 2, 3, "m1.xlarge")
|
1, 2, 3, "4") # m1.xlarge
|
||||||
|
|
||||||
def test_spawn_raw_objectstore(self):
|
def test_spawn_raw_objectstore(self):
|
||||||
FLAGS.xenapi_image_service = 'objectstore'
|
FLAGS.xenapi_image_service = 'objectstore'
|
||||||
@@ -523,7 +523,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
'image_id': 1,
|
'image_id': 1,
|
||||||
'kernel_id': 2,
|
'kernel_id': 2,
|
||||||
'ramdisk_id': 3,
|
'ramdisk_id': 3,
|
||||||
'instance_type': 'm1.large',
|
'instance_type_id': '3', # m1.large
|
||||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||||
'os_type': 'linux'}
|
'os_type': 'linux'}
|
||||||
instance = db.instance_create(self.context, values)
|
instance = db.instance_create(self.context, values)
|
||||||
@@ -580,7 +580,7 @@ class XenAPIMigrateInstance(test.TestCase):
|
|||||||
'kernel_id': None,
|
'kernel_id': None,
|
||||||
'ramdisk_id': None,
|
'ramdisk_id': None,
|
||||||
'local_gb': 5,
|
'local_gb': 5,
|
||||||
'instance_type': 'm1.large',
|
'instance_type_id': '3', # m1.large
|
||||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||||
'os_type': 'linux'}
|
'os_type': 'linux'}
|
||||||
|
|
||||||
|
|||||||
4691
po/pt_BR.po
4691
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
4063
po/zh_CN.po
4063
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user