summaryrefslogtreecommitdiffstats
path: root/nova/compute
diff options
context:
space:
mode:
authorTodd Willey <todd@rubidine.com>2010-07-14 20:14:25 -0400
committerTodd Willey <todd@rubidine.com>2010-07-14 20:14:25 -0400
commit5ff20ec381ca6d051d5052efe99c142da602622f (patch)
tree4c5e919502c28f1aa8bb68e4c98a1fc9b949670f /nova/compute
parent70783d594c15f1aafd6b1a6ba28ae6bd25102285 (diff)
Move BasicModel into datastore.
Diffstat (limited to 'nova/compute')
-rw-r--r--nova/compute/model.py213
-rw-r--r--nova/compute/network.py43
2 files changed, 27 insertions, 229 deletions
diff --git a/nova/compute/model.py b/nova/compute/model.py
index 88b94525c..2ea4fecc9 100644
--- a/nova/compute/model.py
+++ b/nova/compute/model.py
@@ -57,19 +57,6 @@ from nova import utils
FLAGS = flags.FLAGS
-class ConnectionError(exception.Error):
- pass
-
-
-def absorb_connection_error(fn):
- def _wrapper(*args, **kwargs):
- try:
- return fn(*args, **kwargs)
- except redis.exceptions.ConnectionError, ce:
- raise ConnectionError(str(ce))
- return _wrapper
-
-
# TODO(todd): Implement this at the class level for Instance
class InstanceDirectory(object):
"""an api for interacting with the global state of instances"""
@@ -81,7 +68,7 @@ class InstanceDirectory(object):
def __getitem__(self, item):
return self.get(item)
- @absorb_connection_error
+ @datastore.absorb_connection_error
def by_project(self, project):
"""returns a list of instance objects for a project"""
for instance_id in datastore.Redis.instance().smembers('project:%s:instances' % project):
@@ -105,12 +92,12 @@ class InstanceDirectory(object):
"""returns the instance a volume is attached to"""
pass
- @absorb_connection_error
+ @datastore.absorb_connection_error
def exists(self, instance_id):
return datastore.Redis.instance().sismember('instances', instance_id)
@property
- @absorb_connection_error
+ @datastore.absorb_connection_error
def all(self):
"""returns a list of all instances"""
for instance_id in datastore.Redis.instance().smembers('instances'):
@@ -121,196 +108,8 @@ class InstanceDirectory(object):
instance_id = utils.generate_uid('i')
return self.get(instance_id)
-class BasicModel(object):
- """
- All Redis-backed data derives from this class.
-
- You MUST specify an identifier() property that returns a unique string
- per instance.
-
- You MUST have an initializer that takes a single argument that is a value
- returned by identifier() to load a new class with.
-
- You may want to specify a dictionary for default_state().
-
- You may also specify override_type at the class left to use a key other
- than __class__.__name__.
-
- You override save and destroy calls to automatically build and destroy
- associations.
- """
-
- override_type = None
-
- @absorb_connection_error
- def __init__(self):
- self.initial_state = {}
- self.state = datastore.Redis.instance().hgetall(self.__redis_key)
- if self.state:
- self.initial_state = self.state
- else:
- self.state = self.default_state()
-
- def default_state(self):
- """You probably want to define this in your subclass"""
- return {}
-
- @classmethod
- def _redis_name(cls):
- return self.override_type or cls.__name__
-
- @classmethod
- def lookup(cls, identifier):
- rv = cls(identifier)
- if rv.is_new_record():
- return None
- else:
- return rv
-
- @classmethod
- @absorb_connection_error
- def all(cls):
- """yields all objects in the store"""
- redis_set = cls._redis_set_name(cls.__name__)
- for identifier in datastore.Redis.instance().smembers(redis_set):
- yield cls(identifier)
-
- @classmethod
- @absorb_connection_error
- def associated_to(cls, foreign_type, foreign_id):
- redis_set = cls._redis_association_name(foreign_type, foreign_id)
- for identifier in datastore.Redis.instance().smembers(redis_set):
- yield cls(identifier)
-
- @classmethod
- def _redis_set_name(cls, kls_name):
- # stupidly pluralize (for compatiblity with previous codebase)
- return kls_name.lower() + "s"
-
- @classmethod
- def _redis_association_name(cls, foreign_type, foreign_id):
- return cls._redis_set_name("%s:%s:%s" %
- (foreign_type, foreign_id, cls.__name__))
-
- @property
- def identifier(self):
- """You DEFINITELY want to define this in your subclass"""
- raise NotImplementedError("Your sublcass should define identifier")
-
- @property
- def __redis_key(self):
- return '%s:%s' % (self.__class__.__name__.lower(), self.identifier)
-
- def __repr__(self):
- return "<%s:%s>" % (self.__class__.__name__, self.identifier)
-
- def keys(self):
- return self.state.keys()
-
- def copy(self):
- copyDict = {}
- for item in self.keys():
- copyDict[item] = self[item]
- return copyDict
-
- def get(self, item, default):
- return self.state.get(item, default)
-
- def update(self, update_dict):
- return self.state.update(update_dict)
-
- def setdefault(self, item, default):
- return self.state.setdefault(item, default)
-
- def __getitem__(self, item):
- return self.state[item]
-
- def __setitem__(self, item, val):
- self.state[item] = val
- return self.state[item]
-
- def __delitem__(self, item):
- """We don't support this"""
- raise Exception("Silly monkey, models NEED all their properties.")
-
- def is_new_record(self):
- return self.initial_state == {}
-
- @absorb_connection_error
- def add_to_index(self):
- set_name = self.__class__._redis_set_name(self.__class__.__name__)
- datastore.Redis.instance().sadd(set_name, self.identifier)
-
- @absorb_connection_error
- def remove_from_index(self):
- set_name = self.__class__._redis_set_name(self.__class__.__name__)
- datastore.Redis.instance().srem(set_name, self.identifier)
-
- @absorb_connection_error
- def remove_from_index(self):
- set_name = self.__class__._redis_set_name(self.__class__.__name__)
- datastore.Redis.instance().srem(set_name, self.identifier)
-
- @absorb_connection_error
- def associate_with(self, foreign_type, foreign_id):
- # note the extra 's' on the end is for plurality
- # to match the old data without requiring a migration of any sort
- self.add_associated_model_to_its_set(foreign_type, foreign_id)
- redis_set = self.__class__._redis_association_name(foreign_type,
- foreign_id)
- datastore.Redis.instance().sadd(redis_set, self.identifier)
-
- @absorb_connection_error
- def unassociate_with(self, foreign_type, foreign_id):
- redis_set = self.__class__._redis_association_name(foreign_type,
- foreign_id)
- datastore.Redis.instance().srem(redis_set, self.identifier)
-
- def add_associated_model_to_its_set(self, my_type, my_id):
- table = globals()
- klsname = my_type.capitalize()
- if table.has_key(klsname):
- my_class = table[klsname]
- my_inst = my_class(my_id)
- my_inst.save()
- else:
- logging.warning("no model class for %s when building"
- " association from %s",
- klsname, self)
-
- @absorb_connection_error
- def save(self):
- """
- update the directory with the state from this model
- also add it to the index of items of the same type
- then set the initial_state = state so new changes are tracked
- """
- # TODO(ja): implement hmset in redis-py and use it
- # instead of multiple calls to hset
- if self.is_new_record():
- self["create_time"] = utils.isotime()
- for key, val in self.state.iteritems():
- # if (not self.initial_state.has_key(key)
- # or self.initial_state[key] != val):
- datastore.Redis.instance().hset(self.__redis_key, key, val)
- self.add_to_index()
- self.initial_state = self.state
- return True
-
- @absorb_connection_error
- def destroy(self):
- """
- deletes all related records from datastore.
- does NOT do anything to running libvirt state.
- """
- logging.info("Destroying datamodel for %s %s",
- self.__class__.__name__, self.identifier)
- datastore.Redis.instance().delete(self.__redis_key)
- self.remove_from_index()
- return True
-
-class Instance(BasicModel):
+class Instance(datastore.BasicModel):
"""Wrapper around stored properties of an instance"""
def __init__(self, instance_id):
@@ -365,7 +164,7 @@ class Instance(BasicModel):
self.unassociate_with("project", self.project)
return super(Instance, self).destroy()
-class Host(BasicModel):
+class Host(datastore.BasicModel):
"""A Host is the machine where a Daemon is running."""
def __init__(self, hostname):
@@ -382,7 +181,7 @@ class Host(BasicModel):
return self.hostname
-class Daemon(BasicModel):
+class Daemon(datastore.BasicModel):
"""A Daemon is a job (compute, api, network, ...) that runs on a host."""
def __init__(self, host_or_combined, binpath=None):
diff --git a/nova/compute/network.py b/nova/compute/network.py
index 81618269d..15635d707 100644
--- a/nova/compute/network.py
+++ b/nova/compute/network.py
@@ -31,11 +31,10 @@ from nova import vendor
import IPy
from nova import datastore
-import nova.exception
-from nova.compute import exception
from nova import flags
-from nova.compute import model
from nova import utils
+from nova import exception
+from nova.compute import exception as compute_exception
from nova.auth import users
import linux_net
@@ -62,7 +61,7 @@ flags.DEFINE_integer('cloudpipe_start_port', 12000,
logging.getLogger().setLevel(logging.DEBUG)
-class Vlan(model.BasicModel):
+class Vlan(datastore.BasicModel):
def __init__(self, project, vlan):
"""
Since we don't want to try and find a vlan by its identifier,
@@ -82,7 +81,7 @@ class Vlan(model.BasicModel):
return instance
@classmethod
- @model.absorb_connection_error
+ @datastore.absorb_connection_error
def lookup(cls, project):
set_name = cls._redis_set_name(cls.__name__)
vlan = datastore.Redis.instance().hget(set_name, project)
@@ -92,14 +91,14 @@ class Vlan(model.BasicModel):
return None
@classmethod
- @model.absorb_connection_error
+ @datastore.absorb_connection_error
def dict_by_project(cls):
"""a hash of project:vlan"""
set_name = cls._redis_set_name(cls.__name__)
return datastore.Redis.instance().hgetall(set_name)
@classmethod
- @model.absorb_connection_error
+ @datastore.absorb_connection_error
def dict_by_vlan(cls):
"""a hash of vlan:project"""
set_name = cls._redis_set_name(cls.__name__)
@@ -110,13 +109,13 @@ class Vlan(model.BasicModel):
return rv
@classmethod
- @model.absorb_connection_error
+ @datastore.absorb_connection_error
def all(cls):
set_name = cls._redis_set_name(cls.__name__)
for project,vlan in datastore.Redis.instance().hgetall(set_name):
yield cls(project, vlan)
- @model.absorb_connection_error
+ @datastore.absorb_connection_error
def save(self):
"""
Vlan saves state into a giant hash named "vlans", with keys of
@@ -126,7 +125,7 @@ class Vlan(model.BasicModel):
set_name = self._redis_set_name(self.__class__.__name__)
datastore.Redis.instance().hset(set_name, self.project_id, self.vlan_id)
- @model.absorb_connection_error
+ @datastore.absorb_connection_error
def destroy(self):
set_name = self._redis_set_name(self.__class__.__name__)
datastore.Redis.instance().hdel(set_name, self.project)
@@ -144,7 +143,7 @@ class Vlan(model.BasicModel):
# TODO(ja): does vlanpool "keeper" need to know the min/max - shouldn't FLAGS always win?
# TODO(joshua): Save the IPs at the top of each subnet for cloudpipe vpn clients
-class BaseNetwork(model.BasicModel):
+class BaseNetwork(datastore.BasicModel):
override_type = 'network'
@property
@@ -241,11 +240,11 @@ class BaseNetwork(model.BasicModel):
self._add_host(user_id, project_id, address, mac)
self.express(address=address)
return address
- raise exception.NoMoreAddresses()
+ raise compute_exception.NoMoreAddresses()
def deallocate_ip(self, ip_str):
if not ip_str in self.assigned:
- raise exception.AddressNotAllocated()
+ raise compute_exception.AddressNotAllocated()
self.deexpress(address=ip_str)
self._rem_host(ip_str)
@@ -351,7 +350,7 @@ class DHCPNetwork(BridgedNetwork):
else:
linux_net.start_dnsmasq(self)
-class PublicAddress(model.BasicModel):
+class PublicAddress(datastore.BasicModel):
override_type = "address"
def __init__(self, address):
@@ -422,14 +421,14 @@ class PublicNetworkController(BaseNetwork):
def associate_address(self, public_ip, private_ip, instance_id):
if not public_ip in self.assigned:
- raise exception.AddressNotAllocated()
+ raise compute_exception.AddressNotAllocated()
# TODO(joshua): Keep an index going both ways
for addr in self.host_objs:
if addr.get('private_ip', None) == private_ip:
- raise exception.AddressAlreadyAssociated()
+ raise compute_exception.AddressAlreadyAssociated()
addr = self.get_host(public_ip)
if addr.get('private_ip', 'available') != 'available':
- raise exception.AddressAlreadyAssociated()
+ raise compute_exception.AddressAlreadyAssociated()
addr['private_ip'] = private_ip
addr['instance_id'] = instance_id
addr.save()
@@ -437,10 +436,10 @@ class PublicNetworkController(BaseNetwork):
def disassociate_address(self, public_ip):
if not public_ip in self.assigned:
- raise exception.AddressNotAllocated()
+ raise compute_exception.AddressNotAllocated()
addr = self.get_host(public_ip)
if addr.get('private_ip', 'available') == 'available':
- raise exception.AddressNotAssociated()
+ raise compute_exception.AddressNotAssociated()
self.deexpress(address=public_ip)
addr['private_ip'] = 'available'
addr['instance_id'] = 'available'
@@ -510,14 +509,14 @@ def get_vlan_for_project(project_id):
return vlan
else:
return Vlan.create(project_id, vnum)
- raise exception.AddressNotAllocated("Out of VLANs")
+ raise compute_exception.AddressNotAllocated("Out of VLANs")
def get_network_by_address(address):
for project in users.UserManager.instance().get_projects():
net = get_project_network(project.id)
if address in net.assigned:
return net
- raise exception.AddressNotAllocated()
+ raise compute_exception.AddressNotAllocated()
def allocate_vpn_ip(user_id, project_id, mac):
return get_project_network(project_id).allocate_vpn_ip(mac)
@@ -534,7 +533,7 @@ def get_project_network(project_id, security_group='default'):
# Refactor to still use the LDAP backend, but not User specific.
project = users.UserManager.instance().get_project(project_id)
if not project:
- raise nova.exception.Error("Project %s doesn't exist, uhoh." %
+ raise exception.Error("Project %s doesn't exist, uhoh." %
project_id)
return DHCPNetwork.get_network_for_project(project.project_manager_id,
project.id, security_group)