CORD-770 Change TenantWithContainer from proxy to real model, rebuild openstack synchronizer when models are added, fix reaper to ignore parent classes
Change-Id: Ic54a9cdd36583dc65b28404c5d50a97f078e8526
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index fd0a51a..18d2c38 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -821,78 +821,39 @@
class TenantWithContainer(Tenant):
- """ A tenant that manages a container """
+ """ A tenant that manages an Instance
+ Note: Should probably be called TenantWithInstance instead of TenantWithContainer
+ """
- class Meta:
- proxy = True
+ instance = models.ForeignKey("Instance", related_name='+', help_text="Instance used by this Tenant", null=True, blank=True)
+ creator = models.ForeignKey("User", related_name='+', help_text="Creator of this Tenant", null=True, blank=True)
+ external_hostname = StrippedCharField(max_length=30, help_text="External host name", null=True, blank=True) # is this still used?
+ external_container = StrippedCharField(max_length=30, help_text="External host name", null=True, blank=True) # is this still used?
def __init__(self, *args, **kwargs):
super(TenantWithContainer, self).__init__(*args, **kwargs)
- self.cached_instance = None
- self.orig_instance_id = self.get_initial_attribute("instance_id")
- @property
- def instance(self):
- from core.models import Instance
- if getattr(self, "cached_instance", None):
- return self.cached_instance
- instance_id = self.get_attribute("instance_id")
- if not instance_id:
- return None
- instances = Instance.objects.filter(id=instance_id)
- if not instances:
- return None
- instance = instances[0]
- instance.caller = self.creator
- self.cached_instance = instance
- return instance
+ # vSG service relies on knowing when instance id has changed
+ self.orig_instance_id = self.get_attribute("instance_id")
- @instance.setter
- def instance(self, value):
- if value:
- value = value.id
- if (value != self.get_attribute("instance_id", None)):
- self.cached_instance = None
- self.set_attribute("instance_id", value)
+ # vSG service relies on instance_id attribute
+ def get_attribute(self, name, default=None):
+ if name=="instance_id":
+ if self.instance:
+ return self.instance.id
+ else:
+ return None
+ else:
+ return super(TenantWithContainer, self).get_attribute(name, default)
- @property
- def external_hostname(self):
- return self.get_attribute("external_hostname", "")
-
- @external_hostname.setter
- def external_hostname(self, value):
- self.set_attribute("external_hostname", value)
-
- @property
- def external_container(self):
- return self.get_attribute("external_container", "")
-
- @external_container.setter
- def external_container(self, value):
- self.set_attribute("external_container", value)
-
- @property
- def creator(self):
- from core.models import User
- if getattr(self, "cached_creator", None):
- return self.cached_creator
- creator_id = self.get_attribute("creator_id")
- if not creator_id:
- return None
- users = User.objects.filter(id=creator_id)
- if not users:
- return None
- user = users[0]
- self.cached_creator = users[0]
- return user
-
- @creator.setter
- def creator(self, value):
- if value:
- value = value.id
- if (value != self.get_attribute("creator_id", None)):
- self.cached_creator = None
- self.set_attribute("creator_id", value)
+ # Services may wish to override the image() function to return different
+ # images based on criteria in the tenant object. For example,
+ # if (self.has_feature_A):
+ # return Instance.object.get(name="image_with_feature_a")
+ # elif (self.has_feature_B):
+ # return Instance.object.get(name="image_with_feature_b")
+ # else:
+ # return super(MyTenantClass,self).image()
@property
def image(self):
diff --git a/xos/synchronizers/base/SyncInstanceUsingAnsible.py b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
index d21cc1f..ebab1c4 100644
--- a/xos/synchronizers/base/SyncInstanceUsingAnsible.py
+++ b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
@@ -148,21 +148,22 @@
fields["private_key"] = key
- # now the ceilometer stuff
+ # Now the ceilometer stuff
+ # Only do this if the instance is not being deleted.
+ if not instance.deleted:
+ cslice = ControllerSlice.objects.get(slice=instance.slice)
+ if not cslice:
+ raise Exception("Controller slice object for %s does not exist" % instance.slice.name)
- cslice = ControllerSlice.objects.get(slice=instance.slice)
- if not cslice:
- raise Exception("Controller slice object for %s does not exist" % instance.slice.name)
+ cuser = ControllerUser.objects.get(user=instance.creator)
+ if not cuser:
+ raise Exception("Controller user object for %s does not exist" % instance.creator)
- cuser = ControllerUser.objects.get(user=instance.creator)
- if not cuser:
- raise Exception("Controller user object for %s does not exist" % instance.creator)
-
- fields.update({"keystone_tenant_id": cslice.tenant_id,
- "keystone_user_id": cuser.kuser_id,
- "rabbit_user": getattr(instance.controller,"rabbit_user", None),
- "rabbit_password": getattr(instance.controller, "rabbit_password", None),
- "rabbit_host": getattr(instance.controller, "rabbit_host", None)})
+ fields.update({"keystone_tenant_id": cslice.tenant_id,
+ "keystone_user_id": cuser.kuser_id,
+ "rabbit_user": getattr(instance.controller,"rabbit_user", None),
+ "rabbit_password": getattr(instance.controller, "rabbit_password", None),
+ "rabbit_host": getattr(instance.controller, "rabbit_host", None)})
return fields
@@ -237,6 +238,10 @@
# the instance is gone. There's nothing left for us to do.
return
+ if instance.deleted:
+ # the instance is being deleted. There's nothing left for us to do.
+ return
+
if isinstance(instance, basestring):
# sync to some external host
@@ -258,14 +263,14 @@
for attribute_name in o.sync_attributes:
fields[attribute_name] = getattr(o, attribute_name)
- fields.update(self.map_delete_inputs(o))
+ if hasattr(self, "map_delete_inputs"):
+ fields.update(self.map_delete_inputs(o))
fields['delete']=True
res = self.run_playbook(o,fields)
- try:
- self.map_delete_outputs(o,res)
- except AttributeError:
- pass
+
+ if hasattr(self, "map_delete_outputs"):
+ self.map_delete_outputs(o,res)
#In order to enable the XOS watcher functionality for a synchronizer, define the 'watches' attribute
#in the derived class: eg. watches = [ModelLink(CoarseTenant,via='coarsetenant')]
diff --git a/xos/synchronizers/model_policy.py b/xos/synchronizers/model_policy.py
index ef5f4b0..4b4ae24 100644
--- a/xos/synchronizers/model_policy.py
+++ b/xos/synchronizers/model_policy.py
@@ -156,7 +156,16 @@
for (k, models) in collector.data.items():
for model in models:
if model==m:
+ # collector will return ourself; ignore it.
continue
+ if issubclass(m.__class__, model.__class__):
+ # collector will return our parent classes; ignore them.
+ continue
+# We don't actually need this check, as with multiple passes the reaper can
+# clean up a hierarchy of objects.
+# if getattr(model, "backend_need_reap", False):
+# # model is already marked for reaping; ignore it.
+# continue
deps.append(model)
return deps
diff --git a/xos/synchronizers/onboarding/steps/sync_servicecontrollerresource.py b/xos/synchronizers/onboarding/steps/sync_servicecontrollerresource.py
index a089bec..238b149 100644
--- a/xos/synchronizers/onboarding/steps/sync_servicecontrollerresource.py
+++ b/xos/synchronizers/onboarding/steps/sync_servicecontrollerresource.py
@@ -28,11 +28,22 @@
logger.info("Sync'ing ServiceControllerResource %s" % scr)
self.download_resource(scr)
+ # TODO: The following should be redone with watchers
+
if scr.loadable_module and scr.loadable_module.xos:
# Make sure the xos UI is resynced
xos = scr.loadable_module.xos
xos.save(update_fields=["updated"], always_update_timestamp=True)
+ if (scr.kind=="models") and scr.loadable_module and (scr.loadable_module.name != "openstack"):
+ # Make sure the openstack controller is restarted. This is necessary
+ # as the OpenStack controller is the only one that handles model
+ # policies.
+ os_scr = ServiceController.objects.filter(name="openstack")
+ if os_scr:
+ os_scr = os_scr[0]
+ os_scr.save(update_fields=["updated"], always_update_timestamp=True)
+
def delete_record(self, m):
pass