class Charge(PlCoreBase): KIND_CHOICES = (('besteffort', 'besteffort'), ('reservation', 'reservation'), ('monthlyfee', 'monthlyfee')) STATE_CHOICES = (('pending', 'pending'), ('invoiced', 'invoiced')) account = models.ForeignKey(Account, related_name="charges") slice = models.ForeignKey(Slice, related_name="charges", null=True, blank=True) kind = StrippedCharField(max_length=30, choices=KIND_CHOICES, default="besteffort") state = StrippedCharField(max_length=30, choices=STATE_CHOICES, default="pending") date = models.DateTimeField() object = models.ForeignKey(UsableObject) amount = models.FloatField(default=0.0) coreHours = models.FloatField(default=0.0) invoice = models.ForeignKey(Invoice, blank=True, null=True, related_name="charges") xos_links = [ ModelLink(Account, via='account'), ModelLink(Slice, via='slice'), ModelLink(Invoice, via='invoice') ] def __unicode__(self): return u'%s-%0.2f-%s' % (self.account.site.name, self.amount, str(self.date))
class SiteDeployment(PlCoreBase): objects = ControllerLinkManager() deleted_objects = ControllerLinkDeletionManager() site = models.ForeignKey(Site, related_name='sitedeployments') deployment = models.ForeignKey(Deployment, related_name='sitedeployments') controller = models.ForeignKey(Controller, null=True, blank=True, related_name='sitedeployments') availability_zone = StrippedCharField( max_length=200, null=True, blank=True, help_text="OpenStack availability zone") xos_links = [ ModelLink(Site, 'site'), ModelLink(Deployment, 'deployment'), ModelLink(Controller, 'controller') ] class Meta: unique_together = ('site', 'deployment', 'controller') def __unicode__(self): return u'%s %s' % (self.deployment, self.site)
class SitePrivilege(PlCoreBase): user = models.ForeignKey('User', related_name='siteprivileges') site = models.ForeignKey('Site', related_name='siteprivileges') role = models.ForeignKey('SiteRole', related_name='siteprivileges') xos_links = [ ModelLink(Site, 'site'), ModelLink('User', 'user'), ModelLink('Role', 'role') ] def __unicode__(self): return u'%s %s %s' % (self.site, self.user, self.role) def save(self, *args, **kwds): if not self.user.is_active: raise PermissionDenied, "Cannot modify role(s) of a disabled user" super(SitePrivilege, self).save(*args, **kwds) def delete(self, *args, **kwds): super(SitePrivilege, self).delete(*args, **kwds) def can_update(self, user): return user.can_update_site(self, allow=['pi']) @staticmethod def select_by_user(user): if user.is_admin: qs = SitePrivilege.objects.all() else: sp_ids = [sp.id for sp in SitePrivilege.objects.filter(user=user)] qs = SitePrivilege.objects.filter(id__in=sp_ids) return qs
class DeploymentPrivilege(PlCoreBase): #objects = DeploymentLinkManager() #deleted_objects = DeploymentLinkDeletionManager() user = models.ForeignKey('User', related_name='deploymentprivileges') deployment = models.ForeignKey('Deployment', related_name='deploymentprivileges') role = models.ForeignKey('DeploymentRole', related_name='deploymentprivileges') xos_links = [ ModelLink(Deployment, 'deployment'), ModelLink('User', 'user'), ModelLink('Role', 'role') ] class Meta: unique_together = ('user', 'deployment', 'role') def __unicode__(self): return u'%s %s %s' % (self.deployment, self.user, self.role) def can_update(self, user): return user.can_update_deployment(self) @staticmethod def select_by_user(user): if user.is_admin: qs = DeploymentPrivilege.objects.all() else: dpriv_ids = [ dp.id for dp in DeploymentPrivilege.objects.filter(user=user) ] qs = DeploymentPrivilege.objects.filter(id__in=dpriv_ids) return qs
class ControllerSlicePrivilege(PlCoreBase): objects = ControllerLinkManager() deleted_objects = ControllerLinkDeletionManager() controller = models.ForeignKey('Controller', related_name='controllersliceprivileges') slice_privilege = models.ForeignKey('SlicePrivilege', related_name='controllersliceprivileges') role_id = StrippedCharField(null=True, blank=True, max_length=200, db_index=True, help_text="Keystone id") xos_links = [ModelLink(Controller,via='controller'), ModelLink(SlicePrivilege,via='slice_privilege')] class Meta: unique_together = ('controller', 'slice_privilege') def __unicode__(self): return u'%s %s' % (self.controller, self.slice_privilege) def can_update(self, user): if user.is_readonly: return False if user.is_admin: return True cprivs = ControllerSlicePrivilege.objects.filter(slice_privilege__user=user) for cpriv in dprivs: if cpriv.role.role == ['admin', 'Admin']: return True return False @staticmethod def select_by_user(user): if user.is_admin: qs = ControllerSlicePrivilege.objects.all() else: cpriv_ids = [cp.id for cp in ControllerSlicePrivilege.objects.filter(slice_privilege__user=user)] qs = ControllerSlicePrivilege.objects.filter(id__in=cpriv_ids) return qs
class ControllerUser(PlCoreBase): objects = ControllerLinkManager() deleted_objects = ControllerLinkDeletionManager() user = models.ForeignKey(User,related_name='controllerusers') controller = models.ForeignKey(Controller,related_name='controllersusers') kuser_id = StrippedCharField(null=True, blank=True, max_length=200, help_text="Keystone user id") xos_links = [ModelLink(Controller,via='controller'), ModelLink(User,via='user')] class Meta: unique_together = ('user', 'controller') def __unicode__(self): return u'%s %s' % (self.controller, self.user) @staticmethod def select_by_user(user): if user.is_admin: qs = ControllerUser.objects.all() else: users = User.select_by_user(user) qs = ControllerUser.objects.filter(user__in=users) return qs def can_update(self, user): return user.can_update_root()
class UserDashboardView(PlCoreBase): user = models.ForeignKey(User, related_name='userdashboardviews') dashboardView = models.ForeignKey( DashboardView, related_name='userdashboardviews') order = models.IntegerField(default=0) xos_links = [ModelLink(User, via='user'), ModelLink(DashboardView,via='userdashboardviews')]
class SlicePrivilege(PlCoreBase): user = models.ForeignKey('User', related_name='sliceprivileges') slice = models.ForeignKey('Slice', related_name='sliceprivileges') role = models.ForeignKey('SliceRole', related_name='sliceprivileges') xos_links = [ ModelLink(User, via='user'), ModelLink(Slice, via='slice'), ModelLink(Role, via='role') ] class Meta: unique_together = ('user', 'slice', 'role') def __unicode__(self): return u'%s %s %s' % (self.slice, self.user, self.role) def save(self, *args, **kwds): if not self.user.is_active: raise PermissionDenied, "Cannot modify role(s) of a disabled user" super(SlicePrivilege, self).save(*args, **kwds) def can_update(self, user): return user.can_update_slice(self.slice) @staticmethod def select_by_user(user): if user.is_admin: qs = SlicePrivilege.objects.all() else: # You can see your own SlicePrivileges sp_ids = [sp.id for sp in SlicePrivilege.objects.filter(user=user)] # A site pi or site admin can see the SlicePrivileges for all slices in his Site for priv in SitePrivilege.objects.filter(user=user): if priv.role.role in ['pi', 'admin']: sp_ids.extend([ sp.id for sp in SlicePrivilege.objects.filter( slice__site=priv.site) ]) # A slice admin can see the SlicePrivileges for his Slice for priv in SlicePrivilege.objects.filter(user=user, role__role="admin"): sp_ids.extend([ sp.id for sp in SlicePrivilege.objects.filter(slice=priv.slice) ]) qs = SlicePrivilege.objects.filter(id__in=sp_ids) return qs
class ControllerNetwork(PlCoreBase): objects = ControllerLinkManager() deleted_objects = ControllerLinkDeletionManager() # Stores the openstack ids at various controllers network = models.ForeignKey(Network, related_name='controllernetworks') controller = models.ForeignKey(Controller, related_name='controllernetworks') subnet = models.CharField(max_length=32, blank=True) start_ip = models.CharField(max_length=32, blank=True) stop_ip = models.CharField(max_length=32, blank=True) net_id = models.CharField(null=True, blank=True, max_length=256, help_text="Neutron network") # feedback state router_id = models.CharField( null=True, blank=True, max_length=256, help_text="Neutron router id") # feedback state subnet_id = models.CharField( null=True, blank=True, max_length=256, help_text="Neutron subnet id") # feedback state gateway = models.CharField(max_length=32, blank=True, null=True) # feedback state segmentation_id = models.CharField(max_length=32, blank=True, null=True) # feedback state xos_links = [ ModelLink(Controller, via='controller'), ModelLink(Network, via='network') ] class Meta: unique_together = ('network', 'controller') def tologdict(self): d = super(ControllerNetwork, self).tologdict() try: d['network_name'] = self.network.name d['controller_name'] = self.controller.name except: pass return d @staticmethod def select_by_user(user): if user.is_admin: qs = ControllerNetwork.objects.all() else: slices = Slice.select_by_user(user) networks = Network.objects.filter(owner__in=slices) qs = ControllerNetwork.objects.filter(network__in=networks) return qs
class ControllerSlice(PlCoreBase): objects = ControllerLinkManager() deleted_objects = ControllerLinkDeletionManager() controller = models.ForeignKey(Controller, related_name='controllerslices') slice = models.ForeignKey(Slice, related_name='controllerslices') tenant_id = StrippedCharField(null=True, blank=True, max_length=200, help_text="Keystone tenant id") xos_links = [ ModelLink(Controller, via='controller'), ModelLink(Slice, via='slice') ] class Meta: unique_together = ('controller', 'slice') def tologdict(self): d = super(ControllerSlice, self).tologdict() try: d['slice_name'] = self.slice.name d['controller_name'] = self.controller.name except: pass return d def __unicode__(self): return u'%s %s' % (self.slice, self.controller) @staticmethod def select_by_user(user): if user.is_admin: qs = ControllerSlice.objects.all() else: slices = Slice.select_by_user(user) qs = ControllerSlice.objects.filter(slice__in=slices) return qs def get_cpu_stats(self): filter = 'project_id=%s' % self.tenant_id return monitor.get_meter('cpu', filter, None) def get_bw_stats(self): filter = 'project_id=%s' % self.tenant_id return monitor.get_meter('network.outgoing.bytes', filter, None) def get_node_stats(self): return len(self.slice.instances)
class NetworkSlice(PlCoreBase): # This object exists solely so we can implement the permission check when # adding slices to networks. It adds no additional fields to the relation. network = models.ForeignKey(Network, related_name='networkslices') slice = models.ForeignKey(Slice, related_name='networkslices') xos_links = [ ModelLink(Network, via='network'), ModelLink(Slice, via='slice') ] class Meta: unique_together = ('network', 'slice') def save(self, *args, **kwds): slice = self.slice if (slice not in self.network.permitted_slices.all()) and ( slice != self.network.owner) and (not self.network.permit_all_slices): # to add a instance to the network, then one of the following must be true: # 1) instance's slice is in network's permittedSlices list, # 2) instance's slice is network's owner, or # 3) network's permitAllSlices is true raise ValueError( "Slice %s is not allowed to connect to network %s" % (str(slice), str(self.network))) super(NetworkSlice, self).save(*args, **kwds) def __unicode__(self): return u'%s-%s' % (self.network.name, self.slice.name) def can_update(self, user): return user.can_update_slice(self.slice) @staticmethod def select_by_user(user): if user.is_admin: qs = NetworkSlice.objects.all() else: slice_ids = [s.id for s in Slice.select_by_user(user)] network_ids = [ network.id for network in Network.select_by_user(user) ] qs = NetworkSlice.objects.filter( Q(slice__in=slice_ids) | Q(network__in=network_ids)) return qs
class Account(PlCoreBase): site = models.ForeignKey(Site, related_name="accounts", help_text="Site for this account") xos_links = [ModelLink(Site, via='site')] @property def total_invoices(self): # Since the amount of an invoice is the sum of it's charges, we can # compute the sum of the invoices by summing all charges where # charge.invoice != Null. x = self.charges.filter(invoice__isnull=False).aggregate( Sum('amount'))["amount__sum"] if (x == None): return 0.0 return x @property def total_payments(self): x = self.payments.all().aggregate(Sum('amount'))["amount__sum"] if (x == None): return 0.0 return x @property def balance_due(self): return self.total_invoices - self.total_payments def __unicode__(self): return u'%s' % (self.site.name)
class Payment(PlCoreBase): account = models.ForeignKey(Account, related_name="payments") amount = models.FloatField(default=0.0) date = models.DateTimeField(default=timezone.now) xos_links = [ModelLink(Account, via='account')] def __unicode__(self): return u'%s-%0.2f-%s' % (self.account.site.name, self.amount, str(self.date))
class Invoice(PlCoreBase): date = models.DateTimeField() account = models.ForeignKey(Account, related_name="invoices") xos_links = [ModelLink(Account, via='account')] @property def amount(self): return str(self.charges.all().aggregate(Sum('amount'))["amount__sum"]) def __unicode__(self): return u'%s-%s' % (self.account.site.name, str(self.date))
class ControllerSite(PlCoreBase): site = models.ForeignKey(Site, related_name='controllersite') controller = models.ForeignKey(Controller, null=True, blank=True, related_name='controllersite') tenant_id = StrippedCharField(null=True, blank=True, max_length=200, db_index=True, help_text="Keystone tenant id") xos_links = [ ModelLink(Controller, via='controller'), ModelLink(Site, via='site') ] def delete(self, *args, **kwds): super(ControllerSite, self).delete(*args, **kwds) class Meta: unique_together = ('site', 'controller')
class SyncExampleTenant(SyncInstanceUsingAnsible): provides = [ExampleTenant] observes = ExampleTenant requested_interval = 0 template_name = "exampletenant_playbook.yaml" service_key_name = "/opt/xos/synchronizers/exampleservice/exampleservice_private_key" watches = [ModelLink(CoarseTenant,via='coarsetenant')] def __init__(self, *args, **kwargs): super(SyncExampleTenant, self).__init__(*args, **kwargs) def fetch_pending(self, deleted): if (not deleted): objs = ExampleTenant.get_tenant_objects().filter( Q(enacted__lt=F('updated')) | Q(enacted=None), Q(lazy_blocked=False)) else: # If this is a deletion we get all of the deleted tenants.. objs = ExampleTenant.get_deleted_tenant_objects() return objs def get_exampleservice(self, o): if not o.provider_service: return None exampleservice = ExampleService.get_service_objects().filter(id=o.provider_service.id) if not exampleservice: return None return exampleservice[0] # Gets the attributes that are used by the Ansible template but are not # part of the set of default attributes. def get_extra_attributes(self, o): fields = {} fields['tenant_message'] = o.tenant_message fields['image_name'] = o.image_name return fields
class UserCredential(PlCoreBase): user = models.ForeignKey( User, related_name='usercredentials', help_text="The User this credential is associated with") name = models.SlugField(help_text="The credential type, e.g. ec2", max_length=128) key_id = StrippedCharField(help_text="The backend id of this credential", max_length=1024) enc_value = EncryptedCharField( help_text="The key value of this credential", max_length=1024) xos_links = [ModelLink(User, via='user')] def __unicode__(self): return self.name
class ControllerCredential(PlCoreBase): objects = ControllerLinkManager() deleted_objects = ControllerLinkDeletionManager() controller = models.ForeignKey( Controller, related_name='controllercredentials', help_text="The User this credential is associated with") name = models.SlugField(help_text="The credential type, e.g. ec2", max_length=128) key_id = models.CharField(help_text="The backend id of this credential", max_length=1024) enc_value = EncryptedCharField( help_text="The key value of this credential", max_length=1024) xos_links = [ModelLink(Controller, via='controller')] def __unicode__(self): return self.name
class SliceTag(PlCoreBase): slice = models.ForeignKey(Slice, related_name='slicetags') NAME_CHOICES = (('privatekey', 'Private Key'), ('publickey', 'Public Key')) name = StrippedCharField(help_text="The name of this tag", max_length=30, choices=NAME_CHOICES) value = StrippedCharField(help_text="The value of this tag", max_length=1024) xos_links = [ModelLink(Slice, via='slice')] def can_update(self, user): return user.can_update_slice(self.slice) @staticmethod def select_by_user(user): if user.is_admin: qs = SliceTag.objects.all() else: slices = Slice.select_by_user(user) qs = SliceTag.objects.filter(slice__in=slices) return qs
class Node(PlCoreBase): name = StrippedCharField(max_length=200, unique=True, help_text="Name of the Node") site_deployment = models.ForeignKey(SiteDeployment, related_name='nodes') site = models.ForeignKey(Site, null=True, blank=True, related_name='nodes') tags = GenericRelation(Tag) xos_links = [ModelLink(Site, 'site')] def __unicode__(self): return u'%s' % (self.name) def __init__(self, *args, **kwargs): super(Node, self).__init__(*args, **kwargs) self.no_sync = True def save(self, *args, **kwds): if self.site is None and self.site_deployment is not None: self.site = self.site_deployment.site super(Node, self).save(*args, **kwds) def can_update(self, user): return user.can_update_site(self.site, allow=['tech'])
class SyncONOSNetcfg(SyncStep): provides = [VTNService] observes = VTNService watches = [ ModelLink(Node, via='node'), ModelLink(AddressPool, via='addresspool') ] requested_interval = 0 def __init__(self, **args): SyncStep.__init__(self, **args) def handle_watched_object(self, o): logger.info("handle_watched_object is invoked for object %s" % (str(o)), extra=o.tologdict()) if (type(o) is Node): # For Node add/delete/modify self.call() if (type(o) is AddressPool): # For public gateways self.call() def get_node_tag(self, node, tagname): tags = Tag.select_by_content_object(node).filter(name=tagname) return tags[0].value def get_tenants_who_want_config(self): tenants = [] # attribute is comma-separated list for ta in TenantAttribute.objects.filter(name="autogenerate"): if ta.value: for config in ta.value.split(','): if config == "vtn-network-cfg": tenants.append(ta.tenant) return tenants def save_tenant_attribute(self, tenant, name, value): tas = TenantAttribute.objects.filter(tenant=tenant, name=name) if tas: ta = tas[0] if ta.value != value: logger.info("updating %s with attribute" % name) ta.value = value ta.save() else: logger.info("saving autogenerated config %s" % name) ta = TenantAttribute(tenant=tenant, name=name, value=value) ta.save() # This function currently assumes a single Deployment and Site def get_onos_netcfg(self, vtn): privateGatewayMac = vtn.privateGatewayMac localManagementIp = vtn.localManagementIp ovsdbPort = vtn.ovsdbPort sshPort = vtn.sshPort sshUser = vtn.sshUser sshKeyFile = vtn.sshKeyFile mgmtSubnetBits = vtn.mgmtSubnetBits xosEndpoint = vtn.xosEndpoint xosUser = vtn.xosUser xosPassword = vtn.xosPassword controllerPort = vtn.controllerPort if ":" in controllerPort: (c_hostname, c_port) = controllerPort.split(":", 1) controllerPort = socket.gethostbyname(c_hostname) + ":" + c_port else: controllerPort = ":" + controllerPort data = { "apps": { "org.opencord.vtn": { "cordvtn": { "privateGatewayMac": privateGatewayMac, "localManagementIp": localManagementIp, "ovsdbPort": ovsdbPort, "ssh": { "sshPort": sshPort, "sshUser": sshUser, "sshKeyFile": sshKeyFile }, "xos": { "endpoint": xosEndpoint, "user": xosUser, "password": xosPassword }, "publicGateways": [], "nodes": [], "controllers": [controllerPort] } } } } # Generate apps->org.opencord.vtn->cordvtn->openstack controllers = Controller.objects.all() if controllers: controller = controllers[0] keystone_server = controller.auth_url user_name = controller.admin_user tenant_name = controller.admin_tenant password = controller.admin_password openstack = { "endpoint": keystone_server, "tenant": tenant_name, "user": user_name, "password": password } data["apps"]["org.opencord.vtn"]["cordvtn"][ "openstack"] = openstack # Generate apps->org.opencord.vtn->cordvtn->nodes nodes = Node.objects.all() for node in nodes: nodeip = socket.gethostbyname(node.name) try: bridgeId = self.get_node_tag(node, "bridgeId") dataPlaneIntf = self.get_node_tag(node, "dataPlaneIntf") dataPlaneIp = self.get_node_tag(node, "dataPlaneIp") except: logger.error("not adding node %s to the VTN configuration" % node.name) continue node_dict = { "hostname": node.name, "hostManagementIp": "%s/%s" % (nodeip, mgmtSubnetBits), "bridgeId": bridgeId, "dataPlaneIntf": dataPlaneIntf, "dataPlaneIp": dataPlaneIp } # this one is optional try: node_dict["hostManagementIface"] = self.get_node_tag( node, "hostManagementIface") except IndexError: pass data["apps"]["org.opencord.vtn"]["cordvtn"]["nodes"].append( node_dict) # Generate apps->org.onosproject.cordvtn->cordvtn->publicGateways # Pull the gateway information from vRouter try: from services.vrouter.models import VRouterService vrouters = VRouterService.get_service_objects().all() if vrouters: for gateway in vrouters[0].get_gateways(): gatewayIp = gateway['gateway_ip'].split('/', 1)[0] gatewayMac = gateway['gateway_mac'] gateway_dict = { "gatewayIp": gatewayIp, "gatewayMac": gatewayMac } data["apps"]["org.opencord.vtn"]["cordvtn"][ "publicGateways"].append(gateway_dict) except: logger.info( "No VRouter service present, not adding publicGateways to config" ) return json.dumps(data, indent=4, sort_keys=True) def call(self, **args): vtn_service = VTNService.get_service_objects().all() if not vtn_service: raise Exception("No VTN Service") vtn_service = vtn_service[0] # Check for autogenerate attribute netcfg = self.get_onos_netcfg(vtn_service) tenants = self.get_tenants_who_want_config() for tenant in tenants: self.save_tenant_attribute(tenant, "rest_onos/v1/network/configuration/", netcfg)
class Instance(PlCoreBase): ISOLATION_CHOICES = (('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM')) objects = InstanceManager() deleted_objects = InstanceDeletionManager() instance_id = StrippedCharField(null=True, blank=True, max_length=200, help_text="Nova instance id") instance_uuid = StrippedCharField(null=True, blank=True, max_length=200, help_text="Nova instance uuid") name = StrippedCharField(max_length=200, help_text="Instance name") instance_name = StrippedCharField(blank=True, null=True, max_length=200, help_text="OpenStack generated name") ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True) image = models.ForeignKey(Image, related_name='instances') creator = models.ForeignKey(User, related_name='instances', blank=True, null=True) slice = models.ForeignKey(Slice, related_name='instances') deployment = models.ForeignKey(Deployment, verbose_name='deployment', related_name='instance_deployment') node = models.ForeignKey(Node, related_name='instances') numberCores = models.IntegerField(verbose_name="Number of Cores", help_text="Number of cores for instance", default=0) flavor = models.ForeignKey(Flavor, help_text="Flavor of this instance", default=get_default_flavor) tags = GenericRelation(Tag) userData = models.TextField( blank=True, null=True, help_text="user_data passed to instance during creation") isolation = models.CharField(null=False, blank=False, max_length=30, choices=ISOLATION_CHOICES, default="vm") volumes = models.TextField( null=True, blank=True, help_text= "Comma-separated list of directories to expose to parent context") parent = models.ForeignKey( "Instance", null=True, blank=True, help_text="Parent Instance for containers nested inside of VMs") xos_links = [ ModelLink(dest=Slice, via='slice'), ModelLink(dest=Image, via='image') ] def get_controller(self): return self.node.site_deployment.controller def tologdict(self): d = super(Instance, self).tologdict() try: d['slice_name'] = self.slice.name d['controller_name'] = self.get_controller().name except: pass return d def __unicode__(self): if self.name and Slice.objects.filter( id=self.slice_id) and (self.name != self.slice.name): # NOTE: The weird check on self.slice_id was due to a problem when # deleting the slice before the instance. return u'%s' % self.name elif self.instance_name: return u'%s' % (self.instance_name) elif self.id: return u'uninstantiated-%s' % str(self.id) elif self.slice: return u'unsaved-instance on %s' % self.slice.name else: return u'unsaved-instance' def save(self, *args, **kwds): if not self.name: self.name = self.slice.name if not self.creator and hasattr(self, 'caller'): self.creator = self.caller if not self.creator: raise ValidationError('instance has no creator') if (self.isolation == "container") or (self.isolation == "container_vm"): if (self.image.kind != "container"): raise ValidationError( "Container instance must use container image") elif (self.isolation == "vm"): if (self.image.kind != "vm"): raise ValidationError("VM instance must use VM image") if (self.isolation == "container_vm") and (not self.parent): raise ValidationError("Container-vm instance must have a parent") if (self.parent) and (self.isolation != "container_vm"): raise ValidationError( "Parent field can only be set on Container-vm instances") if (self.slice.creator != self.creator): # Check to make sure there's a slice_privilege for the user. If there # isn't, then keystone will throw an exception inside the observer. slice_privs = SlicePrivilege.objects.filter(slice=self.slice, user=self.creator) if not slice_privs: raise ValidationError( 'instance creator has no privileges on slice') # XXX smbaker - disabled for now, was causing fault in tenant view create slice # if not self.controllerNetwork.test_acl(slice=self.slice): # raise exceptions.ValidationError("Deployment %s's ACL does not allow any of this slice %s's users" % (self.controllerNetwork.name, self.slice.name)) super(Instance, self).save(*args, **kwds) def can_update(self, user): return user.can_update_slice(self.slice) def all_ips(self): ips = {} for ns in self.ports.all(): if ns.ip: ips[ns.network.name] = ns.ip return ips def all_ips_string(self): result = [] ips = self.all_ips() for key in sorted(ips.keys()): #result.append("%s = %s" % (key, ips[key])) result.append(ips[key]) return ", ".join(result) all_ips_string.short_description = "addresses" def get_public_ip(self): for ns in self.ports.all(): if (ns.ip) and (ns.network.template.visibility == "public") and (ns.network.template.translation == "none"): return ns.ip return None # return an address on nat-net def get_network_ip(self, pattern): for ns in self.ports.all(): if pattern in ns.network.name.lower(): return ns.ip return None # return an address that the synchronizer can use to SSH to the instance def get_ssh_ip(self): # first look specifically for a management_local network for ns in self.ports.all(): if ns.network.template and ns.network.template.vtn_kind == "MANAGEMENT_LOCAL": return ns.ip # for compatibility, now look for any management network management = self.get_network_ip("management") if management: return management # if all else fails, look for nat-net (for OpenCloud?) return self.get_network_ip("nat") @staticmethod def select_by_user(user): if user.is_admin: qs = Instance.objects.all() else: slices = Slice.select_by_user(user) qs = Instance.objects.filter(slice__in=slices) return qs def get_cpu_stats(self): filter = 'instance_id=%s' % self.instance_id return monitor.get_meter('cpu', filter, None) def get_bw_stats(self): filter = 'instance_id=%s' % self.instance_id return monitor.get_meter('network.outgoing.bytes', filter, None) def get_node_stats(self): # Note sure what should go back here return 1 def get_ssh_command(self): if (not self.instance_id) or (not self.node) or ( not self.instance_name): return None else: return 'ssh -o "ProxyCommand ssh -q %s@%s" ubuntu@%s' % ( self.instance_id, self.node.name, self.instance_name) def get_public_keys(self): slice_memberships = SlicePrivilege.objects.filter(slice=self.slice) pubkeys = set([ sm.user.public_key for sm in slice_memberships if sm.user.public_key ]) if self.creator.public_key: pubkeys.add(self.creator.public_key) if self.slice.creator.public_key: pubkeys.add(self.slice.creator.public_key) if self.slice.service and self.slice.service.public_key: pubkeys.add(self.slice.service.public_key) return pubkeys
class SyncVSGTenant(SyncInstanceUsingAnsible): provides = [VSGTenant] observes = VSGTenant requested_interval = 0 template_name = "sync_vcpetenant.yaml" watches = [ ModelLink(CoarseTenant, via='coarsetenant'), ModelLink(ServiceMonitoringAgentInfo, via='monitoringagentinfo') ] def __init__(self, *args, **kwargs): super(SyncVSGTenant, self).__init__(*args, **kwargs) def fetch_pending(self, deleted): if (not deleted): objs = VSGTenant.get_tenant_objects().filter( Q(enacted__lt=F('updated')) | Q(enacted=None), Q(lazy_blocked=False)) else: objs = VSGTenant.get_deleted_tenant_objects() return objs def get_vcpe_service(self, o): if not o.provider_service: return None vcpes = VSGService.get_service_objects().filter( id=o.provider_service.id) if not vcpes: return None return vcpes[0] def get_extra_attributes(self, o): # This is a place to include extra attributes that aren't part of the # object itself. In the case of vCPE, we need to know: # 1) the addresses of dnsdemux, to setup dnsmasq in the vCPE # 2) CDN prefixes, so we know what URLs to send to dnsdemux # 3) BroadBandShield server addresses, for parental filtering # 4) vlan_ids, for setting up networking in the vCPE VM vcpe_service = self.get_vcpe_service(o) dnsdemux_ip = None cdn_prefixes = [] cdn_config_fn = "/opt/xos/synchronizers/vsg/cdn_config" if os.path.exists(cdn_config_fn): # manual CDN configuration # the first line is the address of dnsredir # the remaining lines are domain names, one per line lines = file(cdn_config_fn).readlines() if len(lines) >= 2: dnsdemux_ip = lines[0].strip() cdn_prefixes = [x.strip() for x in lines[1:] if x.strip()] elif hpc_service_onboarded: # automatic CDN configuiration # it learns everything from CDN objects in XOS # not tested on pod. if vcpe_service.backend_network_label: # Connect to dnsdemux using the network specified by # vcpe_service.backend_network_label for service in HpcService.objects.all(): for slice in service.slices.all(): if "dnsdemux" in slice.name: for instance in slice.instances.all(): for ns in instance.ports.all(): if ns.ip and ns.network.labels and ( vcpe_service.backend_network_label in ns.network.labels): dnsdemux_ip = ns.ip if not dnsdemux_ip: logger.info("failed to find a dnsdemux on network %s" % vcpe_service.backend_network_label, extra=o.tologdict()) else: # Connect to dnsdemux using the instance's public address for service in HpcService.objects.all(): for slice in service.slices.all(): if "dnsdemux" in slice.name: for instance in slice.instances.all(): if dnsdemux_ip == "none": try: dnsdemux_ip = socket.gethostbyname( instance.node.name) except: pass if not dnsdemux_ip: logger.info( "failed to find a dnsdemux with a public address", extra=o.tologdict()) for prefix in CDNPrefix.objects.all(): cdn_prefixes.append(prefix.prefix) dnsdemux_ip = dnsdemux_ip or "none" # Broadbandshield can either be set up internally, using vcpe_service.bbs_slice, # or it can be setup externally using vcpe_service.bbs_server. bbs_addrs = [] if vcpe_service.bbs_slice: if vcpe_service.backend_network_label: for bbs_instance in vcpe_service.bbs_slice.instances.all(): for ns in bbs_instance.ports.all(): if ns.ip and ns.network.labels and ( vcpe_service.backend_network_label in ns.network.labels): bbs_addrs.append(ns.ip) else: logger.info( "unsupported configuration -- bbs_slice is set, but backend_network_label is not", extra=o.tologdict()) if not bbs_addrs: logger.info("failed to find any usable addresses on bbs_slice", extra=o.tologdict()) elif vcpe_service.bbs_server: bbs_addrs.append(vcpe_service.bbs_server) else: logger.info( "neither bbs_slice nor bbs_server is configured in the vCPE", extra=o.tologdict()) s_tags = [] c_tags = [] if o.volt: s_tags.append(o.volt.s_tag) c_tags.append(o.volt.c_tag) try: full_setup = Config().observer_full_setup except: full_setup = True safe_macs = [] if vcpe_service.url_filter_kind == "safebrowsing": if o.volt and o.volt.subscriber: for user in o.volt.subscriber.devices: level = user.get("level", None) mac = user.get("mac", None) if level in ["G", "PG"]: if mac: safe_macs.append(mac) docker_opts = [] if vcpe_service.docker_insecure_registry: reg_name = vcpe_service.docker_image_name.split("/", 1)[0] docker_opts.append("--insecure-registry " + reg_name) fields = { "s_tags": s_tags, "c_tags": c_tags, "docker_remote_image_name": vcpe_service.docker_image_name, "docker_local_image_name": vcpe_service. docker_image_name, # vcpe_service.docker_image_name.split("/",1)[1].split(":",1)[0], "docker_opts": " ".join(docker_opts), "dnsdemux_ip": dnsdemux_ip, "cdn_prefixes": cdn_prefixes, "bbs_addrs": bbs_addrs, "full_setup": full_setup, "isolation": o.instance.isolation, "safe_browsing_macs": safe_macs, "container_name": "vcpe-%s-%s" % (s_tags[0], c_tags[0]), "dns_servers": [x.strip() for x in vcpe_service.dns_servers.split(",")], "url_filter_kind": vcpe_service.url_filter_kind } # add in the sync_attributes that come from the SubscriberRoot object if o.volt and o.volt.subscriber and hasattr(o.volt.subscriber, "sync_attributes"): for attribute_name in o.volt.subscriber.sync_attributes: fields[attribute_name] = getattr(o.volt.subscriber, attribute_name) return fields def sync_fields(self, o, fields): # the super causes the playbook to be run super(SyncVSGTenant, self).sync_fields(o, fields) # now do all of our broadbandshield stuff... service = self.get_vcpe_service(o) if not service: # Ansible uses the service's keypair in order to SSH into the # instance. It would be bad if the slice had no service. raise Exception("Slice %s is not associated with a service" % instance.slice.name) # Make sure the slice is configured properly if (service != o.instance.slice.service): raise Exception( "Slice %s is associated with some service that is not %s" % (str(instance.slice), str(service))) # only enable filtering if we have a subscriber object (see below) url_filter_enable = False # for attributes that come from CordSubscriberRoot if o.volt and o.volt.subscriber: url_filter_enable = o.volt.subscriber.url_filter_enable url_filter_level = o.volt.subscriber.url_filter_level url_filter_users = o.volt.subscriber.devices if service.url_filter_kind == "broadbandshield": # disable url_filter if there are no bbs_addrs if url_filter_enable and (not fields.get("bbs_addrs", [])): logger.info( "disabling url_filter because there are no bbs_addrs", extra=o.tologdict()) url_filter_enable = False if url_filter_enable: bbs_hostname = None if service.bbs_api_hostname and service.bbs_api_port: bbs_hostname = service.bbs_api_hostname else: # TODO: extract from slice bbs_hostname = "cordcompute01.onlab.us" if service.bbs_api_port: bbs_port = service.bbs_api_port else: bbs_port = 8018 if not bbs_hostname: logger.info("broadbandshield is not configured", extra=o.tologdict()) else: tStart = time.time() bbs = BBS(o.bbs_account, "123", bbs_hostname, bbs_port) bbs.sync(url_filter_level, url_filter_users) if o.hpc_client_ip: logger.info("associate account %s with ip %s" % (o.bbs_account, o.hpc_client_ip), extra=o.tologdict()) bbs.associate(o.hpc_client_ip) else: logger.info("no hpc_client_ip to associate", extra=o.tologdict()) logger.info("bbs update time %d" % int(time.time() - tStart), extra=o.tologdict()) def run_playbook(self, o, fields): ansible_hash = hashlib.md5(repr(sorted(fields.items()))).hexdigest() quick_update = (o.last_ansible_hash == ansible_hash) if ENABLE_QUICK_UPDATE and quick_update: logger.info("quick_update triggered; skipping ansible recipe", extra=o.tologdict()) else: if o.instance.isolation in ["container", "container_vm"]: super(SyncVSGTenant, self).run_playbook(o, fields, "sync_vcpetenant_new.yaml") else: if CORD_USE_VTN: super(SyncVSGTenant, self).run_playbook( o, fields, template_name="sync_vcpetenant_vtn.yaml") else: super(SyncVSGTenant, self).run_playbook(o, fields) o.last_ansible_hash = ansible_hash def delete_record(self, m): pass def handle_service_monitoringagentinfo_watch_notification( self, monitoring_agent_info): if not monitoring_agent_info.service: logger.info( "handle watch notifications for service monitoring agent info...ignoring because service attribute in monitoring agent info:%s is null" % (monitoring_agent_info)) return if not monitoring_agent_info.target_uri: logger.info( "handle watch notifications for service monitoring agent info...ignoring because target_uri attribute in monitoring agent info:%s is null" % (monitoring_agent_info)) return objs = VSGTenant.get_tenant_objects().all() for obj in objs: if obj.provider_service.id != monitoring_agent_info.service.id: logger.info( "handle watch notifications for service monitoring agent info...ignoring because service attribute in monitoring agent info:%s is not matching" % (monitoring_agent_info)) return instance = self.get_instance(obj) if not instance: logger.warn( "handle watch notifications for service monitoring agent info...: No valid instance found for object %s" % (str(obj))) return logger.info( "handling watch notification for monitoring agent info:%s for VSGTenant object:%s" % (monitoring_agent_info, obj)) #Run ansible playbook to update the routing table entries in the instance fields = self.get_ansible_fields(instance) fields["ansible_tag"] = obj.__class__.__name__ + "_" + str( obj.id) + "_service_monitoring" #Parse the monitoring agent target_uri url = urlparse(monitoring_agent_info.target_uri) #Assuming target_uri is rabbitmq URI fields["rabbit_user"] = url.username fields["rabbit_password"] = url.password fields["rabbit_host"] = url.hostname template_name = "sync_monitoring_agent.yaml" super(SyncVSGTenant, self).run_playbook(obj, fields, template_name) pass
class Port(PlCoreBase, ParameterMixin): network = models.ForeignKey(Network, related_name='links') instance = models.ForeignKey(Instance, null=True, blank=True, related_name='ports') ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True) port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Neutron port id") mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port") xos_created = models.BooleanField( default=False ) # True if XOS created this port in Neutron, False if port created by Neutron and observed by XOS xos_links = [ ModelLink(Network, via='network'), ModelLink('Instance', via='instance') ] class Meta: unique_together = ('network', 'instance') def save(self, *args, **kwds): if self.instance: slice = self.instance.slice if (slice not in self.network.permitted_slices.all()) and ( slice != self.network.owner) and ( not self.network.permit_all_slices): # to add a instance to the network, then one of the following must be true: # 1) instance's slice is in network's permittedSlices list, # 2) instance's slice is network's owner, or # 3) network's permitAllSlices is true raise ValueError( "Slice %s is not allowed to connect to network %s" % (str(slice), str(self.network))) super(Port, self).save(*args, **kwds) def __unicode__(self): if self.instance: return u'%s-%s' % (self.network.name, self.instance.instance_name) else: return u'%s-unboundport-%s' % (self.network.name, self.id) def can_update(self, user): if self.instance: return user.can_update_slice(self.instance.slice) if self.network: return user.can_update_slice(self.network.owner) return False @staticmethod def select_by_user(user): if user.is_admin: qs = Port.objects.all() else: instances = Instance.select_by_user(user) instance_ids = [instance.id for instance in instances] networks = Network.select_by_user(user) network_ids = [network.id for network in networks] qs = Port.objects.filter( Q(instance__in=instance_ids) | Q(network__in=network_ids)) return qs def get_parameters(self): # returns parameters from the network, updated by self. p = {} if self.network: p = self.network.get_parameters() p.update(ParameterMixin.get_parameters(self)) return p
class Slice(PlCoreBase): ISOLATION_CHOICES = (('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM')) NETWORK_CHOICES = ((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks')) name = StrippedCharField(unique=True, help_text="The Name of the Slice", max_length=80) enabled = models.BooleanField(default=True, help_text="Status for this Slice") omf_friendly = models.BooleanField(default=False) description = models.TextField( blank=True, help_text="High level description of the slice and expected activities", max_length=1024) slice_url = models.URLField(blank=True, max_length=512) site = models.ForeignKey(Site, related_name='slices', help_text="The Site this Slice belongs to") max_instances = models.IntegerField(default=10) service = models.ForeignKey(Service, related_name='slices', null=True, blank=True) network = models.CharField(null=True, blank=True, max_length=256, choices=NETWORK_CHOICES) exposed_ports = models.CharField(null=True, blank=True, max_length=256) tags = GenericRelation(Tag) serviceClass = models.ForeignKey(ServiceClass, related_name="slices", null=True, blank=True) # DEPRECATED creator = models.ForeignKey(User, related_name='slices', blank=True, null=True) # for tenant view default_flavor = models.ForeignKey(Flavor, related_name="slices", null=True, blank=True) default_image = models.ForeignKey(Image, related_name="slices", null=True, blank=True) default_node = models.ForeignKey(Node, related_name="slices", null=True, blank=True) mount_data_sets = StrippedCharField(default="GenBank", null=True, blank=True, max_length=256) default_isolation = models.CharField(null=False, blank=False, max_length=30, choices=ISOLATION_CHOICES, default="vm") xos_links = [ModelLink(Site, 'site'), ModelLink(User, 'user')] def __unicode__(self): return u'%s' % (self.name) @property def slicename(self): return "%s_%s" % (self.site.login_base, self.name) def save(self, *args, **kwds): site = Site.objects.get(id=self.site.id) # allow preexisting slices to keep their original name for now if not self.id and not self.name.startswith(site.login_base): raise XOSValidationError('slice name must begin with %s' % site.login_base) if self.name == site.login_base + "_": raise XOSValidationError('slice name is too short') if " " in self.name: raise XOSValidationError('slice name must not contain spaces') # set creator on first save if not self.creator and hasattr(self, 'caller'): self.creator = self.caller # only admins change a slice's creator if 'creator' in self.changed_fields and \ (not hasattr(self, 'caller') or not self.caller.is_admin): if (self._initial["creator"] == None) and (self.creator == getattr( self, "caller", None)): # it's okay if the creator is being set by the caller to # himeself on a new slice object. pass else: raise PermissionDenied( "Insufficient privileges to change slice creator") if not self.creator: raise XOSValidationError('slice has no creator') if self.network == "Private Only": # "Private Only" was the default from the old Tenant View self.network = None self.enforce_choices(self.network, self.NETWORK_CHOICES) super(Slice, self).save(*args, **kwds) def can_update(self, user): return user.can_update_slice(self) @staticmethod def select_by_user(user): if user.is_admin: qs = Slice.objects.all() else: # users can see slices they belong to slice_ids = [ sp.slice.id for sp in SlicePrivilege.objects.filter(user=user) ] # pis and admins can see slices at their sites sites = [sp.site for sp in SitePrivilege.objects.filter(user=user)\ if (sp.role.role == 'pi') or (sp.role.role == 'admin')] slice_ids.extend( [s.id for s in Slice.objects.filter(site__in=sites)]) qs = Slice.objects.filter(id__in=slice_ids) return qs """
class Controller(PlCoreBase): objects = ControllerManager() deleted_objects = ControllerDeletionManager() name = StrippedCharField(max_length=200, unique=True, help_text="Name of the Controller") backend_type = StrippedCharField( max_length=200, help_text= "Type of compute controller, e.g. EC2, OpenStack, or OpenStack version" ) version = StrippedCharField(max_length=200, help_text="Controller version") auth_url = StrippedCharField( max_length=200, null=True, blank=True, help_text="Auth url for the compute controller") admin_user = StrippedCharField( max_length=200, null=True, blank=True, help_text="Username of an admin user at this controller") admin_password = StrippedCharField( max_length=200, null=True, blank=True, help_text="Password of theadmin user at this controller") admin_tenant = StrippedCharField( max_length=200, null=True, blank=True, help_text="Name of the tenant the admin user belongs to") domain = StrippedCharField( max_length=200, null=True, blank=True, help_text="Name of the domain this controller belongs to") rabbit_host = StrippedCharField( max_length=200, null=True, blank=True, help_text="IP address of rabbitmq server at this controller") rabbit_user = StrippedCharField( max_length=200, null=True, blank=True, help_text="Username of rabbitmq server at this controller") rabbit_password = StrippedCharField( max_length=200, null=True, blank=True, help_text="Password of rabbitmq server at this controller") deployment = models.ForeignKey(Deployment, related_name='controllerdeployments') xos_links = [ModelLink(Deployment, via='deployment')] def __init__(self, *args, **kwargs): super(Controller, self).__init__(*args, **kwargs) self.no_sync = True def __unicode__(self): return u'%s %s %s' % (self.name, self.backend_type, self.version) @property def auth_url_v3(self): if self.auth_url and self.auth_url[-1] == '/': return '{}/v3/'.format('/'.join(self.auth_url.split('/')[:-2])) else: return '{}/v3/'.format('/'.join(self.auth_url.split('/')[:-1])) @staticmethod def select_by_user(user): if user.is_admin: qs = Controller.objects.all() else: deployments = [ dp.deployment for dp in DeploymentPrivilege.objects.filter( user=user, role__role__in=['Admin', 'admin']) ] qs = Controller.objects.filter(deployment__in=deployments) return qs