refactor vBBU as a separate project
(1) Moved xos/xos/services/mcord/ to xos_services/vBBU
(2) Moved the location of tosca files
from xos/xos/tosca/ to xos_services/vBBU/xos/tosca/
(3) Moved the location of synchronizer files
from xos/xos/synchronizers to xos_services/vBBU/xos/synchronizers
(4) Added make_synchronizer_manifest.sh and manifest
(5) Added vBBU-onboard.yaml
Change-Id: I97c8c234d66ef3121403fb9dd109d7497efe4e2a
diff --git a/xos/__init__.py b/xos/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xos/__init__.py
diff --git a/xos/admin.py b/xos/admin.py
new file mode 100644
index 0000000..7e20f70
--- /dev/null
+++ b/xos/admin.py
@@ -0,0 +1,212 @@
+
+from core.admin import ReadOnlyAwareAdmin, SliceInline
+from core.middleware import get_request
+from core.models import User
+from django import forms
+from django.contrib import admin
+from services.mcord.models import MCORDService, VBBUComponent, VPGWCComponent, MCORD_KIND
+
+# The class to provide an admin interface on the web for the service.
+# We do only configuration here and don't change any logic because the logic
+# is taken care of for us by ReadOnlyAwareAdmin
+class MCORDServiceAdmin(ReadOnlyAwareAdmin):
+ # We must set the model so that the admin knows what fields to use
+ model = MCORDService
+ verbose_name = "MCORD Service"
+ verbose_name_plural = "MCORD Services"
+
+ # Setting list_display creates columns on the admin page, each value here
+ # is a column, the column is populated for every instance of the model.
+ list_display = ("backend_status_icon", "name", "enabled")
+
+ # Used to indicate which values in the columns of the admin form are links.
+ list_display_links = ('backend_status_icon', 'name', )
+
+ # Denotes the sections of the form, the fields in the section, and the
+ # CSS classes used to style them. We represent this as a set of tuples, each
+ # tuple as a name (or None) and a set of fields and classes.
+ # Here the first section does not have a name so we use none. That first
+ # section has several fields indicated in the 'fields' attribute, and styled
+ # by the classes indicated in the 'classes' attribute. The classes given
+ # here are important for rendering the tabs on the form. To give the tabs
+ # we must assign the classes suit-tab and suit-tab-<name> where
+ # where <name> will be used later.
+ fieldsets = [(None, {'fields': ['backend_status_text', 'name', 'enabled',
+ 'versionNumber', 'description', "view_url"],
+ 'classes':['suit-tab suit-tab-general']})]
+
+ # Denotes the fields that are readonly and cannot be changed.
+ readonly_fields = ('backend_status_text', )
+
+ # Inlines are used to denote other models that can be edited on the same
+ # form as this one. In this case the service form also allows changes
+ # to slices.
+ inlines = [SliceInline]
+
+ extracontext_registered_admins = True
+
+ # Denotes the fields that can be changed by an admin but not be all users
+ user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
+
+ # Associates fieldsets from this form and from the inlines.
+ # The format here are tuples, of (<name>, tab title). <name> comes from the
+ # <name> in the fieldsets.
+ suit_form_tabs = (('general', 'MCORD Service Details'),
+ ('administration', 'Components'),
+ ('slices', 'Slices'),)
+
+ # Used to include a template for a tab. Here we include the
+ # helloworldserviceadmin template in the top position for the administration
+ # tab.
+ suit_form_includes = (('mcordadmin.html',
+ 'top',
+ 'administration'),)
+
+ # Used to get the objects for this model that are associated with the
+ # requesting user.
+ def queryset(self, request):
+ return MCORDService.get_service_objects_by_user(request.user)
+
+# Class to represent the form to add and edit tenants.
+# We need to define this instead of just using an admin like we did for the
+# service because tenants vary more than services and there isn't a common form.
+# This allows us to change the python behavior for the admin form to save extra
+# fields and control defaults.
+class VBBUComponentForm(forms.ModelForm):
+ # Defines a field for the creator of this service. It is a dropdown which
+ # is populated with all of the users.
+ creator = forms.ModelChoiceField(queryset=User.objects.all())
+ # Defines a text field for the display message, it is not required.
+ display_message = forms.CharField(required=False)
+
+ def __init__(self, *args, **kwargs):
+ super(VBBUComponentForm, self).__init__(*args, **kwargs)
+ # Set the kind field to readonly
+ self.fields['kind'].widget.attrs['readonly'] = True
+ # Define the logic for obtaining the objects for the provider_service
+ # dropdown of the tenant form.
+ self.fields[
+ 'provider_service'].queryset = MCORDService.get_service_objects().all()
+ # Set the initial kind to HELLO_WORLD_KIND for this tenant.
+ self.fields['kind'].initial = MCORD_KIND
+ # If there is an instance of this model then we can set the initial
+ # form values to the existing values.
+ if self.instance:
+ self.fields['creator'].initial = self.instance.creator
+ self.fields[
+ 'display_message'].initial = self.instance.display_message
+
+ # If there is not an instance then we need to set initial values.
+ if (not self.instance) or (not self.instance.pk):
+ self.fields['creator'].initial = get_request().user
+ if MCORDService.get_service_objects().exists():
+ self.fields["provider_service"].initial = MCORDService.get_service_objects().all()[0]
+
+ # This function describes what happens when the save button is pressed on
+ # the tenant form. In this case we set the values for the instance that were
+ # entered.
+ def save(self, commit=True):
+ self.instance.creator = self.cleaned_data.get("creator")
+ self.instance.display_message = self.cleaned_data.get(
+ "display_message")
+ return super(VBBUComponentForm, self).save(commit=commit)
+
+ class Meta:
+ model = VBBUComponent
+ fields = '__all__'
+
+# Class to represent the form to add and edit tenants.
+# We need to define this instead of just using an admin like we did for the
+# service because tenants vary more than services and there isn't a common form.
+# This allows us to change the python behavior for the admin form to save extra
+# fields and control defaults.
+class VPGWCComponentForm(forms.ModelForm):
+ # Defines a field for the creator of this service. It is a dropdown which
+ # is populated with all of the users.
+ creator = forms.ModelChoiceField(queryset=User.objects.all())
+ # Defines a text field for the display message, it is not required.
+ display_message = forms.CharField(required=False)
+
+ def __init__(self, *args, **kwargs):
+ super(VPGWCComponentForm, self).__init__(*args, **kwargs)
+ # Set the kind field to readonly
+ self.fields['kind'].widget.attrs['readonly'] = True
+ # Define the logic for obtaining the objects for the provider_service
+ # dropdown of the tenant form.
+ self.fields[
+ 'provider_service'].queryset = MCORDService.get_service_objects().all()
+ # Set the initial kind to HELLO_WORLD_KIND for this tenant.
+ self.fields['kind'].initial = MCORD_KIND
+ # If there is an instance of this model then we can set the initial
+ # form values to the existing values.
+ if self.instance:
+ self.fields['creator'].initial = self.instance.creator
+ self.fields[
+ 'display_message'].initial = self.instance.display_message
+
+ # If there is not an instance then we need to set initial values.
+ if (not self.instance) or (not self.instance.pk):
+ self.fields['creator'].initial = get_request().user
+ if MCORDService.get_service_objects().exists():
+ self.fields["provider_service"].initial = MCORDService.get_service_objects().all()[0]
+
+ # This function describes what happens when the save button is pressed on
+ # the tenant form. In this case we set the values for the instance that were
+ # entered.
+ def save(self, commit=True):
+ self.instance.creator = self.cleaned_data.get("creator")
+ self.instance.display_message = self.cleaned_data.get(
+ "display_message")
+ return super(VPGWCComponentForm, self).save(commit=commit)
+
+ class Meta:
+ model = VPGWCComponent
+ fields = '__all__'
+
+
+# Define the admin form for the tenant. This uses a similar structure as the
+# service but uses HelloWorldTenantCompleteForm to change the python behavior.
+
+class VBBUComponentAdmin(ReadOnlyAwareAdmin):
+ verbose_name = "vBBU Component"
+ verbose_name_plural = "vBBU Components"
+ list_display = ('id', 'backend_status_icon', 'instance', 'display_message')
+ list_display_links = ('backend_status_icon', 'instance', 'display_message',
+ 'id')
+ fieldsets = [(None, {'fields': ['backend_status_text', 'kind',
+ 'provider_service', 'instance', 'creator',
+ 'display_message'],
+ 'classes': ['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', 'instance',)
+ form = VBBUComponentForm
+
+ suit_form_tabs = (('general', 'Details'),)
+
+ def queryset(self, request):
+ return VBBUComponent.get_tenant_objects_by_user(request.user)
+
+
+# Define the admin form for the tenant. This uses a similar structure as the
+# service but uses HelloWorldTenantCompleteForm to change the python behavior.
+class VPGWCComponentAdmin(ReadOnlyAwareAdmin):
+ verbose_name = "vPGWC Component"
+ verbose_name_plural = "vPGWC Components"
+ list_display = ('id', 'backend_status_icon', 'instance', 'display_message')
+ list_display_links = ('backend_status_icon', 'instance', 'display_message',
+ 'id')
+ fieldsets = [(None, {'fields': ['backend_status_text', 'kind',
+ 'provider_service', 'instance', 'creator',
+ 'display_message'],
+ 'classes': ['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', 'instance',)
+ form = VPGWCComponentForm
+
+ suit_form_tabs = (('general', 'Details'),)
+
+ def queryset(self, request):
+ return VPGWCComponent.get_tenant_objects_by_user(request.user)
+
+# Associate the admin forms with the models.
+admin.site.register(MCORDService, MCORDServiceAdmin)
+admin.site.register(VBBUComponent, VBBUComponentAdmin)
+admin.site.register(VPGWCComponent, VPGWCComponentAdmin)
diff --git a/xos/make_synchronizer_manifest.sh b/xos/make_synchronizer_manifest.sh
new file mode 100755
index 0000000..4058982
--- /dev/null
+++ b/xos/make_synchronizer_manifest.sh
@@ -0,0 +1,2 @@
+#! /bin/bash
+find synchronizer -type f | cut -b 14- > synchronizer/manifest
diff --git a/xos/migrations/0001_initial.py b/xos/migrations/0001_initial.py
new file mode 100644
index 0000000..a11fe30
--- /dev/null
+++ b/xos/migrations/0001_initial.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='MCORDService',
+ fields=[
+ ],
+ options={
+ 'verbose_name': 'MCORD Service',
+ 'proxy': True,
+ },
+ bases=('core.service',),
+ ),
+ migrations.CreateModel(
+ name='VBBUComponent',
+ fields=[
+ ],
+ options={
+ 'verbose_name': 'VBBU MCORD Service Component',
+ 'proxy': True,
+ },
+ bases=('core.tenantwithcontainer',),
+ ),
+ migrations.CreateModel(
+ name='VPGWCComponent',
+ fields=[
+ ],
+ options={
+ 'verbose_name': 'VPGWC MCORD Service Component',
+ 'proxy': True,
+ },
+ bases=('core.tenantwithcontainer',),
+ ),
+ ]
diff --git a/xos/migrations/__init__.py b/xos/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xos/migrations/__init__.py
diff --git a/xos/models.py b/xos/models.py
new file mode 100644
index 0000000..6d070d8
--- /dev/null
+++ b/xos/models.py
@@ -0,0 +1,483 @@
+from django.db import models
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, AddressPool, Port
+from core.models.plcorebase import StrippedCharField
+import os
+from django.db import models, transaction
+from django.forms.models import model_to_dict
+from django.db.models import Q
+from operator import itemgetter, attrgetter, methodcaller
+from core.models import Tag
+from core.models.service import LeastLoadedNodeScheduler
+import traceback
+from xos.exceptions import *
+from core.models import SlicePrivilege, SitePrivilege
+from sets import Set
+from xos.config import Config
+
+MCORD_KIND = "RAN" # This should be changed later I did it fo demo
+MCORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
+VBBU_KIND = "RAN"
+VSGW_KIND = "vSGW"
+VPGWC_KIND = "RAN"
+vbbu_net_types = ("s1u", "s1mme", "rru")
+vpgwc_net_types = ("s5s8")
+# The class to represent the service. Most of the service logic is given for us
+# in the Service class but, we have some configuration that is specific for
+# this example.
+class MCORDService(Service):
+ KIND = MCORD_KIND
+
+ class Meta:
+ # When the proxy field is set to True the model is represented as
+ # it's superclass in the database, but we can still change the python
+ # behavior. In this case HelloWorldServiceComplete is a Service in the
+ # database.
+ proxy = True
+ # The name used to find this service, all directories are named this
+ app_label = "mcord"
+ verbose_name = "MCORD Service"
+
+# This is the class to represent the tenant. Most of the logic is given to use
+# in TenantWithContainer, however there is some configuration and logic that
+# we need to define for this example.
+class VBBUComponent(TenantWithContainer):
+
+ class Meta:
+ # Same as a above, HelloWorldTenantComplete is represented as a
+ # TenantWithContainer, but we change the python behavior.
+ proxy = True
+ verbose_name = "VBBU MCORD Service Component"
+
+ # The kind of the service is used on forms to differentiate this service
+ # from the other services.
+ KIND = VBBU_KIND
+
+ # Ansible requires that the sync_attributes field contain nat_ip and nat_mac
+ # these will be used to determine where to SSH to for ansible.
+ # Getters must be defined for every attribute specified here.
+ sync_attributes = ("s1u_ip", "s1u_mac",
+ "s1mme_ip", "s1mme_mac",
+ "rru_ip", "rru_mac")
+ # default_attributes is used cleanly indicate what the default values for
+ # the fields are.
+ default_attributes = {"display_message": "New vBBU Component", "s1u_tag": "901", "s1mme_tag": "900", "rru_tag": "999"}
+ def __init__(self, *args, **kwargs):
+ mcord_services = MCORDService.get_service_objects().all()
+ # When the tenant is created the default service in the form is set
+ # to be the first created HelloWorldServiceComplete
+ if mcord_services:
+ self._meta.get_field(
+ "provider_service").default = mcord_services[0].id
+ super(VBBUComponent, self).__init__(*args, **kwargs)
+
+ def can_update(self, user):
+ #Allow creation of this model instances for non-admin users also
+ return True
+
+ def save(self, *args, **kwargs):
+ if not self.creator:
+ if not getattr(self, "caller", None):
+ # caller must be set when creating a monitoring channel since it creates a slice
+ raise XOSProgrammingError("ServiceComponents's self.caller was not set")
+ self.creator = self.caller
+ if not self.creator:
+ raise XOSProgrammingError("ServiceComponents's self.creator was not set")
+
+ super(VBBUComponent, self).save(*args, **kwargs)
+ # This call needs to happen so that an instance is created for this
+ # tenant is created in the slice. One instance is created per tenant.
+ model_policy_mcord_servicecomponent(self.pk)
+
+ def save_instance(self, instance):
+ with transaction.atomic():
+ super(VBBUComponent, self).save_instance(instance)
+ if instance.isolation in ["vm"]:
+ for ntype in vbbu_net_types:
+ lan_network = self.get_lan_network(instance, ntype)
+ port = self.find_or_make_port(instance,lan_network)
+ if (ntype == "s1u"):
+ port.set_parameter("s_tag", self.s1u_tag)
+ port.set_parameter("neutron_port_name", "stag-%s" % self.s1u_tag)
+ port.save()
+ elif (ntype == "s1mme"):
+ port.set_parameter("s_tag", self.s1mme_tag)
+ port.set_parameter("neutron_port_name", "stag-%s" % self.s1mme_tag)
+ port.save()
+ elif (ntype == "rru"):
+ port.set_parameter("s_tag", self.rru_tag)
+ port.set_parameter("neutron_port_name", "stag-%s" % self.rru_tag)
+ port.save()
+
+ def delete(self, *args, **kwargs):
+ # Delete the instance that was created for this tenant
+ self.cleanup_container()
+ super(VBBUComponent, self).delete(*args, **kwargs)
+
+ def find_or_make_port(self, instance, network, **kwargs):
+ port = Port.objects.filter(instance=instance, network=network)
+ if port:
+ port = port[0]
+ print "port already exist", port[0]
+ else:
+ port = Port(instance=instance, network=network, **kwargs)
+ print "NETWORK", network, "MAKE_PORT", port
+ port.save()
+ return port
+
+ def get_lan_network(self, instance, ntype):
+ slice = self.provider_service.slices.all()[0]
+ lan_networks = [x for x in slice.networks.all() if ntype in x.name]
+ if not lan_networks:
+ raise XOSProgrammingError("No lan_network")
+ return lan_networks[0]
+
+ def manage_container(self):
+ from core.models import Instance, Flavor
+
+ if self.deleted:
+ return
+
+ # For container or container_vm isolation, use what TenantWithCotnainer
+ # provides us
+ slice = self.get_slice()
+ if slice.default_isolation in ["container_vm", "container"]:
+ super(VBBUComponent,self).manage_container()
+ return
+
+ if not self.s1u_tag:
+ raise XOSConfigurationError("S1U_TAG is missed")
+
+ if not self.s1mme_tag:
+ raise XOSConfigurationError("S1U_TAG is missed")
+
+ if not self.rru_tag:
+ raise XOSConfigurationError("S1U_TAG is missed")
+
+ if self.instance:
+ # We're good.
+ return
+
+ instance = self.make_instance()
+ self.instance = instance
+ super(TenantWithContainer, self).save()
+
+ def get_slice(self):
+ if not self.provider_service.slices.count():
+ raise XOSConfigurationError("The service has no slices")
+ slice = self.provider_service.slices.all()[0]
+ return slice
+
+ def make_instance(self):
+ slice = self.provider_service.slices.all()[0]
+ flavors = Flavor.objects.filter(name=slice.default_flavor)
+# flavors = Flavor.objects.filter(name="m1.xlarge")
+ if not flavors:
+ raise XOSConfigurationError("No default flavor")
+ default_flavor = slice.default_flavor
+ slice = self.provider_service.slices.all()[0]
+ if slice.default_isolation == "container_vm":
+ (node, parent) = ContainerVmScheduler(slice).pick()
+ else:
+ (node, parent) = LeastLoadedNodeScheduler(slice).pick()
+ instance = Instance(slice = slice,
+ node = node,
+ image = self.image,
+ creator = self.creator,
+ deployment = node.site_deployment.deployment,
+ flavor = flavors[0],
+ isolation = slice.default_isolation,
+ parent = parent)
+ self.save_instance(instance)
+ return instance
+
+ def ip_to_mac(self, ip):
+ (a, b, c, d) = ip.split('.')
+ return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
+
+ # Getter for the message that will appear on the webpage
+ # By default it is "Hello World!"
+ @property
+ def display_message(self):
+ return self.get_attribute(
+ "display_message",
+ self.default_attributes['display_message'])
+
+ @display_message.setter
+ def display_message(self, value):
+ self.set_attribute("display_message", value)
+
+ @property
+ def s1u_tag(self):
+ return self.get_attribute(
+ "s1u_tag",
+ self.default_attributes['s1u_tag'])
+
+ @s1u_tag.setter
+ def s1u_tag(self, value):
+ self.set_attribute("s1u_tag", value)
+
+ @property
+ def s1mme_tag(self):
+ return self.get_attribute(
+ "s1mme_tag",
+ self.default_attributes['s1mme_tag'])
+
+ @s1mme_tag.setter
+ def s1mme_tag(self, value):
+ self.set_attribute("s1mme_tag", value)
+
+ @property
+ def rru_tag(self):
+ return self.get_attribute(
+ "rru_tag",
+ self.default_attributes['rru_tag'])
+
+ @rru_tag.setter
+ def rru_tag(self, value):
+ self.set_attribute("rru_tag", value)
+
+
+ @property
+ def addresses(self):
+ if (not self.id) or (not self.instance):
+ return {}
+
+ addresses = {}
+ for ns in self.instance.ports.all():
+ if "s1u" in ns.network.name.lower():
+ addresses["s1u"] = (ns.ip, ns.mac)
+ elif "s1mme" in ns.network.name.lower():
+ addresses["s1mme"] = (ns.ip, ns.mac)
+ elif "rru" in ns.network.name.lower():
+ addresses["rru"] = (ns.ip, ns.mac)
+ return addresses
+
+
+ @property
+ def s1u_ip(self):
+ return self.addresses.get("s1u", (None, None))[0]
+ @property
+ def s1u_mac(self):
+ return self.addresses.get("s1u", (None, None))[1]
+ @property
+ def s1mme_ip(self):
+ return self.addresses.get("s1mme", (None, None))[0]
+ @property
+ def s1mme_mac(self):
+ return self.addresses.get("s1mme", (None, None))[1]
+ @property
+ def rru_ip(self):
+ return self.addresses.get("rru", (None, None))[0]
+ @property
+ def rru_mac(self):
+ return self.addresses.get("rru", (None, None))[1]
+
+
+# This is the class to represent the tenant. Most of the logic is given to use
+# in TenantWithContainer, however there is some configuration and logic that
+# we need to define for this example.
+class VPGWCComponent(TenantWithContainer):
+
+ class Meta:
+ # Same as a above, HelloWorldTenantComplete is represented as a
+ # TenantWithContainer, but we change the python behavior.
+ proxy = True
+ verbose_name = "VPGWC MCORD Service Component"
+
+ # The kind of the service is used on forms to differentiate this service
+ # from the other services.
+ KIND = VPGWC_KIND
+
+ # Ansible requires that the sync_attributes field contain nat_ip and nat_mac
+ # these will be used to determine where to SSH to for ansible.
+ # Getters must be defined for every attribute specified here.
+ sync_attributes = ("s5s8_pgw_ip", "s5s8_pgw_mac")
+
+ # default_attributes is used cleanly indicate what the default values for
+ # the fields are.
+ default_attributes = {"display_message": "New vPGWC Component", "s5s8_pgw_tag": "300"}
+ def __init__(self, *args, **kwargs):
+ mcord_services = MCORDService.get_service_objects().all()
+ # When the tenant is created the default service in the form is set
+ # to be the first created HelloWorldServiceComplete
+ if mcord_services:
+ self._meta.get_field(
+ "provider_service").default = mcord_services[0].id
+ super(VPGWCComponent, self).__init__(*args, **kwargs)
+
+ def can_update(self, user):
+ #Allow creation of this model instances for non-admin users also
+ return True
+
+ def save(self, *args, **kwargs):
+ if not self.creator:
+ if not getattr(self, "caller", None):
+ # caller must be set when creating a monitoring channel since it creates a slice
+ raise XOSProgrammingError("ServiceComponents's self.caller was not set")
+ self.creator = self.caller
+ if not self.creator:
+ raise XOSProgrammingError("ServiceComponents's self.creator was not set")
+
+ super(VPGWCComponent, self).save(*args, **kwargs)
+ # This call needs to happen so that an instance is created for this
+ # tenant is created in the slice. One instance is created per tenant.
+ model_policy_mcord_servicecomponent(self.pk)
+
+ def save_instance(self, instance):
+ with transaction.atomic():
+ super(VPGWCComponent, self).save_instance(instance)
+ if instance.isolation in ["vm"]:
+ for ntype in vpgwc_net_types:
+ lan_network = self.get_lan_network(instance, ntype)
+ port = self.find_or_make_port(instance,lan_network)
+ if (ntype == "s5s8"):
+ port.set_parameter("s_tag", self.s5s8_pgw_tag)
+ port.set_parameter("neutron_port_name", "stag-%s" % self.s5s8_pgw_tag)
+ port.save()
+ else:
+ return True
+
+ def delete(self, *args, **kwargs):
+ # Delete the instance that was created for this tenant
+ self.cleanup_container()
+ super(VPGWCComponent, self).delete(*args, **kwargs)
+
+ def find_or_make_port(self, instance, network, **kwargs):
+ port = Port.objects.filter(instance=instance, network=network)
+ if port:
+ port = port[0]
+ print "port already exist", port[0]
+ else:
+ port = Port(instance=instance, network=network, **kwargs)
+ print "NETWORK", network, "MAKE_PORT", port
+ port.save()
+ return port
+
+ def get_lan_network(self, instance, ntype):
+ slice = self.provider_service.slices.all()[0]
+ lan_networks = [x for x in slice.networks.all() if ntype in x.name]
+ if not lan_networks:
+ raise XOSProgrammingError("No lan_network")
+ return lan_networks[0]
+
+ def manage_container(self):
+ from core.models import Instance, Flavor
+
+ if self.deleted:
+ return
+
+ # For container or container_vm isolation, use what TenantWithCotnainer
+ # provides us
+ slice = self.get_slice()
+ if slice.default_isolation in ["container_vm", "container"]:
+ super(VPGWCComponent,self).manage_container()
+ return
+
+ if not self.s5s8_pgw_tag:
+ raise XOSConfigurationError("S5S8_PGW_TAG is missed")
+
+ if self.instance:
+ # We're good.
+ return
+
+ instance = self.make_instance()
+ self.instance = instance
+ super(TenantWithContainer, self).save()
+
+ def get_slice(self):
+ if not self.provider_service.slices.count():
+ raise XOSConfigurationError("The service has no slices")
+ slice = self.provider_service.slices.all()[0]
+ return slice
+
+ def make_instance(self):
+ slice = self.provider_service.slices.all()[0]
+ flavors = Flavor.objects.filter(name=slice.default_flavor)
+# flavors = Flavor.objects.filter(name="m1.xlarge")
+ if not flavors:
+ raise XOSConfigurationError("No default flavor")
+ default_flavor = slice.default_flavor
+ slice = self.provider_service.slices.all()[0]
+ if slice.default_isolation == "container_vm":
+ (node, parent) = ContainerVmScheduler(slice).pick()
+ else:
+ (node, parent) = LeastLoadedNodeScheduler(slice).pick()
+ instance = Instance(slice = slice,
+ node = node,
+ image = self.image,
+ creator = self.creator,
+ deployment = node.site_deployment.deployment,
+ flavor = flavors[0],
+ isolation = slice.default_isolation,
+ parent = parent)
+ self.save_instance(instance)
+ return instance
+
+ def ip_to_mac(self, ip):
+ (a, b, c, d) = ip.split('.')
+ return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
+
+ # Getter for the message that will appear on the webpage
+ # By default it is "Hello World!"
+ @property
+ def display_message(self):
+ return self.get_attribute(
+ "display_message",
+ self.default_attributes['display_message'])
+
+ @display_message.setter
+ def display_message(self, value):
+ self.set_attribute("display_message", value)
+
+ @property
+ def s5s8_pgw_tag(self):
+ return self.get_attribute(
+ "s5s8_pgw_tag",
+ self.default_attributes['s5s8_pgw_tag'])
+
+ @s5s8_pgw_tag.setter
+ def s5s8_pgw_tag(self, value):
+ self.set_attribute("s5s8_pgw_tag", value)
+
+
+ @property
+ def addresses(self):
+ if (not self.id) or (not self.instance):
+ return {}
+
+ addresses = {}
+ for ns in self.instance.ports.all():
+ if "s5s8_pgw" in ns.network.name.lower():
+ addresses["s5s8_pgw"] = (ns.ip, ns.mac)
+ return addresses
+
+
+ @property
+ def s5s8_pgw_ip(self):
+ return self.addresses.get("s5s8_pgw", (None, None))[0]
+ @property
+ def s5s8_pgw_mac(self):
+ return self.addresses.get("s5s8_pgw", (None, None))[1]
+
+def model_policy_mcord_servicecomponent(pk):
+ # This section of code is atomic to prevent race conditions
+ with transaction.atomic():
+ # We find all of the tenants that are waiting to update
+ component = VBBUComponent.objects.select_for_update().filter(pk=pk)
+ if not component:
+ return
+ # Since this code is atomic it is safe to always use the first tenant
+ component = component[0]
+ component.manage_container()
+
+
+def model_policy_mcord_servicecomponent(pk):
+ # This section of code is atomic to prevent race conditions
+ with transaction.atomic():
+ # We find all of the tenants that are waiting to update
+ component = VPGWCComponent.objects.select_for_update().filter(pk=pk)
+ if not component:
+ return
+ # Since this code is atomic it is safe to always use the first tenant
+ component = component[0]
+ component.manage_container()
diff --git a/xos/synchronizer/__init__.py b/xos/synchronizer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xos/synchronizer/__init__.py
diff --git a/xos/synchronizer/manifest b/xos/synchronizer/manifest
new file mode 100644
index 0000000..1eb3bf5
--- /dev/null
+++ b/xos/synchronizer/manifest
@@ -0,0 +1,9 @@
+model-deps
+vbbu-synchronizer.py
+vbbu_config
+steps/sync_vbbu.py
+steps/sync_vbbu.yaml
+stop.sh
+manifest
+__init__.py
+run.sh
diff --git a/xos/synchronizer/model-deps b/xos/synchronizer/model-deps
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/xos/synchronizer/model-deps
@@ -0,0 +1 @@
+{}
diff --git a/xos/synchronizer/run.sh b/xos/synchronizer/run.sh
new file mode 100644
index 0000000..27d4e24
--- /dev/null
+++ b/xos/synchronizer/run.sh
@@ -0,0 +1,3 @@
+# Runs the XOS observer using helloworldservice_config
+export XOS_DIR=/opt/xos
+python vbbu-synchronizer.py -C $XOS_DIR/synchronizers/vbbu/vbbu_config
diff --git a/xos/synchronizer/steps/sync_vbbu.py b/xos/synchronizer/steps/sync_vbbu.py
new file mode 100644
index 0000000..09b1fe8
--- /dev/null
+++ b/xos/synchronizer/steps/sync_vbbu.py
@@ -0,0 +1,37 @@
+import os
+import sys
+from django.db.models import Q, F
+from services.mcord.models import MCORDService, VBBUComponent
+from synchronizers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+
+parentdir = os.path.join(os.path.dirname(__file__), "..")
+sys.path.insert(0, parentdir)
+
+class SyncVBBUComponent(SyncInstanceUsingAnsible):
+
+ provides = [VBBUComponent]
+
+ observes = VBBUComponent
+
+ requested_interval = 0
+
+ template_name = "sync_vbbu.yaml"
+
+ service_key_name = "/opt/xos/configurations/mcord/mcord_private_key"
+
+ def __init__(self, *args, **kwargs):
+ super(SyncVBBUComponent, self).__init__(*args, **kwargs)
+
+ def fetch_pending(self, deleted):
+
+ if (not deleted):
+ objs = VBBUComponent.get_tenant_objects().filter(
+ Q(enacted__lt=F('updated')) | Q(enacted=None), Q(lazy_blocked=False))
+ else:
+
+ objs = VBBUComponent.get_deleted_tenant_objects()
+
+ return objs
+
+ def get_extra_attributes(self, o):
+ return {"display_message": o.display_message, "s1u_tag": o.s1u_tag, "s1mme_tag": o.s1mme_tag, "rru_tag": o.rru_tag}
diff --git a/xos/synchronizer/steps/sync_vbbu.yaml b/xos/synchronizer/steps/sync_vbbu.yaml
new file mode 100644
index 0000000..1e1e258
--- /dev/null
+++ b/xos/synchronizer/steps/sync_vbbu.yaml
@@ -0,0 +1,19 @@
+---
+- hosts: {{ instance_name }}
+ gather_facts: False
+ connection: ssh
+ user: ubuntu
+ sudo: yes
+ tasks:
+
+ - name: write message
+ shell: echo "{{ display_message }}" > /var/tmp/index.html
+
+ - name: setup s1u interface config
+ shell: ./start_3gpp_int.sh eth1 {{ s1u_tag }} {{ s1u_ip }}/24
+
+ - name: setup s1mme interface config
+ shell: ./start_3gpp_int.sh eth2 {{ s1mme_tag }} {{ s1mme_ip }}/24
+
+ - name: setup rru interface config
+ shell: ./start_3gpp_int.sh eth3 {{ rru_tag }} {{ rru_ip }}/24
diff --git a/xos/synchronizer/stop.sh b/xos/synchronizer/stop.sh
new file mode 100644
index 0000000..7ad7f4c
--- /dev/null
+++ b/xos/synchronizer/stop.sh
@@ -0,0 +1,2 @@
+# Kill the observer
+pkill -9 -f vbbu-synchronizer.py
diff --git a/xos/synchronizer/vbbu-synchronizer.py b/xos/synchronizer/vbbu-synchronizer.py
new file mode 100644
index 0000000..95f4081
--- /dev/null
+++ b/xos/synchronizer/vbbu-synchronizer.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+# This imports and runs ../../xos-observer.py
+# Runs the standard XOS observer
+
+import importlib
+import os
+import sys
+observer_path = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)), "../../synchronizers/base")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-synchronizer")
+mod.main()
diff --git a/xos/synchronizer/vbbu_config b/xos/synchronizer/vbbu_config
new file mode 100644
index 0000000..3713b67
--- /dev/null
+++ b/xos/synchronizer/vbbu_config
@@ -0,0 +1,40 @@
+# Required by XOS
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+# Required by XOS
+[api]
+nova_enabled=True
+
+# Sets options for the observer
+[observer]
+# Optional name
+name=vbbu
+# This is the location to the dependency graph you generate
+dependency_graph=/opt/xos/synchronizers/vbbu/model-deps
+# The location of your SyncSteps
+steps_dir=/opt/xos/synchronizers/vbbu/steps
+# A temporary directory that will be used by ansible
+sys_dir=/opt/xos/synchronizers/vbbu/sys
+# Location of the file to save logging messages to the backend log is often used
+logfile=/var/log/xos_backend.log
+# If this option is true, then nothing will change, we simply pretend to run
+pretend=False
+# If this is False then XOS will use an exponential backoff when the observer
+# fails, since we will be waiting for an instance, we don't want this.
+backoff_disabled=True
+# We want the output from ansible to be logged
+save_ansible_output=True
+# This determines how we SSH to a client, if this is set to True then we try
+# to ssh using the instance name as a proxy, if this is disabled we ssh using
+# the NAT IP of the instance. On CloudLab the first option will fail so we must
+# set this to False
+proxy_ssh=True
+proxy_ssh_key=/root/setup/id_rsa
+proxy_ssh_user=root
+[networking]
+use_vtn=True
diff --git a/xos/templates/mcordadmin.html b/xos/templates/mcordadmin.html
new file mode 100644
index 0000000..4a69dcc
--- /dev/null
+++ b/xos/templates/mcordadmin.html
@@ -0,0 +1,10 @@
+<!-- Template used to for the button leading to the HelloWorldTenantComplete form. -->
+<div class = "left-nav">
+ <ul>
+ <li>
+ <a href="/admin/mcordservice/vbbucomponent/">
+ MCORD Service Components
+ </a>
+ </li>
+ </ul>
+</div>
diff --git a/xos/tosca/resources/vbbucomponent.py b/xos/tosca/resources/vbbucomponent.py
new file mode 100644
index 0000000..ee60d0c
--- /dev/null
+++ b/xos/tosca/resources/vbbucomponent.py
@@ -0,0 +1,40 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from services.mcord.models import VBBUComponent, MCORDService
+
+from xosresource import XOSResource
+
+class XOSVBBUComponent(XOSResource):
+ provides = "tosca.nodes.VBBUComponent"
+ xos_model = VBBUComponent
+ copyin_props = ["s1u_tag", "s1mme_tag", "rru_tag", "display_message"]
+ name_field = None
+
+ def get_xos_args(self, throw_exception=True):
+ args = super(XOSVBBUComponent, self).get_xos_args()
+
+ provider_name = self.get_requirement("tosca.relationships.MemberOfService", throw_exception=throw_exception)
+ if provider_name:
+ args["provider_service"] = self.get_xos_object(MCORDService, throw_exception=throw_exception, name=provider_name)
+
+ return args
+
+ def get_existing_objs(self):
+ args = self.get_xos_args(throw_exception=False)
+ provider_service = args.get("provider", None)
+ if provider_service:
+ return [ self.get_xos_object(provider_service=provider_service) ]
+ return []
+
+ def postprocess(self, obj):
+ pass
+
+ def can_delete(self, obj):
+ return super(XOSVBBUComponent, self).can_delete(obj)
+
diff --git a/xos/vBBU-onboard.yaml b/xos/vBBU-onboard.yaml
new file mode 100644
index 0000000..4e1e597
--- /dev/null
+++ b/xos/vBBU-onboard.yaml
@@ -0,0 +1,27 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the vBBU service
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ servicecontroller#vBBU:
+ type: tosca.nodes.ServiceController
+ properties:
+ base_url: file:///opt/xos_services/vBBU/xos/
+ # The following will concatenate with base_url automatically, if
+ # base_url is non-null.
+ models: models.py
+ admin: admin.py
+ admin_template: templates/mcordadmin.html
+ synchronizer: synchronizer/manifest
+ synchronizer_run: vbbu-synchronizer.py
+ #tosca_custom_types: exampleservice.yaml
+ tosca_resource: tosca/resources/vbbucomponent.py
+ #rest_service: subdirectory:vsg api/service/vsg/vsgservice.py
+ #rest_tenant: subdirectory:cord api/tenant/cord/vsg.py
+ private_key: file:///opt/xos/key_import/vBBU_rsa
+ public_key: file:///opt/xos/key_import/vBBU_rsa.pub
+
diff --git a/xos/view.py b/xos/view.py
new file mode 100644
index 0000000..dc5f06a
--- /dev/null
+++ b/xos/view.py
@@ -0,0 +1,106 @@
+from django.http import HttpResponse
+from django.views.generic import TemplateView, View
+from django import template
+from core.models import *
+import json
+import os
+import time
+import tempfile
+
+
+class MCordView(TemplateView):
+ head_template = r"""{% extends "admin/dashboard/dashboard_base.html" %}
+ {% load admin_static %}
+ {% block content %}
+ """
+
+ tail_template = r"{% endblock %}"
+
+ def get(self, request, name="root", *args, **kwargs):
+ head_template = self.head_template
+ tail_template = self.tail_template
+
+ title = request.GET.get('service', '')
+ url = "/mcord/?service=%s" % (title)
+
+ form = """
+ <h2 class="content-title">Change %s Service</h2>
+ <div id="content-main">
+ <form class="form-horizontal">
+ <div class="tab-content tab-content-main">
+ <div class="suit-include suit-tab suit-tab-administration hide">
+ <div class="left-nav">
+ <ul>
+ <li><a href="/admin/ceilometer/monitoringchannel/">Monitoring Channels</a></li>
+ </ul>
+ </div>
+ </div>
+ <fieldset class="module aligned suit-tab suit-tab-general show">
+ <div class="panel fieldset-body">
+ <div class="form-group field-backend_status_text ">
+ <label class="control-label col-xs-12 col-sm-2"><label>Backend status text:</label></label>
+ <div class="form-column col-xs-12 col-sm-8 col-md-6 col-lg-4">
+ <p><img src="/static/admin/img/icon_clock.gif"> Pending sync, last_status = 0 - Provisioning in progress</p>
+ </div>
+ </div>
+ <div class="form-group field-name ">
+ <label class="control-label col-xs-12 col-sm-2"><label class="required" for="id_name">Name:</label></label>
+ <div class="form-column widget-AdminTextInputWidget col-xs-12 col-sm-8 col-md-6 col-lg-4">
+ <input class="vTextField form-control" id="id_name" maxlength="30" name="name" type="text" value="%s">
+ <div class="help-block">Service Name</div>
+ </div>
+ </div>
+ <div class="form-group field-enabled ">
+ <label class="control-label col-xs-12 col-sm-2"><label class="vCheckboxLabel" for="id_enabled">Enabled</label></label>
+ <div class="form-column widget-CheckboxInput col-xs-12 col-sm-8 col-md-6 col-lg-4">
+ <input checked="checked" id="id_enabled" name="enabled" type="checkbox">
+ </div>
+ </div>
+ <div class="form-group field-versionNumber ">
+ <label class="control-label col-xs-12 col-sm-2"><label class="required" for="id_versionNumber">VersionNumber:</label></label>
+ <div class="form-column widget-AdminTextInputWidget col-xs-12 col-sm-8 col-md-6 col-lg-4">
+ <input class="vTextField form-control" id="id_versionNumber" maxlength="30" name="versionNumber" type="text">
+ <div class="help-block">Version of Service Definition</div>
+ </div>
+ </div>
+ <div class="form-group field-description ">
+ <label class="control-label col-xs-12 col-sm-2"><label for="id_description">Description:</label></label>
+ <div class="form-column widget-AdminTextareaWidget col-xs-12 col-sm-8 col-md-6 col-lg-4">
+ <textarea class="vLargeTextField form-control" cols="40" id="id_description" maxlength="254" name="description" rows="10"></textarea>
+ <div class="help-block">Description of Service</div>
+ </div>
+ </div>
+ <div class="form-group field-view_url ">
+ <label class="control-label col-xs-12 col-sm-2"><label for="id_view_url">View url:</label></label>
+ <div class="form-column widget-AdminTextInputWidget col-xs-12 col-sm-8 col-md-6 col-lg-4">
+ <input class="vTextField form-control" id="id_view_url" maxlength="1024" name="view_url" type="text" value="%s">
+ </div>
+ </div>
+ <div class="form-group field-icon_url ">
+ <label class="control-label col-xs-12 col-sm-2"><label for="id_icon_url">Icon url:</label></label>
+ <div class="form-column widget-AdminTextInputWidget col-xs-12 col-sm-8 col-md-6 col-lg-4">
+ <input class="vTextField form-control" id="id_icon_url" maxlength="1024" name="icon_url" type="text">
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ <div class="form-buttons clearfix">
+ <button type="submit" class="btn btn-high btn-success" name="_save">Save</button>
+ <button type="submit" name="_continue" class=" btn btn-high btn-info">Save and continue editing</button>
+ <button type="submit" name="_addanother" class="btn btn-info">Save and add another</button>
+ <a href="delete/" class="text-error deletelink">Delete</a>
+ </div>
+ </div>
+ """ % (title, title, url)
+
+ t = template.Template(head_template + form + tail_template)
+
+ response_kwargs = {}
+ response_kwargs.setdefault('content_type', self.content_type)
+ return self.response_class(
+ request=request,
+ template=t,
+ **response_kwargs
+ )