summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/nova-manage78
-rw-r--r--doc/.autogenerated147
-rw-r--r--doc/source/api/autoindex.rst6
-rw-r--r--doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst6
-rw-r--r--doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst6
-rw-r--r--doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst6
-rw-r--r--doc/source/api/nova..tests.test_instance_types.rst6
-rw-r--r--doc/source/api/nova..tests.test_test.rst6
-rw-r--r--doc/source/api/nova..tests.test_utils.rst6
-rw-r--r--doc/source/man/novamanage.rst36
-rw-r--r--doc/source/nova.concepts.rst5
-rw-r--r--doc/source/runnova/managing.instance.types.rst84
-rw-r--r--nova/api/ec2/admin.py11
-rw-r--r--nova/api/openstack/__init__.py1
-rw-r--r--nova/api/openstack/flavors.py20
-rw-r--r--nova/compute/api.py6
-rw-r--r--nova/compute/instance_types.py123
-rw-r--r--nova/db/api.py35
-rw-r--r--nova/db/sqlalchemy/api.py92
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py87
-rw-r--r--nova/db/sqlalchemy/models.py16
-rw-r--r--nova/tests/api/openstack/test_flavors.py8
-rw-r--r--nova/tests/db/fakes.py21
-rw-r--r--nova/tests/test_instance_types.py86
-rw-r--r--nova/tests/test_quota.py17
-rw-r--r--nova/tests/test_xenapi.py2
-rw-r--r--nova/virt/libvirt_conn.py6
-rw-r--r--nova/virt/xenapi/vm_utils.py6
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/glance3
29 files changed, 748 insertions, 184 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index 89332f2af..9bf3a1bb3 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -84,6 +84,7 @@ from nova import utils
from nova.api.ec2.cloud import ec2_id_to_id
from nova.auth import manager
from nova.cloudpipe import pipelib
+from nova.compute import instance_types
from nova.db import migration
FLAGS = flags.FLAGS
@@ -661,6 +662,79 @@ class VolumeCommands(object):
"mountpoint": volume['mountpoint']}})
+class InstanceTypeCommands(object):
+ """Class for managing instance types / flavors."""
+
+ def _print_instance_types(self, n, val):
+ deleted = ('', ', inactive')[val["deleted"] == 1]
+ print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, "
+ "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB%s") % (
+ n, val["memory_mb"], val["vcpus"], val["local_gb"],
+ val["flavorid"], val["swap"], val["rxtx_quota"],
+ val["rxtx_cap"], deleted)
+
+ def create(self, name, memory, vcpus, local_gb, flavorid,
+ swap=0, rxtx_quota=0, rxtx_cap=0):
+ """Creates instance types / flavors
+ arguments: name memory vcpus local_gb flavorid [swap] [rxtx_quota]
+ [rxtx_cap]
+ """
+ try:
+ instance_types.create(name, memory, vcpus, local_gb,
+ flavorid, swap, rxtx_quota, rxtx_cap)
+ except exception.InvalidInputException:
+ print "Must supply valid parameters to create instance type"
+ print e
+ sys.exit(1)
+ except exception.DBError, e:
+ print "DB Error: %s" % e
+ sys.exit(2)
+ except:
+ print "Unknown error"
+ sys.exit(3)
+ else:
+ print "%s created" % name
+
+ def delete(self, name, purge=None):
+ """Marks instance types / flavors as deleted
+ arguments: name"""
+ try:
+ if purge == "--purge":
+ instance_types.purge(name)
+ verb = "purged"
+ else:
+ instance_types.destroy(name)
+ verb = "deleted"
+ except exception.ApiError:
+ print "Valid instance type name is required"
+ sys.exit(1)
+ except exception.DBError, e:
+ print "DB Error: %s" % e
+ sys.exit(2)
+ except:
+ sys.exit(3)
+ else:
+ print "%s %s" % (name, verb)
+
+ def list(self, name=None):
+ """Lists all active or specific instance types / flavors
+ arguments: [name]"""
+ try:
+ if name == None:
+ inst_types = instance_types.get_all_types()
+ elif name == "--all":
+ inst_types = instance_types.get_all_types(1)
+ else:
+ inst_types = instance_types.get_instance_type(name)
+ except exception.DBError, e:
+ _db_error(e)
+ if isinstance(inst_types.values()[0], dict):
+ for k, v in inst_types.iteritems():
+ self._print_instance_types(k, v)
+ else:
+ self._print_instance_types(name, inst_types)
+
+
CATEGORIES = [
('user', UserCommands),
('project', ProjectCommands),
@@ -673,7 +747,9 @@ CATEGORIES = [
('service', ServiceCommands),
('log', LogCommands),
('db', DbCommands),
- ('volume', VolumeCommands)]
+ ('volume', VolumeCommands),
+ ('instance_type', InstanceTypeCommands),
+ ('flavor', InstanceTypeCommands)]
def lazy_match(name, key_value_tuples):
diff --git a/doc/.autogenerated b/doc/.autogenerated
index e4c98ec9b..456c8ad1e 100644
--- a/doc/.autogenerated
+++ b/doc/.autogenerated
@@ -40,6 +40,9 @@ source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst
source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst
source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst
source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst
+source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst
+source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst
+source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst
source/api/nova..db.sqlalchemy.migration.rst
source/api/nova..db.sqlalchemy.models.rst
source/api/nova..db.sqlalchemy.session.rst
@@ -98,6 +101,7 @@ source/api/nova..tests.test_compute.rst
source/api/nova..tests.test_console.rst
source/api/nova..tests.test_direct.rst
source/api/nova..tests.test_flags.rst
+source/api/nova..tests.test_instance_types.rst
source/api/nova..tests.test_localization.rst
source/api/nova..tests.test_log.rst
source/api/nova..tests.test_middleware.rst
@@ -107,7 +111,9 @@ source/api/nova..tests.test_quota.rst
source/api/nova..tests.test_rpc.rst
source/api/nova..tests.test_scheduler.rst
source/api/nova..tests.test_service.rst
+source/api/nova..tests.test_test.rst
source/api/nova..tests.test_twistd.rst
+source/api/nova..tests.test_utils.rst
source/api/nova..tests.test_virt.rst
source/api/nova..tests.test_volume.rst
source/api/nova..tests.test_xenapi.rst
@@ -176,6 +182,9 @@ source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst
source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst
source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst
source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst
+source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst
+source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst
+source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst
source/api/nova..db.sqlalchemy.migration.rst
source/api/nova..db.sqlalchemy.models.rst
source/api/nova..db.sqlalchemy.session.rst
@@ -234,6 +243,7 @@ source/api/nova..tests.test_compute.rst
source/api/nova..tests.test_console.rst
source/api/nova..tests.test_direct.rst
source/api/nova..tests.test_flags.rst
+source/api/nova..tests.test_instance_types.rst
source/api/nova..tests.test_localization.rst
source/api/nova..tests.test_log.rst
source/api/nova..tests.test_middleware.rst
@@ -243,142 +253,9 @@ source/api/nova..tests.test_quota.rst
source/api/nova..tests.test_rpc.rst
source/api/nova..tests.test_scheduler.rst
source/api/nova..tests.test_service.rst
+source/api/nova..tests.test_test.rst
source/api/nova..tests.test_twistd.rst
-source/api/nova..tests.test_virt.rst
-source/api/nova..tests.test_volume.rst
-source/api/nova..tests.test_xenapi.rst
-source/api/nova..tests.xenapi.stubs.rst
-source/api/nova..twistd.rst
-source/api/nova..utils.rst
-source/api/nova..version.rst
-source/api/nova..virt.connection.rst
-source/api/nova..virt.disk.rst
-source/api/nova..virt.fake.rst
-source/api/nova..virt.hyperv.rst
-source/api/nova..virt.images.rst
-source/api/nova..virt.libvirt_conn.rst
-source/api/nova..virt.xenapi.fake.rst
-source/api/nova..virt.xenapi.network_utils.rst
-source/api/nova..virt.xenapi.vm_utils.rst
-source/api/nova..virt.xenapi.vmops.rst
-source/api/nova..virt.xenapi.volume_utils.rst
-source/api/nova..virt.xenapi.volumeops.rst
-source/api/nova..virt.xenapi_conn.rst
-source/api/nova..volume.api.rst
-source/api/nova..volume.driver.rst
-source/api/nova..volume.manager.rst
-source/api/nova..volume.san.rst
-source/api/nova..wsgi.rst
-source/api/nova..adminclient.rst
-source/api/nova..api.direct.rst
-source/api/nova..api.ec2.admin.rst
-source/api/nova..api.ec2.apirequest.rst
-source/api/nova..api.ec2.cloud.rst
-source/api/nova..api.ec2.metadatarequesthandler.rst
-source/api/nova..api.openstack.auth.rst
-source/api/nova..api.openstack.backup_schedules.rst
-source/api/nova..api.openstack.common.rst
-source/api/nova..api.openstack.consoles.rst
-source/api/nova..api.openstack.faults.rst
-source/api/nova..api.openstack.flavors.rst
-source/api/nova..api.openstack.images.rst
-source/api/nova..api.openstack.servers.rst
-source/api/nova..api.openstack.shared_ip_groups.rst
-source/api/nova..api.openstack.zones.rst
-source/api/nova..auth.dbdriver.rst
-source/api/nova..auth.fakeldap.rst
-source/api/nova..auth.ldapdriver.rst
-source/api/nova..auth.manager.rst
-source/api/nova..auth.signer.rst
-source/api/nova..cloudpipe.pipelib.rst
-source/api/nova..compute.api.rst
-source/api/nova..compute.instance_types.rst
-source/api/nova..compute.manager.rst
-source/api/nova..compute.monitor.rst
-source/api/nova..compute.power_state.rst
-source/api/nova..console.api.rst
-source/api/nova..console.fake.rst
-source/api/nova..console.manager.rst
-source/api/nova..console.xvp.rst
-source/api/nova..context.rst
-source/api/nova..crypto.rst
-source/api/nova..db.api.rst
-source/api/nova..db.base.rst
-source/api/nova..db.migration.rst
-source/api/nova..db.sqlalchemy.api.rst
-source/api/nova..db.sqlalchemy.migrate_repo.manage.rst
-source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst
-source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst
-source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst
-source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst
-source/api/nova..db.sqlalchemy.migration.rst
-source/api/nova..db.sqlalchemy.models.rst
-source/api/nova..db.sqlalchemy.session.rst
-source/api/nova..exception.rst
-source/api/nova..fakememcache.rst
-source/api/nova..fakerabbit.rst
-source/api/nova..flags.rst
-source/api/nova..image.glance.rst
-source/api/nova..image.local.rst
-source/api/nova..image.s3.rst
-source/api/nova..image.service.rst
-source/api/nova..log.rst
-source/api/nova..manager.rst
-source/api/nova..network.api.rst
-source/api/nova..network.linux_net.rst
-source/api/nova..network.manager.rst
-source/api/nova..objectstore.bucket.rst
-source/api/nova..objectstore.handler.rst
-source/api/nova..objectstore.image.rst
-source/api/nova..objectstore.stored.rst
-source/api/nova..quota.rst
-source/api/nova..rpc.rst
-source/api/nova..scheduler.chance.rst
-source/api/nova..scheduler.driver.rst
-source/api/nova..scheduler.manager.rst
-source/api/nova..scheduler.simple.rst
-source/api/nova..scheduler.zone.rst
-source/api/nova..service.rst
-source/api/nova..test.rst
-source/api/nova..tests.api.openstack.fakes.rst
-source/api/nova..tests.api.openstack.test_adminapi.rst
-source/api/nova..tests.api.openstack.test_api.rst
-source/api/nova..tests.api.openstack.test_auth.rst
-source/api/nova..tests.api.openstack.test_common.rst
-source/api/nova..tests.api.openstack.test_faults.rst
-source/api/nova..tests.api.openstack.test_flavors.rst
-source/api/nova..tests.api.openstack.test_images.rst
-source/api/nova..tests.api.openstack.test_ratelimiting.rst
-source/api/nova..tests.api.openstack.test_servers.rst
-source/api/nova..tests.api.openstack.test_shared_ip_groups.rst
-source/api/nova..tests.api.openstack.test_zones.rst
-source/api/nova..tests.api.test_wsgi.rst
-source/api/nova..tests.db.fakes.rst
-source/api/nova..tests.declare_flags.rst
-source/api/nova..tests.fake_flags.rst
-source/api/nova..tests.glance.stubs.rst
-source/api/nova..tests.hyperv_unittest.rst
-source/api/nova..tests.objectstore_unittest.rst
-source/api/nova..tests.real_flags.rst
-source/api/nova..tests.runtime_flags.rst
-source/api/nova..tests.test_access.rst
-source/api/nova..tests.test_api.rst
-source/api/nova..tests.test_auth.rst
-source/api/nova..tests.test_cloud.rst
-source/api/nova..tests.test_compute.rst
-source/api/nova..tests.test_console.rst
-source/api/nova..tests.test_direct.rst
-source/api/nova..tests.test_flags.rst
-source/api/nova..tests.test_localization.rst
-source/api/nova..tests.test_log.rst
-source/api/nova..tests.test_middleware.rst
-source/api/nova..tests.test_misc.rst
-source/api/nova..tests.test_network.rst
-source/api/nova..tests.test_quota.rst
-source/api/nova..tests.test_rpc.rst
-source/api/nova..tests.test_scheduler.rst
-source/api/nova..tests.test_service.rst
-source/api/nova..tests.test_twistd.rst
+source/api/nova..tests.test_utils.rst
source/api/nova..tests.test_virt.rst
source/api/nova..tests.test_volume.rst
source/api/nova..tests.test_xenapi.rst
diff --git a/doc/source/api/autoindex.rst b/doc/source/api/autoindex.rst
index 41fc1f4a9..329a465db 100644
--- a/doc/source/api/autoindex.rst
+++ b/doc/source/api/autoindex.rst
@@ -43,6 +43,9 @@
nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst
nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst
nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst
+ nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst
+ nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst
+ nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst
nova..db.sqlalchemy.migration.rst
nova..db.sqlalchemy.models.rst
nova..db.sqlalchemy.session.rst
@@ -101,6 +104,7 @@
nova..tests.test_console.rst
nova..tests.test_direct.rst
nova..tests.test_flags.rst
+ nova..tests.test_instance_types.rst
nova..tests.test_localization.rst
nova..tests.test_log.rst
nova..tests.test_middleware.rst
@@ -110,7 +114,9 @@
nova..tests.test_rpc.rst
nova..tests.test_scheduler.rst
nova..tests.test_service.rst
+ nova..tests.test_test.rst
nova..tests.test_twistd.rst
+ nova..tests.test_utils.rst
nova..tests.test_virt.rst
nova..tests.test_volume.rst
nova..tests.test_xenapi.rst
diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst
new file mode 100644
index 000000000..cef0c243e
--- /dev/null
+++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst
@@ -0,0 +1,6 @@
+The :mod:`nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata` Module
+==============================================================================
+.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst
new file mode 100644
index 000000000..a15697196
--- /dev/null
+++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst
@@ -0,0 +1,6 @@
+The :mod:`nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes` Module
+==============================================================================
+.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst
new file mode 100644
index 000000000..38842d1af
--- /dev/null
+++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst
@@ -0,0 +1,6 @@
+The :mod:`nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types` Module
+==============================================================================
+.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/source/api/nova..tests.test_instance_types.rst b/doc/source/api/nova..tests.test_instance_types.rst
new file mode 100644
index 000000000..ebe689966
--- /dev/null
+++ b/doc/source/api/nova..tests.test_instance_types.rst
@@ -0,0 +1,6 @@
+The :mod:`nova..tests.test_instance_types` Module
+==============================================================================
+.. automodule:: nova..tests.test_instance_types
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/source/api/nova..tests.test_test.rst b/doc/source/api/nova..tests.test_test.rst
new file mode 100644
index 000000000..389eb3c99
--- /dev/null
+++ b/doc/source/api/nova..tests.test_test.rst
@@ -0,0 +1,6 @@
+The :mod:`nova..tests.test_test` Module
+==============================================================================
+.. automodule:: nova..tests.test_test
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/source/api/nova..tests.test_utils.rst b/doc/source/api/nova..tests.test_utils.rst
new file mode 100644
index 000000000..d61a7021f
--- /dev/null
+++ b/doc/source/api/nova..tests.test_utils.rst
@@ -0,0 +1,6 @@
+The :mod:`nova..tests.test_utils` Module
+==============================================================================
+.. automodule:: nova..tests.test_utils
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/source/man/novamanage.rst b/doc/source/man/novamanage.rst
index bb9d7a7fe..17ba91bef 100644
--- a/doc/source/man/novamanage.rst
+++ b/doc/source/man/novamanage.rst
@@ -179,6 +179,42 @@ Nova Floating IPs
Displays a list of all floating IP addresses.
+Nova Flavor
+~~~~~~~~~~~
+
+``nova-manage flavor list``
+
+ Outputs a list of all active flavors to the screen.
+
+``nova-manage flavor list --all``
+
+ Outputs a list of all flavors (active and inactive) to the screen.
+
+``nova-manage flavor create <name> <memory> <vCPU> <local_storage> <flavorID> <(optional) swap> <(optional) RXTX Quota> <(optional) RXTX Cap>``
+
+ creates a flavor with the following positional arguments:
+ * memory (expressed in megabytes)
+ * vcpu(s) (integer)
+ * local storage (expressed in gigabytes)
+ * flavorid (unique integer)
+ * swap space (expressed in megabytes, defaults to zero, optional)
+ * RXTX quotas (expressed in gigabytes, defaults to zero, optional)
+ * RXTX cap (expressed in gigabytes, defaults to zero, optional)
+
+``nova-manage flavor delete <name>``
+
+ Delete the flavor with the name <name>. This marks the flavor as inactive and cannot be launched. However, the record stays in the database for archival and billing purposes.
+
+``nova-manage flavor delete <name> --purge``
+
+ Purges the flavor with the name <name>. This removes this flavor from the database.
+
+
+Nova Instance_type
+~~~~~~~~~~~~~~~~~~
+
+The instance_type command is provided as an alias for the flavor command. All the same subcommands and arguments from nova-manage flavor can be used.
+
FILES
========
diff --git a/doc/source/nova.concepts.rst b/doc/source/nova.concepts.rst
index e9687dc98..45cc4b879 100644
--- a/doc/source/nova.concepts.rst
+++ b/doc/source/nova.concepts.rst
@@ -64,6 +64,11 @@ Concept: Instances
An 'instance' is a word for a virtual machine that runs inside the cloud.
+Concept: Instance Type
+----------------------
+
+An 'instance type' describes the compute, memory and storage capacity of nova computing instances. In layman terms, this is the size (in terms of vCPUs, RAM, etc.) of the virtual server that you will be launching.
+
Concept: System Architecture
----------------------------
diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst
new file mode 100644
index 000000000..746077716
--- /dev/null
+++ b/doc/source/runnova/managing.instance.types.rst
@@ -0,0 +1,84 @@
+..
+ Copyright 2011 Ken Pepple
+
+ 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.
+
+Managing Instance Types and Flavors
+===================================
+
+What are Instance Types or Flavors ?
+------------------------------------
+
+Instance types describe the compute, memory and storage capacity of nova computing instances. In layman terms, this is the size (in terms of vCPUs, RAM, etc.) of the virtual server that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "512 MB Server".
+
+In Nova, "flavor" and "instance type" are equivalent terms. When you create an EC2 instance type, you are also creating a OpenStack API flavor. To reduce repetition, for the rest of this document I will refer to these as instance types.
+
+Instance types can be in either the active or inactive state:
+ * Active instance types are available to be used for launching instances
+ * Inactive instance types are not available for launching instances
+
+In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users.
+
+Basic Management
+----------------
+
+Instance types / flavor are managed through the nova-manage binary with
+the "instance_type" command and an appropriate subcommand. Note that you can also use
+the "flavor" command as a synonym for "instance_types".
+
+To see all currently active instance types, use the list subcommand::
+
+ # nova-manage instance_type list
+ m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+
+By default, the list subcommand only shows active instance types. To see all instance types (inactive and active), use the list subcommand with the "--all" flag::
+
+ # nova-manage instance_type list --all
+ m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB
+ m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB, inactive
+
+To create an instance type, use the "create" subcommand with the following positional arguments:
+ * memory (expressed in megabytes)
+ * vcpu(s) (integer)
+ * local storage (expressed in gigabytes)
+ * flavorid (unique integer)
+ * swap space (expressed in megabytes, defaults to zero, optional)
+ * RXTX quotas (expressed in gigabytes, defaults to zero, optional)
+ * RXTX cap (expressed in gigabytes, defaults to zero, optional)
+
+The following example creates an instance type named "m1.xxlarge"::
+
+ # nova-manage instance_type create m1.xxlarge 32768 16 320 0 0 0
+ m1.xxlarge created
+
+To delete an instance type, use the "delete" subcommand and specify the name::
+
+ # nova-manage instance_type delete m1.xxlarge
+ m1.xxlarge deleted
+
+Please note that the "delete" command only marks the instance type as
+inactive in the database; it does not actually remove the instance type. This is done
+to preserve the instance type definition for long running instances (which may not
+terminate for months or years). If you are sure that you want to delete this instance
+type from the database, pass the "--purge" flag after the name::
+
+ # nova-manage instance_type delete m1.xxlarge --purge
+ m1.xxlarge purged
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index e2a05fce1..d9a4ef999 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -29,7 +29,6 @@ from nova import flags
from nova import log as logging
from nova import utils
from nova.auth import manager
-from nova.compute import instance_types
FLAGS = flags.FLAGS
@@ -80,8 +79,8 @@ def host_dict(host, compute_service, instances, volume_service, volumes, now):
return rv
-def instance_dict(name, inst):
- return {'name': name,
+def instance_dict(inst):
+ return {'name': inst['name'],
'memory_mb': inst['memory_mb'],
'vcpus': inst['vcpus'],
'disk_gb': inst['local_gb'],
@@ -115,9 +114,9 @@ class AdminController(object):
def __str__(self):
return 'AdminController'
- def describe_instance_types(self, _context, **_kwargs):
- return {'instanceTypeSet': [instance_dict(n, v) for n, v in
- instance_types.INSTANCE_TYPES.iteritems()]}
+ def describe_instance_types(self, context, **_kwargs):
+ """Returns all active instance types data (vcpus, memory, etc.)"""
+ return {'instanceTypeSet': [db.instance_type_get_all(context)]}
def describe_user(self, _context, name, **_kwargs):
"""Returns user data, including access and secret keys."""
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 2f226d9b5..274330e3b 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -74,6 +74,7 @@ class APIRouter(wsgi.Router):
server_members = {'action': 'POST'}
if FLAGS.allow_admin_api:
LOG.debug(_("Including admin operations in API."))
+
server_members['pause'] = 'POST'
server_members['unpause'] = 'POST'
server_members["diagnostics"] = "GET"
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index f620d4107..f3d040ba3 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -17,6 +17,8 @@
from webob import exc
+from nova import db
+from nova import context
from nova.api.openstack import faults
from nova.api.openstack import common
from nova.compute import instance_types
@@ -39,19 +41,19 @@ class Controller(wsgi.Controller):
def detail(self, req):
"""Return all flavors in detail."""
- items = [self.show(req, id)['flavor'] for id in self._all_ids()]
- items = common.limited(items, req)
+ items = [self.show(req, id)['flavor'] for id in self._all_ids(req)]
return dict(flavors=items)
def show(self, req, id):
"""Return data about the given flavor id."""
- for name, val in instance_types.INSTANCE_TYPES.iteritems():
- if val['flavorid'] == int(id):
- item = dict(ram=val['memory_mb'], disk=val['local_gb'],
- id=val['flavorid'], name=name)
- return dict(flavor=item)
+ ctxt = req.environ['nova.context']
+ values = db.instance_type_get_by_flavor_id(ctxt, id)
+ return dict(flavor=values)
raise faults.Fault(exc.HTTPNotFound())
- def _all_ids(self):
+ def _all_ids(self, req):
"""Return the list of all flavorids."""
- return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()]
+ ctxt = req.environ['nova.context']
+ inst_types = db.instance_type_get_all(ctxt)
+ flavor_ids = [inst_types[i]['flavorid'] for i in inst_types.keys()]
+ return sorted(flavor_ids)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 625778b66..35a7d7bc0 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -88,9 +88,9 @@ class API(base.Base):
availability_zone=None, user_data=None, metadata=[],
onset_files=None):
"""Create the number of instances requested if quota and
- other arguments check out ok.
- """
- type_data = instance_types.INSTANCE_TYPES[instance_type]
+ other arguments check out ok."""
+
+ type_data = instance_types.get_instance_type(instance_type)
num_instances = quota.allowed_instances(context, max_count, type_data)
if num_instances < min_count:
pid = context.project_id
diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py
index 7a2a5baa3..fa02a5dfa 100644
--- a/nova/compute/instance_types.py
+++ b/nova/compute/instance_types.py
@@ -4,6 +4,7 @@
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
# Copyright (c) 2010 Citrix Systems, Inc.
+# Copyright 2011 Ken Pepple
#
# 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
@@ -21,30 +22,120 @@
The built-in instance properties.
"""
-from nova import flags
+from nova import context
+from nova import db
from nova import exception
+from nova import flags
+from nova import log as logging
FLAGS = flags.FLAGS
-INSTANCE_TYPES = {
- 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1),
- 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2),
- 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3),
- 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4),
- 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)}
+LOG = logging.getLogger('nova.instance_types')
+
+
+def create(name, memory, vcpus, local_gb, flavorid, swap=0,
+ rxtx_quota=0, rxtx_cap=0):
+ """Creates instance types / flavors
+ arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap
+ """
+ for option in [memory, vcpus, local_gb, flavorid]:
+ try:
+ int(option)
+ except ValueError:
+ raise exception.InvalidInputException(
+ _("create arguments must be positive integers"))
+ if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0):
+ raise exception.InvalidInputException(
+ _("create arguments must be positive integers"))
+
+ try:
+ db.instance_type_create(
+ context.get_admin_context(),
+ dict(name=name,
+ memory_mb=memory,
+ vcpus=vcpus,
+ local_gb=local_gb,
+ flavorid=flavorid,
+ swap=swap,
+ rxtx_quota=rxtx_quota,
+ rxtx_cap=rxtx_cap))
+ except exception.DBError, e:
+ LOG.exception(_('DB error: %s' % e))
+ raise exception.ApiError(_("Cannot create instance type: %s" % name))
+
+
+def destroy(name):
+ """Marks instance types / flavors as deleted
+ arguments: name"""
+ if name == None:
+ raise exception.InvalidInputException(_("No instance type specified"))
+ else:
+ try:
+ db.instance_type_destroy(context.get_admin_context(), name)
+ except exception.NotFound:
+ LOG.exception(_('Instance type %s not found for deletion' % name))
+ raise exception.ApiError(_("Unknown instance type: %s" % name))
+
+
+def purge(name):
+ """Removes instance types / flavors from database
+ arguments: name"""
+ if name == None:
+ raise exception.InvalidInputException(_("No instance type specified"))
+ else:
+ try:
+ db.instance_type_purge(context.get_admin_context(), name)
+ except exception.NotFound:
+ LOG.exception(_('Instance type %s not found for purge' % name))
+ raise exception.ApiError(_("Unknown instance type: %s" % name))
+
+
+def get_all_types(inactive=0):
+ """Retrieves non-deleted instance_types.
+ Pass true as argument if you want deleted instance types returned also."""
+ return db.instance_type_get_all(context.get_admin_context(), inactive)
+
+
+def get_all_flavors():
+ """retrieves non-deleted flavors. alias for instance_types.get_all_types().
+ Pass true as argument if you want deleted instance types returned also."""
+ return get_all_types(context.get_admin_context())
+
+
+def get_instance_type(name):
+ """Retrieves single instance type by name"""
+ if name is None:
+ return FLAGS.default_instance_type
+ try:
+ ctxt = context.get_admin_context()
+ inst_type = db.instance_type_get_by_name(ctxt, name)
+ return inst_type
+ except exception.DBError:
+ raise exception.ApiError(_("Unknown instance type: %s" % name))
def get_by_type(instance_type):
- """Build instance data structure and save it to the data store."""
+ """retrieve instance type name"""
if instance_type is None:
return FLAGS.default_instance_type
- if instance_type not in INSTANCE_TYPES:
- raise exception.ApiError(_("Unknown instance type: %s") % \
- instance_type, "Invalid")
- return instance_type
+
+ try:
+ ctxt = context.get_admin_context()
+ inst_type = db.instance_type_get_by_name(ctxt, instance_type)
+ return inst_type['name']
+ except exception.DBError, e:
+ LOG.exception(_('DB error: %s' % e))
+ raise exception.ApiError(_("Unknown instance type: %s" %\
+ instance_type))
def get_by_flavor_id(flavor_id):
- for instance_type, details in INSTANCE_TYPES.iteritems():
- if details['flavorid'] == int(flavor_id):
- return instance_type
- return FLAGS.default_instance_type
+ """retrieve instance type's name by flavor_id"""
+ if flavor_id is None:
+ return FLAGS.default_instance_type
+ try:
+ ctxt = context.get_admin_context()
+ flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id)
+ return flavor['name']
+ except exception.DBError, e:
+ LOG.exception(_('DB error: %s' % e))
+ raise exception.ApiError(_("Unknown flavor: %s" % flavor_id))
diff --git a/nova/db/api.py b/nova/db/api.py
index dcaf55e8f..d23f14a3c 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -1007,6 +1007,41 @@ def console_get(context, console_id, instance_id=None):
return IMPL.console_get(context, console_id, instance_id)
+ ##################
+
+
+def instance_type_create(context, values):
+ """Create a new instance type"""
+ return IMPL.instance_type_create(context, values)
+
+
+def instance_type_get_all(context, inactive=0):
+ """Get all instance types"""
+ return IMPL.instance_type_get_all(context, inactive)
+
+
+def instance_type_get_by_name(context, name):
+ """Get instance type by name"""
+ return IMPL.instance_type_get_by_name(context, name)
+
+
+def instance_type_get_by_flavor_id(context, id):
+ """Get instance type by name"""
+ return IMPL.instance_type_get_by_flavor_id(context, id)
+
+
+def instance_type_destroy(context, name):
+ """Delete a instance type"""
+ return IMPL.instance_type_destroy(context, name)
+
+
+def instance_type_purge(context, name):
+ """Purges (removes) an instance type from DB
+ Use instance_type_destroy for most cases
+ """
+ return IMPL.instance_type_purge(context, name)
+
+
####################
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 6df2a8843..919dda118 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -2073,6 +2073,98 @@ def console_get(context, console_id, instance_id=None):
return result
+ ##################
+
+
+@require_admin_context
+def instance_type_create(_context, values):
+ try:
+ instance_type_ref = models.InstanceTypes()
+ instance_type_ref.update(values)
+ instance_type_ref.save()
+ except:
+ raise exception.DBError
+ return instance_type_ref
+
+
+@require_context
+def instance_type_get_all(context, inactive=0):
+ """
+ Returns a dict describing all instance_types with name as key.
+ """
+ session = get_session()
+ if inactive:
+ inst_types = session.query(models.InstanceTypes).\
+ order_by("name").\
+ all()
+ else:
+ inst_types = session.query(models.InstanceTypes).\
+ filter_by(deleted=inactive).\
+ order_by("name").\
+ all()
+ if inst_types:
+ inst_dict = {}
+ for i in inst_types:
+ inst_dict[i['name']] = dict(i)
+ return inst_dict
+ else:
+ raise exception.NotFound
+
+
+@require_context
+def instance_type_get_by_name(context, name):
+ """Returns a dict describing specific instance_type"""
+ session = get_session()
+ inst_type = session.query(models.InstanceTypes).\
+ filter_by(name=name).\
+ first()
+ if not inst_type:
+ raise exception.NotFound(_("No instance type with name %s") % name)
+ else:
+ return dict(inst_type)
+
+
+@require_context
+def instance_type_get_by_flavor_id(context, id):
+ """Returns a dict describing specific flavor_id"""
+ session = get_session()
+ inst_type = session.query(models.InstanceTypes).\
+ filter_by(flavorid=int(id)).\
+ first()
+ if not inst_type:
+ raise exception.NotFound(_("No flavor with name %s") % id)
+ else:
+ return dict(inst_type)
+
+
+@require_admin_context
+def instance_type_destroy(context, name):
+ """ Marks specific instance_type as deleted"""
+ session = get_session()
+ instance_type_ref = session.query(models.InstanceTypes).\
+ filter_by(name=name)
+ records = instance_type_ref.update(dict(deleted=1))
+ if records == 0:
+ raise exception.NotFound
+ else:
+ return instance_type_ref
+
+
+@require_admin_context
+def instance_type_purge(context, name):
+ """ Removes specific instance_type from DB
+ Usually instance_type_destroy should be used
+ """
+ session = get_session()
+ instance_type_ref = session.query(models.InstanceTypes).\
+ filter_by(name=name)
+ records = instance_type_ref.delete()
+ if records == 0:
+ raise exception.NotFound
+ else:
+ return instance_type_ref
+
+
####################
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py
new file mode 100644
index 000000000..66609054e
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py
@@ -0,0 +1,87 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Ken Pepple
+# 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.
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import api
+from nova import db
+from nova import log as logging
+
+import datetime
+
+meta = MetaData()
+
+
+#
+# New Tables
+#
+instance_types = Table('instance_types', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('name',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ unique=True),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('memory_mb', Integer(), nullable=False),
+ Column('vcpus', Integer(), nullable=False),
+ Column('local_gb', Integer(), nullable=False),
+ Column('flavorid', Integer(), nullable=False, unique=True),
+ Column('swap', Integer(), nullable=False, default=0),
+ Column('rxtx_quota', Integer(), nullable=False, default=0),
+ Column('rxtx_cap', Integer(), nullable=False, default=0))
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here
+ # Don't create your own engine; bind migrate_engine
+ # to your metadata
+ meta.bind = migrate_engine
+ try:
+ instance_types.create()
+ except Exception:
+ logging.info(repr(table))
+ logging.exception('Exception while creating instance_types table')
+ raise
+
+ # Here are the old static instance types
+ INSTANCE_TYPES = {
+ 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1),
+ 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2),
+ 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3),
+ 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4),
+ 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)}
+ try:
+ i = instance_types.insert()
+ for name, values in INSTANCE_TYPES.iteritems():
+ # FIXME(kpepple) should we be seeding created_at / updated_at ?
+ # now = datetime.datatime.utcnow()
+ i.execute({'name': name, 'memory_mb': values["memory_mb"],
+ 'vcpus': values["vcpus"], 'deleted': 0,
+ 'local_gb': values["local_gb"],
+ 'flavorid': values["flavorid"]})
+ except Exception:
+ logging.info(repr(table))
+ logging.exception('Exception while seeding instance_types table')
+ raise
+
+
+def downgrade(migrate_engine):
+ # Operations to reverse the above upgrade go here.
+ for table in (instance_types):
+ table.drop()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 9ad4ef577..5e7c399ef 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -215,6 +215,20 @@ class InstanceActions(BASE, NovaBase):
error = Column(Text)
+class InstanceTypes(BASE, NovaBase):
+ """Represent possible instance_types or flavor of VM offered"""
+ __tablename__ = "instance_types"
+ id = Column(Integer, primary_key=True)
+ name = Column(String(255), unique=True)
+ memory_mb = Column(Integer)
+ vcpus = Column(Integer)
+ local_gb = Column(Integer)
+ flavorid = Column(Integer, unique=True)
+ swap = Column(Integer, nullable=False, default=0)
+ rxtx_quota = Column(Integer, nullable=False, default=0)
+ rxtx_cap = Column(Integer, nullable=False, default=0)
+
+
class Volume(BASE, NovaBase):
"""Represents a block storage device that can be attached to a vm."""
__tablename__ = 'volumes'
@@ -579,7 +593,7 @@ def register_models():
connection is lost and needs to be reestablished.
"""
from sqlalchemy import create_engine
- models = (Service, Instance, InstanceActions,
+ models = (Service, Instance, InstanceActions, InstanceTypes,
Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp,
Network, SecurityGroup, SecurityGroupIngressRule,
SecurityGroupInstanceAssociation, AuthToken, User,
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 761265965..319767bb5 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -20,6 +20,8 @@ import webob
from nova import test
import nova.api
+from nova import context
+from nova import db
from nova.api.openstack import flavors
from nova.tests.api.openstack import fakes
@@ -33,6 +35,7 @@ class FlavorsTest(test.TestCase):
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
+ self.context = context.get_admin_context()
def tearDown(self):
self.stubs.UnsetAll()
@@ -41,6 +44,9 @@ class FlavorsTest(test.TestCase):
def test_get_flavor_list(self):
req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
def test_get_flavor_by_id(self):
- pass
+ req = webob.Request.blank('/v1.0/flavors/1')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py
index 05bdd172e..d760dc456 100644
--- a/nova/tests/db/fakes.py
+++ b/nova/tests/db/fakes.py
@@ -20,13 +20,22 @@
import time
from nova import db
+from nova import test
from nova import utils
-from nova.compute import instance_types
def stub_out_db_instance_api(stubs):
""" Stubs out the db API for creating Instances """
+ INSTANCE_TYPES = {
+ 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1),
+ 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2),
+ 'm1.medium':
+ dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3),
+ 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4),
+ 'm1.xlarge':
+ dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)}
+
class FakeModel(object):
""" Stubs out for model """
def __init__(self, values):
@@ -41,10 +50,16 @@ def stub_out_db_instance_api(stubs):
else:
raise NotImplementedError()
+ def fake_instance_type_get_all(context, inactive=0):
+ return INSTANCE_TYPES
+
+ def fake_instance_type_get_by_name(context, name):
+ return INSTANCE_TYPES[name]
+
def fake_instance_create(values):
""" Stubs out the db.instance_create method """
- type_data = instance_types.INSTANCE_TYPES[values['instance_type']]
+ type_data = INSTANCE_TYPES[values['instance_type']]
base_options = {
'name': values['name'],
@@ -73,3 +88,5 @@ def stub_out_db_instance_api(stubs):
stubs.Set(db, 'instance_create', fake_instance_create)
stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance)
+ stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all)
+ stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name)
diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py
new file mode 100644
index 000000000..edc538879
--- /dev/null
+++ b/nova/tests/test_instance_types.py
@@ -0,0 +1,86 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Ken Pepple
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+"""
+Unit Tests for instance types code
+"""
+import time
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.compute import instance_types
+from nova.db.sqlalchemy.session import get_session
+from nova.db.sqlalchemy import models
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.compute')
+
+
+class InstanceTypeTestCase(test.TestCase):
+ """Test cases for instance type code"""
+ def setUp(self):
+ super(InstanceTypeTestCase, self).setUp()
+ session = get_session()
+ max_flavorid = session.query(models.InstanceTypes).\
+ order_by("flavorid desc").\
+ first()
+ self.flavorid = max_flavorid["flavorid"] + 1
+ self.name = str(int(time.time()))
+
+ def test_instance_type_create_then_delete(self):
+ """Ensure instance types can be created"""
+ starting_inst_list = instance_types.get_all_types()
+ instance_types.create(self.name, 256, 1, 120, self.flavorid)
+ new = instance_types.get_all_types()
+ self.assertNotEqual(len(starting_inst_list),
+ len(new),
+ 'instance type was not created')
+ instance_types.destroy(self.name)
+ self.assertEqual(1,
+ instance_types.get_instance_type(self.name)["deleted"])
+ self.assertEqual(starting_inst_list, instance_types.get_all_types())
+ instance_types.purge(self.name)
+ self.assertEqual(len(starting_inst_list),
+ len(instance_types.get_all_types()),
+ 'instance type not purged')
+
+ def test_get_all_instance_types(self):
+ """Ensures that all instance types can be retrieved"""
+ session = get_session()
+ total_instance_types = session.query(models.InstanceTypes).\
+ count()
+ inst_types = instance_types.get_all_types()
+ self.assertEqual(total_instance_types, len(inst_types))
+
+ def test_invalid_create_args_should_fail(self):
+ """Ensures that instance type creation fails with invalid args"""
+ self.assertRaises(
+ exception.InvalidInputException,
+ instance_types.create, self.name, 0, 1, 120, self.flavorid)
+ self.assertRaises(
+ exception.InvalidInputException,
+ instance_types.create, self.name, 256, -1, 120, self.flavorid)
+ self.assertRaises(
+ exception.InvalidInputException,
+ instance_types.create, self.name, 256, 1, "aa", self.flavorid)
+
+ def test_non_existant_inst_type_shouldnt_delete(self):
+ """Ensures that instance type creation fails with invalid args"""
+ self.assertRaises(exception.ApiError,
+ instance_types.destroy, "sfsfsdfdfs")
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index 1e42fddf3..4ecb36b54 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -74,19 +74,30 @@ class QuotaTestCase(test.TestCase):
vol['size'] = size
return db.volume_create(self.context, vol)['id']
+ def _get_instance_type(self, name):
+ instance_types = {
+ 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1),
+ 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2),
+ 'm1.medium':
+ dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3),
+ 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4),
+ 'm1.xlarge':
+ dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)}
+ return instance_types[name]
+
def test_quota_overrides(self):
"""Make sure overriding a projects quotas works"""
num_instances = quota.allowed_instances(self.context, 100,
- instance_types.INSTANCE_TYPES['m1.small'])
+ self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 2)
db.quota_create(self.context, {'project_id': self.project.id,
'instances': 10})
num_instances = quota.allowed_instances(self.context, 100,
- instance_types.INSTANCE_TYPES['m1.small'])
+ self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 4)
db.quota_update(self.context, self.project.id, {'cores': 100})
num_instances = quota.allowed_instances(self.context, 100,
- instance_types.INSTANCE_TYPES['m1.small'])
+ self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 10)
# metadata_items
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index b9bb6d5b4..106c0bd6f 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -233,7 +233,7 @@ class XenAPIVMTestCase(test.TestCase):
vm = vms[0]
# Check that m1.large above turned into the right thing.
- instance_type = instance_types.INSTANCE_TYPES['m1.large']
+ instance_type = db.instance_type_get_by_name(conn, 'm1.large')
mem_kib = long(instance_type['memory_mb']) << 10
mem_bytes = str(mem_kib << 10)
vcpus = instance_type['vcpus']
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 778510812..8a83e657f 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -55,6 +55,7 @@ from nova import db
from nova import exception
from nova import flags
from nova import log as logging
+#from nova import test
from nova import utils
#from nova.api import context
from nova.auth import manager
@@ -606,7 +607,7 @@ class LibvirtConnection(object):
user=user,
project=project,
size=size)
- type_data = instance_types.INSTANCE_TYPES[inst['instance_type']]
+ type_data = instance_types.get_instance_type([inst['instance_type']])
if type_data['local_gb']:
self._cache_image(fn=self._create_local,
@@ -667,7 +668,8 @@ class LibvirtConnection(object):
instance['id'])
# FIXME(vish): stick this in db
instance_type = instance['instance_type']
- instance_type = instance_types.INSTANCE_TYPES[instance_type]
+ # instance_type = test.INSTANCE_TYPES[instance_type]
+ instance_type = instance_types.get_instance_type(instance_type)
ip_address = db.instance_get_fixed_address(context.get_admin_context(),
instance['id'])
# Assume that the gateway also acts as the dhcp server.
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 977c19359..a6de3c9aa 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -86,7 +86,8 @@ class VMHelper(HelperBase):
the pv_kernel flag indicates whether the guest is HVM or PV
"""
- instance_type = instance_types.INSTANCE_TYPES[instance.instance_type]
+ instance_type = instance_types.\
+ get_instance_type(instance.instance_type)
mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
vcpus = str(instance_type['vcpus'])
rec = {
@@ -144,7 +145,8 @@ class VMHelper(HelperBase):
@classmethod
def ensure_free_mem(cls, session, instance):
- instance_type = instance_types.INSTANCE_TYPES[instance.instance_type]
+ instance_type = instance_types.get_instance_type(
+ instance.instance_type)
mem = long(instance_type['memory_mb']) * 1024 * 1024
#get free memory from host
host = session.get_xenapi_host()
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index 7531af4ec..aa12d432a 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -207,8 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port):
'transfer-encoding': 'chunked',
'x-image-meta-is_public': 'True',
'x-image-meta-status': 'queued',
- 'x-image-meta-type': 'vhd'
- }
+ 'x-image-meta-type': 'vhd'}
for header, value in headers.iteritems():
conn.putheader(header, value)
conn.endheaders()