resolve merge conflicts
diff --git a/Dockerfile.cord b/Dockerfile.cord
new file mode 100644
index 0000000..5a2074d
--- /dev/null
+++ b/Dockerfile.cord
@@ -0,0 +1,5 @@
+FROM xos:latest
+MAINTAINER Andy Bavier <acb@cs.princeton.edu>
+
+ADD xos/observers/vcpe/supervisor/vcpe-observer.conf /etc/supervisor/conf.d/
+RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/vcpe/vcpe_observer_config
diff --git a/cloudlab-init.sh b/cloudlab-init.sh
index 9810d16..b946249 100755
--- a/cloudlab-init.sh
+++ b/cloudlab-init.sh
@@ -1,23 +1,40 @@
#!/bin/bash
set -x
-# This script assumes that it is being run on the ctl node of the Tutorial-OpenStack
+# This script assumes that it is being run on the ctl node of the OpenStack
# profile on CloudLab.
XOS="http://ctl:9999/"
AUTH="padmin@vicci.org:letmein"
+CORD=0
+IMAGE="xos"
+
+# Create public key if none present
+[ -e ~/.ssh/id_rsa ] || cat /dev/zero | ssh-keygen -q -N ""
# Install Docker
-wget -qO- https://get.docker.com/ | sh
+which docker > /dev/null || wget -qO- https://get.docker.com/ | sh
sudo usermod -aG docker $(whoami)
sudo apt-get install httpie
+if [ "$CORD" -ne 0 ]
+then
+ cp ~/.ssh/id_rsa.pub xos/observers/vcpe/vcpe_public_key
+ cp ~/.ssh/id_rsa xos/observers/vcpe/vcpe_private_key
+fi
+
sudo docker build -t xos .
+if [ "$CORD" -ne 0 ]
+then
+ sudo docker build -t cord -f Dockerfile.cord .
+ IMAGE="cord"
+fi
+
# OpenStack is using port 8000...
MYIP=$( hostname -i )
-sudo docker run -d --add-host="ctl:$MYIP" -p 9999:8000 xos
+sudo docker run -d --add-host="ctl:$MYIP" -p 9999:8000 $IMAGE
echo "Waiting for XOS to come up"
until http $XOS &> /dev/null
@@ -25,12 +42,9 @@
sleep 1
done
-# Create public key if none present
-cat /dev/zero | ssh-keygen -q -N ""
-PUBKEY=$( cat ~/.ssh/id_rsa.pub )
-
# Copy public key
# BUG: Shouldn't have to set the 'enacted' field...
+PUBKEY=$( cat ~/.ssh/id_rsa.pub )
http --auth $AUTH PATCH $XOS/xos/users/1/ public_key="$PUBKEY" enacted=$( date "+%Y-%m-%dT%T")
# Set up controller
@@ -56,3 +70,9 @@
# BUG: Shouldn't have to set the controller_kind field, it's invalid in the initial fixture
FLATNET=$( sudo bash -c "source /root/setup/admin-openrc.sh ; neutron net-list" |grep flat|awk '{print $4}' )
http --auth $AUTH PATCH $XOS/xos/networktemplates/2/ shared_network_name=$FLATNET controller_kind=""
+
+if [ "$CORD" -ne 0 ]
+then
+ DOCKER=$( docker ps|grep $IMAGE|awk '{print $NF}' )
+ docker exec $DOCKER bash -c "cd /opt/xos/tosca; python run.py padmin@vicci.org samples/cord-cloudlab.yaml"
+fi
diff --git a/xos/ceilometer/__init__.py b/xos/ceilometer/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/ceilometer/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/ceilometer/admin.py b/xos/ceilometer/admin.py
new file mode 100644
index 0000000..8b6f469
--- /dev/null
+++ b/xos/ceilometer/admin.py
@@ -0,0 +1,83 @@
+from django.contrib import admin
+
+from ceilometer.models import *
+from django import forms
+from django.utils.safestring import mark_safe
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.signals import user_logged_in
+from django.utils import timezone
+from django.contrib.contenttypes import generic
+from suit.widgets import LinkedSelect
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, ServicePrivilegeInline, TenantRootTenantInline, TenantRootPrivilegeInline
+
+from functools import update_wrapper
+from django.contrib.admin.views.main import ChangeList
+from django.core.urlresolvers import reverse
+from django.contrib.admin.utils import quote
+
+class CeilometerServiceAdmin(ReadOnlyAwareAdmin):
+ model = CeilometerService
+ verbose_name = "Ceilometer Service"
+ verbose_name_plural = "Ceilometer Service"
+ list_display = ("backend_status_icon", "name", "enabled")
+ list_display_links = ('backend_status_icon', 'name', )
+ fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url" ], 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', )
+ inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+
+ extracontext_registered_admins = True
+
+ user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
+
+ suit_form_tabs =(('general', 'Ceilometer Service Details'),
+ ('administration', 'Administration'),
+ ('slices','Slices'),
+ ('serviceattrs','Additional Attributes'),
+ ('serviceprivileges','Privileges'),
+ )
+
+ suit_form_includes = (('ceilometeradmin.html', 'top', 'administration'),
+ )
+
+ def queryset(self, request):
+ return CeilometerService.get_service_objects_by_user(request.user)
+
+class MonitoringChannelForm(forms.ModelForm):
+ creator = forms.ModelChoiceField(queryset=User.objects.all())
+
+ def __init__(self,*args,**kwargs):
+ super (MonitoringChannelForm,self ).__init__(*args,**kwargs)
+ self.fields['kind'].default = CEILOMETER_KIND
+ self.fields['kind'].widget.attrs['readonly'] = True
+ self.fields['provider_service'].queryset = CeilometerService.get_service_objects().all()
+ if self.instance:
+ # fields for the attributes
+ self.fields['creator'].initial = self.instance.creator
+
+ def save(self, commit=True):
+ self.instance.creator = self.cleaned_data.get("creator")
+ return super(MonitoringChannelForm, self).save(commit=commit)
+
+ class Meta:
+ model = MonitoringChannel
+
+class MonitoringChannelAdmin(ReadOnlyAwareAdmin):
+ list_display = ('backend_status_icon', 'id', )
+ list_display_links = ('backend_status_icon', 'id')
+ fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', 'service_specific_attribute',
+ 'sliver',
+ 'creator'],
+ 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', 'sliver', 'service_specific_attribute')
+ form = MonitoringChannelForm
+
+ suit_form_tabs = (('general','Details'),)
+
+ def queryset(self, request):
+ return MonitoringChannel.get_tenant_objects_by_user(request.user)
+
+admin.site.register(CeilometerService, CeilometerServiceAdmin)
+admin.site.register(MonitoringChannel, MonitoringChannelAdmin)
+
diff --git a/xos/ceilometer/models.py b/xos/ceilometer/models.py
new file mode 100644
index 0000000..2b2e990
--- /dev/null
+++ b/xos/ceilometer/models.py
@@ -0,0 +1,60 @@
+from django.db import models
+from core.models import Service, PlCoreBase, Slice, Sliver, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber
+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
+import traceback
+from xos.exceptions import *
+
+CEILOMETER_KIND = "ceilometer"
+
+class CeilometerService(Service):
+ KIND = CEILOMETER_KIND
+
+ class Meta:
+ app_label = "ceilometer"
+ verbose_name = "Ceilometer Service"
+ proxy = True
+
+class MonitoringChannel(TenantWithContainer): # aka 'CeilometerTenant'
+ class Meta:
+ proxy = True
+
+ KIND = CEILOMETER_KIND
+
+ default_attributes = {}
+ def __init__(self, *args, **kwargs):
+ ceilometer_services = CeilometerService.get_service_objects().all()
+ if ceilometer_services:
+ self._meta.get_field("provider_service").default = ceilometer_services[0].id
+ super(MonitoringChannel, self).__init__(*args, **kwargs)
+
+ def save(self, *args, **kwargs):
+ if not self.creator:
+ if not getattr(self, "caller", None):
+ # caller must be set when creating a vCPE since it creates a slice
+ raise XOSProgrammingError("MonitoringChannel's self.caller was not set")
+ self.creator = self.caller
+ if not self.creator:
+ raise XOSProgrammingError("MonitoringChannel's self.creator was not set")
+
+ super(MonitoringChannel, self).save(*args, **kwargs)
+ model_policy_monitoring_channel(self.pk)
+
+ def delete(self, *args, **kwargs):
+ self.cleanup_container()
+ super(MonitoringChannel, self).delete(*args, **kwargs)
+
+def model_policy_monitoring_channel(pk):
+ # TODO: this should be made in to a real model_policy
+ with transaction.atomic():
+ mc = MonitoringChannel.objects.select_for_update().filter(pk=pk)
+ if not mc:
+ return
+ mc = mc[0]
+ mc.manage_container()
+
+
diff --git a/xos/ceilometer/templates/ceilometeradmin.html b/xos/ceilometer/templates/ceilometeradmin.html
new file mode 100644
index 0000000..8149666
--- /dev/null
+++ b/xos/ceilometer/templates/ceilometeradmin.html
@@ -0,0 +1,6 @@
+<div class = "left-nav">
+<ul>
+<li><a href="/admin/ceilometer/monitoringchannel/">Monitoring Channels</a></li>
+</ul>
+</div>
+
diff --git a/xos/cord/models.py b/xos/cord/models.py
index f94befc..68bd2ba 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -1,5 +1,5 @@
from django.db import models
-from core.models import Service, PlCoreBase, Slice, Instance, Tenant, Node, Image, User, Flavor, Subscriber
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber
from core.models.plcorebase import StrippedCharField
import os
from django.db import models, transaction
@@ -457,18 +457,17 @@
VCPEService.setup_simple_attributes()
-class VCPETenant(Tenant):
+class VCPETenant(TenantWithContainer):
class Meta:
proxy = True
KIND = VCPE_KIND
- sync_attributes = ("nat_ip",
- "lan_ip",
- "wan_ip",
- "private_ip",
- "hpc_client_ip",
- "wan_mac")
+ sync_attributes = ("nat_ip", "nat_mac",
+ "lan_ip", "lan_mac",
+ "wan_ip", "wan_mac",
+ "private_ip", "private_mac",
+ "hpc_client_ip", "hpc_client_mac")
default_attributes = {"instance_id": None,
"users": [],
@@ -599,50 +598,58 @@
return {}
addresses = {}
- for ns in self.sliver.ports.all():
+ for ns in self.instance.ports.all():
if "lan" in ns.network.name.lower():
- addresses["lan"] = ns.ip
+ addresses["lan"] = (ns.ip, ns.mac)
elif "wan" in ns.network.name.lower():
- addresses["wan"] = ns.ip
+ addresses["wan"] = (ns.ip, ns.mac)
elif "private" in ns.network.name.lower():
- addresses["private"] = ns.ip
+ addresses["private"] = (ns.ip, ns.mac)
elif "nat" in ns.network.name.lower():
- addresses["nat"] = ns.ip
+ addresses["nat"] = (ns.ip, ns.mac)
elif "hpc_client" in ns.network.name.lower():
- addresses["hpc_client"] = ns.ip
+ addresses["hpc_client"] = (ns.ip, ns.mac)
return addresses
@property
def nat_ip(self):
- return self.addresses.get("nat",None)
+ return self.addresses.get("nat", (None,None) )[0]
+
+ @property
+ def nat_mac(self):
+ return self.addresses.get("nat", (None,None) )[1]
@property
def lan_ip(self):
- return self.addresses.get("lan",None)
+ return self.addresses.get("lan", (None, None) )[0]
+
+ @property
+ def lan_mac(self):
+ return self.addresses.get("lan", (None, None) )[1]
@property
def wan_ip(self):
- return self.addresses.get("wan",None)
+ return self.addresses.get("wan", (None, None) )[0]
@property
def wan_mac(self):
- ip = self.wan_ip
- if not ip:
- return None
- try:
- (a,b,c,d) = ip.split('.')
- wan_mac = "02:42:%2x:%2x:%2x:%2x" % (int(a), int(b), int(c), int(d))
- except:
- wan_mac = "Exception"
- return wan_mac
+ return self.addresses.get("wan", (None, None) )[1]
@property
def private_ip(self):
- return self.addresses.get("private",None)
+ return self.addresses.get("private", (None, None) )[0]
+
+ @property
+ def private_mac(self):
+ return self.addresses.get("private", (None, None) )[1]
@property
def hpc_client_ip(self):
- return self.addresses.get("hpc_client",None)
+ return self.addresses.get("hpc_client", (None, None) )[0]
+
+ @property
+ def hpc_client_mac(self):
+ return self.addresses.get("hpc_client", (None, None) )[1]
@property
def is_synced(self):
@@ -767,7 +774,7 @@
def delete(self, *args, **kwargs):
self.cleanup_vbng()
- self.cleanup_instance()
+ self.cleanup_container()
super(VCPETenant, self).delete(*args, **kwargs)
def model_policy_vcpe(pk):
@@ -777,7 +784,7 @@
if not vcpe:
return
vcpe = vcpe[0]
- vcpe.manage_instance()
+ vcpe.manage_container()
vcpe.manage_vbng()
vcpe.manage_bbs_account()
vcpe.cleanup_orphans()
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 2dae51b..d9f285b 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1273,8 +1273,8 @@
user_readonly_inlines = []
class InstancePortInline(XOSTabularInline):
- fields = ['backend_status_icon', 'network', 'instance', 'ip']
- readonly_fields = ("backend_status_icon", "ip", )
+ fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
+ readonly_fields = ("backend_status_icon", "ip", "mac")
model = Port
selflink_fieldname = "network"
extra = 0
@@ -1701,8 +1701,8 @@
readonly_fields = ('backend_status_icon', )
class NetworkPortInline(XOSTabularInline):
- fields = ['backend_status_icon', 'network', 'instance', 'ip']
- readonly_fields = ("backend_status_icon", "ip", )
+ fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
+ readonly_fields = ("backend_status_icon", "ip", "mac")
model = Port
selflink_fieldname = "instance"
extra = 0
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index 4a2051f..0efe37b 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -1,7 +1,7 @@
from .plcorebase import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,PlModelMixIn
from .project import Project
from .singletonmodel import SingletonModel
-from .service import Service, Tenant, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, Subscriber, Provider
+from .service import Service, Tenant, TenantWithContainer, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, Subscriber, Provider
from .service import ServiceAttribute
from .tag import Tag
from .role import Role
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index f7032b0..dfda072 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -219,6 +219,7 @@
instance = models.ForeignKey(Instance, null=True, blank=True, related_name='networkinstances')
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="Quantum port id")
+ mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
class Meta:
unique_together = ('network', 'instance')
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 6eed241..b69acf1 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -332,6 +332,130 @@
return None
return sorted(st, key=attrgetter('id'))[0]
+class TenantWithContainer(Tenant):
+ """ A tenant that manages a container """
+
+ # this is a hack and should be replaced by something smarter...
+ LOOK_FOR_IMAGES=["ubuntu-vcpe4", # ONOS demo machine -- preferred vcpe image
+ "Ubuntu 14.04 LTS", # portal
+ "Ubuntu-14.04-LTS", # ONOS demo machine
+ "trusty-server-multi-nic", # CloudLab
+ ]
+
+ class Meta:
+ proxy = True
+
+ def __init__(self, *args, **kwargs):
+ super(TenantWithContainer, self).__init__(*args, **kwargs)
+ self.cached_sliver=None
+ self.orig_sliver_id = self.get_initial_attribute("sliver_id")
+
+ @property
+ def sliver(self):
+ from core.models import Sliver
+ if getattr(self, "cached_sliver", None):
+ return self.cached_sliver
+ sliver_id=self.get_attribute("sliver_id")
+ if not sliver_id:
+ return None
+ slivers=Sliver.objects.filter(id=sliver_id)
+ if not slivers:
+ return None
+ sliver=slivers[0]
+ sliver.caller = self.creator
+ self.cached_sliver = sliver
+ return sliver
+
+ @sliver.setter
+ def sliver(self, value):
+ if value:
+ value = value.id
+ if (value != self.get_attribute("sliver_id", None)):
+ self.cached_sliver=None
+ self.set_attribute("sliver_id", 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)
+
+ @property
+ def image(self):
+ from core.models import Image
+ # Implement the logic here to pick the image that should be used when
+ # instantiating the VM that will hold the container.
+ for image_name in self.LOOK_FOR_IMAGES:
+ images = Image.objects.filter(name = image_name)
+ if images:
+ return images[0]
+
+ raise XOSProgrammingError("No VPCE image (looked for %s)" % str(self.LOOK_FOR_IMAGES))
+
+ def pick_node(self):
+ from core.models import Node
+ nodes = list(Node.objects.all())
+ # TODO: logic to filter nodes by which nodes are up, and which
+ # nodes the slice can instantiate on.
+ nodes = sorted(nodes, key=lambda node: node.slivers.all().count())
+ return nodes[0]
+
+ def manage_container(self):
+ from core.models import Sliver, Flavor
+
+ if self.deleted:
+ return
+
+ if (self.sliver is not None) and (self.sliver.image != self.image):
+ self.sliver.delete()
+ self.sliver = None
+
+ if self.sliver is None:
+ if not self.provider_service.slices.count():
+ raise XOSConfigurationError("The VCPE service has no slices")
+
+ flavors = Flavor.objects.filter(name="m1.small")
+ if not flavors:
+ raise XOSConfigurationError("No m1.small flavor")
+
+ node =self.pick_node()
+ sliver = Sliver(slice = self.provider_service.slices.all()[0],
+ node = node,
+ image = self.image,
+ creator = self.creator,
+ deployment = node.site_deployment.deployment,
+ flavor = flavors[0])
+ sliver.save()
+
+ try:
+ self.sliver = sliver
+ super(TenantWithContainer, self).save()
+ except:
+ sliver.delete()
+ raise
+
+ def cleanup_container(self):
+ if self.sliver:
+ # print "XXX cleanup sliver", self.sliver
+ self.sliver.delete()
+ self.sliver = None
class CoarseTenant(Tenant):
""" TODO: rename "CoarseTenant" --> "StaticTenant" """
diff --git a/xos/observers/vcpe/files/vm-resolv.conf b/xos/observers/vcpe/files/vm-resolv.conf
new file mode 100644
index 0000000..cae093a
--- /dev/null
+++ b/xos/observers/vcpe/files/vm-resolv.conf
@@ -0,0 +1 @@
+nameserver 8.8.8.8
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.py b/xos/observers/vcpe/steps/sync_vcpetenant.py
index 7ce3eaa..1a45b54 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.py
@@ -116,10 +116,16 @@
if o.volt:
vlan_ids.append(o.volt.vlan_id)
+ try:
+ full_setup = Config().observer_full_setup
+ except:
+ full_setup = True
+
fields = {"vlan_ids": vlan_ids,
"dnsdemux_ip": dnsdemux_ip,
"cdn_prefixes": cdn_prefixes,
- "bbs_addrs": bbs_addrs}
+ "bbs_addrs": bbs_addrs,
+ "full_setup": full_setup}
# add in the sync_attributes that come from the SubscriberRoot object
@@ -203,4 +209,3 @@
def delete_record(self, m):
pass
-
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.yaml b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
index cb8f92b..063ab06 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
@@ -26,11 +26,15 @@
- {{ bbs_addr }}
{% endfor %}
nat_ip: {{ nat_ip }}
+ nat_mac: {{ nat_mac }}
lan_ip: {{ lan_ip }}
+ lan_mac: {{ lan_mac }}
wan_ip: {{ wan_ip }}
- private_ip: {{ private_ip }}
- hpc_client_ip: {{ hpc_client_ip }}
wan_mac: {{ wan_mac }}
+ private_ip: {{ private_ip }}
+ private_mac: {{ private_mac }}
+ hpc_client_ip: {{ hpc_client_ip }}
+ hpc_client_mac: {{ hpc_client_mac }}
tasks:
{% if full_setup %}
@@ -67,7 +71,7 @@
shell: rm -f /etc/resolv.conf
- name: Install resolv.conf
- copy src=/opt/xos/observers/vcpe/files/vm-resolv.conf
+ copy: src=/opt/xos/observers/vcpe/files/vm-resolv.conf
dest=/etc/resolv.conf
{% endif %}
diff --git a/xos/observers/vcpe/templates/start-vcpe.sh.j2 b/xos/observers/vcpe/templates/start-vcpe.sh.j2
index 565adaf..7e714de 100755
--- a/xos/observers/vcpe/templates/start-vcpe.sh.j2
+++ b/xos/observers/vcpe/templates/start-vcpe.sh.j2
@@ -1,5 +1,15 @@
#!/bin/bash
+function mac_to_iface {
+ MAC=$1
+ ifconfig|grep $MAC| awk '{print $1}'|grep -v '\.'
+}
+
+function generate_mac_from_ip {
+ IP=$1
+ printf "02:42:%02x:%02x:%02x:%02x\n" `echo $IP|awk -F '.' '{print $1, $2, $3, $4}'`
+}
+
iptables -L > /dev/null
ip6tables -L > /dev/null
@@ -15,12 +25,18 @@
fi
# Set up networking via pipework
-docker exec $VCPE ifconfig eth0 >> /dev/null || pipework eth4 -i eth0 $VCPE {{ wan_ip }}/17@192.168.128.1 {{ wan_mac }}
-docker exec $VCPE ifconfig eth1 >> /dev/null || pipework eth3 -i eth1 $VCPE 192.168.0.1/24 @{{ vlan_ids[0] }}
-docker exec $VCPE ifconfig eth2 >> /dev/null || pipework eth0 -i eth2 $VCPE {{ hpc_client_ip }}/24
+WAN_CONTAINER_MAC=$( generate_mac_from_ip {{ wan_ip }} )
+WAN_IFACE=$( mac_to_iface {{ wan_mac }} )
+docker exec $VCPE ifconfig eth0 >> /dev/null || pipework $WAN_IFACE -i eth0 $VCPE {{ wan_ip }}/17@192.168.128.1 $WAN_CONTAINER_MAC
+
+LAN_IFACE=$( mac_to_iface {{ lan_mac }} )
+docker exec $VCPE ifconfig eth1 >> /dev/null || pipework $LAN_IFACE -i eth1 $VCPE 192.168.0.1/24 @{{ vlan_ids[0] }}
+
+HPC_IFACE=$( mac_to_iface {{ hpc_client_mac }} )
+docker exec $VCPE ifconfig eth2 >> /dev/null || pipework $HPC_IFACE -i eth2 $VCPE {{ hpc_client_ip }}/24
# Make sure VM's eth0 (hpc_client) has no IP address
-ifconfig eth0 0.0.0.0
+ifconfig $HPC_IFACE 0.0.0.0
# Now can start up dnsmasq
docker exec $VCPE service dnsmasq start
diff --git a/xos/observers/vcpe/vcpe_observer_config b/xos/observers/vcpe/vcpe_observer_config
index 6d58340..afd1501 100644
--- a/xos/observers/vcpe/vcpe_observer_config
+++ b/xos/observers/vcpe/vcpe_observer_config
@@ -33,6 +33,8 @@
pretend=False
backoff_disabled=True
save_ansible_output=True
+proxy_ssh=True
+full_setup=True
[feefie]
client_id='vicci_dev_central'
diff --git a/xos/openstack_observer/ansible.py b/xos/openstack_observer/ansible.py
index 74af590..fad7610 100755
--- a/xos/openstack_observer/ansible.py
+++ b/xos/openstack_observer/ansible.py
@@ -109,7 +109,7 @@
except:
# fail silently
pass
-
+
else:
msg = open(fqp+'.out').read()
@@ -139,28 +139,40 @@
instance_name = opts["instance_name"]
hostname = opts["hostname"]
private_key = opts["private_key"]
+ nat_ip = opts["nat_ip"]
+
+ try:
+ proxy_ssh = Config().observer_proxy_ssh
+ except:
+ proxy_ssh = True
(opts, fqp) = get_playbook_fn(opts, path)
private_key_pathname = fqp + ".key"
config_pathname = fqp + ".config"
hosts_pathname = fqp + ".hosts"
- proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
-
f = open(private_key_pathname, "w")
f.write(private_key)
f.close()
f = open(config_pathname, "w")
f.write("[ssh_connection]\n")
- f.write('ssh_args = -o "%s" -o StrictHostKeyChecking=no\n' % proxy_command)
+ if proxy_ssh:
+ proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
+ f.write('ssh_args = -o "%s"\n' % proxy_command)
f.write('scp_if_ssh = True\n')
f.write('pipelining = True\n')
+ f.write('\n[defaults]\n')
+ f.write('host_key_checking = False\n')
f.close()
f = open(hosts_pathname, "w")
f.write("[%s]\n" % instance_name)
- f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
+ if proxy_ssh:
+ f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
+ else:
+ # acb: Login user is hardcoded, this is not great
+ f.write("%s ansible_ssh_private_key_file=%s ansible_ssh_user=ubuntu\n" % (nat_ip, private_key_pathname))
f.close()
# SSH will complain if private key is world or group readable
diff --git a/xos/openstack_observer/steps/sync_controller_slices.py b/xos/openstack_observer/steps/sync_controller_slices.py
index 0eceb95..c456a2f 100644
--- a/xos/openstack_observer/steps/sync_controller_slices.py
+++ b/xos/openstack_observer/steps/sync_controller_slices.py
@@ -60,7 +60,7 @@
if (not controller_slice.tenant_id):
try:
driver = OpenStackDriver().admin_driver(controller=controller_slice.controller)
- driver.shell.nova.quotas.update(tenant_id=controller_slice.tenant_id, instances=int(controller_slice.slice.max_instances))
+ driver.shell.nova.quotas.update(tenant_id=tenant_id, instances=int(controller_slice.slice.max_instances))
except:
logger.log_exc('Could not update quota for %s'%controller_slice.slice.name)
raise Exception('Could not update quota for %s'%controller_slice.slice.name)
diff --git a/xos/openstack_observer/steps/sync_ports.py b/xos/openstack_observer/steps/sync_ports.py
index da08e36..15fd4a1 100644
--- a/xos/openstack_observer/steps/sync_ports.py
+++ b/xos/openstack_observer/steps/sync_ports.py
@@ -127,11 +127,13 @@
continue
ip=port["fixed_ips"][0]["ip_address"]
+ mac=port["mac_address"]
logger.info("creating Port (%s, %s, %s, %s)" % (str(network), str(instance), ip, str(port["id"])))
ns = Port(network=network,
instance=instance,
ip=ip,
+ mac=mac,
port_id=port["id"])
try:
diff --git a/xos/scripts/opencloud b/xos/scripts/opencloud
index c4cb2d6..6705b20 100755
--- a/xos/scripts/opencloud
+++ b/xos/scripts/opencloud
@@ -146,6 +146,7 @@
python ./manage.py makemigrations requestrouter
python ./manage.py makemigrations syndicate_storage
python ./manage.py makemigrations cord
+ python ./manage.py makemigrations ceilometer
#python ./manage.py makemigrations servcomp
}
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 9ba9973..0a68c9d 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -13,6 +13,7 @@
type: boolean
default: false
description: do not allow Tosca to create this object)
+# Service
define(xos_base_service_caps,
scalable:
type: tosca.capabilities.Scalable
@@ -36,6 +37,27 @@
default: true
public_key:
type: string
+ required: false
+ versionNumber:
+ type: string
+ required: false)
+# Subscriber
+define(xos_base_subscriber_caps,
+ subscriber:
+ type: tosca.capabilities.xos.Subscriber)
+define(xos_base_subscriber_props,
+ kind:
+ type: string
+ default: generic
+ service_specific_id:
+ type: string
+ required: false)
+define(xos_base_tenant_props,
+ kind:
+ type: string
+ default: generic
+ service_specific_id:
+ type: string
required: false)
# end m4 macros
@@ -77,6 +99,47 @@
properties:
xos_base_service_props
+ tosca.nodes.Subscriber:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ xos_base_subscriber_caps
+ properties:
+ xos_base_subscriber_props
+
+ tosca.nodes.CORDSubscriber:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ xos_base_subscriber_caps
+ properties:
+ xos_base_subscriber_props
+ firewall_enable:
+ type: boolean
+ default: false
+ url_filter_enable:
+ type: boolean
+ default: false
+ url_filter_level:
+ type: string
+ default: PG
+ cdn_enable:
+ type: boolean
+ default: true
+
+ tosca.nodes.CORDUser:
+ derived_from: tosca.nodes.Root
+ properties:
+ level:
+ type: string
+ default: PG_13
+ mac:
+ type: string
+ required: true
+
+ tosca.nodes.VOLTTenant:
+ derived_from: tosca.nodes.Root
+ properties:
+ xos_base_tenant_props
+
tosca.nodes.User:
derived_from: tosca.nodes.Root
@@ -222,7 +285,7 @@
# In the data model, this is defaulted to false. However, to
# preserve Tosca semantics, we default it to true instead.
default: true
- capabilities:
+ capabilities:
link:
type: tosca.capabilities.network.Linkable
@@ -346,6 +409,9 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Network ]
+ tosca.relationships.ConnectsToSlice:
+ derived_from: tosca.relationships.Root
+
# tosca.relationships.OwnsNetwork:
# derived_from: tosca.relationships.Root
# valid_target_types: [ tosca.capabilities.xos.Network ]
@@ -370,6 +436,12 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabiltys.xos.Site ]
+ tosca.relationships.SubscriberDevice:
+ derived_from: tosca.relationships.Root
+
+ tosca.relationships.BelongsToSubscriber:
+ derived_from: tosca.relationships.Root
+
tosca.capabilities.xos.Service:
derived_from: tosca.capabilities.Root
description: An XOS Service
@@ -401,3 +473,8 @@
tosca.capabilities.xos.User:
derived_from: tosca.capabilities.Root
description: An XOS user
+
+ tosca.capabilities.xos.Subscriber:
+ derived_from: tosca.capabilities.Root
+ description: An XOS Subscriber
+
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 3a7e133..c59842f 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -5,6 +5,11 @@
# m4 macros do our inheritance
+# Service
+
+
+# Subscriber
+
@@ -21,12 +26,27 @@
service:
type: tosca.capabilities.xos.Service
properties:
+ kind:
+ type: string
+ default: generic
view_url:
type: string
required: false
icon_url:
type: string
required: false
+ enabled:
+ type: boolean
+ default: true
+ published:
+ type: boolean
+ default: true
+ public_key:
+ type: string
+ required: false
+ versionNumber:
+ type: string
+ required: false
tosca.nodes.VCPEService:
derived_from: tosca.nodes.Root
@@ -36,12 +56,27 @@
service:
type: tosca.capabilities.xos.Service
properties:
+ kind:
+ type: string
+ default: generic
view_url:
type: string
required: false
icon_url:
type: string
required: false
+ enabled:
+ type: boolean
+ default: true
+ published:
+ type: boolean
+ default: true
+ public_key:
+ type: string
+ required: false
+ versionNumber:
+ type: string
+ required: false
backend_network_label:
type: string
required: false
@@ -54,12 +89,27 @@
service:
type: tosca.capabilities.xos.Service
properties:
+ kind:
+ type: string
+ default: generic
view_url:
type: string
required: false
icon_url:
type: string
required: false
+ enabled:
+ type: boolean
+ default: true
+ published:
+ type: boolean
+ default: true
+ public_key:
+ type: string
+ required: false
+ versionNumber:
+ type: string
+ required: false
vbng_url:
type: string
required: false
@@ -72,12 +122,85 @@
service:
type: tosca.capabilities.xos.Service
properties:
+ kind:
+ type: string
+ default: generic
view_url:
type: string
required: false
icon_url:
type: string
required: false
+ enabled:
+ type: boolean
+ default: true
+ published:
+ type: boolean
+ default: true
+ public_key:
+ type: string
+ required: false
+ versionNumber:
+ type: string
+ required: false
+
+ tosca.nodes.Subscriber:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ subscriber:
+ type: tosca.capabilities.xos.Subscriber
+ properties:
+ kind:
+ type: string
+ default: generic
+ service_specific_id:
+ type: string
+ required: false
+
+ tosca.nodes.CORDSubscriber:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ subscriber:
+ type: tosca.capabilities.xos.Subscriber
+ properties:
+ kind:
+ type: string
+ default: generic
+ service_specific_id:
+ type: string
+ required: false
+ firewall_enable:
+ type: boolean
+ default: false
+ url_filter_enable:
+ type: boolean
+ default: false
+ url_filter_level:
+ type: string
+ default: PG
+ cdn_enable:
+ type: boolean
+ default: true
+
+ tosca.nodes.CORDUser:
+ derived_from: tosca.nodes.Root
+ properties:
+ level:
+ type: string
+ default: PG_13
+ mac:
+ type: string
+ required: true
+
+ tosca.nodes.VOLTTenant:
+ derived_from: tosca.nodes.Root
+ properties:
+ kind:
+ type: string
+ default: generic
+ service_specific_id:
+ type: string
+ required: false
tosca.nodes.User:
derived_from: tosca.nodes.Root
@@ -224,7 +347,7 @@
# In the data model, this is defaulted to false. However, to
# preserve Tosca semantics, we default it to true instead.
default: true
- capabilities:
+ capabilities:
link:
type: tosca.capabilities.network.Linkable
@@ -348,6 +471,9 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Network ]
+ tosca.relationships.ConnectsToSlice:
+ derived_from: tosca.relationships.Root
+
# tosca.relationships.OwnsNetwork:
# derived_from: tosca.relationships.Root
# valid_target_types: [ tosca.capabilities.xos.Network ]
@@ -372,6 +498,12 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabiltys.xos.Site ]
+ tosca.relationships.SubscriberDevice:
+ derived_from: tosca.relationships.Root
+
+ tosca.relationships.BelongsToSubscriber:
+ derived_from: tosca.relationships.Root
+
tosca.capabilities.xos.Service:
derived_from: tosca.capabilities.Root
description: An XOS Service
@@ -403,3 +535,8 @@
tosca.capabilities.xos.User:
derived_from: tosca.capabilities.Root
description: An XOS user
+
+ tosca.capabilities.xos.Subscriber:
+ derived_from: tosca.capabilities.Root
+ description: An XOS Subscriber
+
diff --git a/xos/tosca/engine.py b/xos/tosca/engine.py
index 3efb5ef..efce829 100644
--- a/xos/tosca/engine.py
+++ b/xos/tosca/engine.py
@@ -44,7 +44,7 @@
self.ordered_nodetemplates = []
self.ordered_names = self.topsort_dependencies()
- print "ordered_names", self.ordered_names
+ self.log("ordered_names: %s" % self.ordered_names)
for name in self.ordered_names:
if name in self.nodetemplates_by_name:
self.ordered_nodetemplates.append(self.nodetemplates_by_name[name])
diff --git a/xos/tosca/flavorselect.py b/xos/tosca/flavorselect.py
index a79f8a5..cab8e56 100644
--- a/xos/tosca/flavorselect.py
+++ b/xos/tosca/flavorselect.py
@@ -18,7 +18,11 @@
return int(s)
def get_mb(self, s):
- return self.get_gb(s) * 1024
+ if "GB" in s:
+ return int(s.split("GB")[0].strip())*1024
+ if "MB" in s:
+ return int(s.split("MB")[0].strip())
+ return int(s)
def get_flavor(self):
flavor = "m1.tiny"
diff --git a/xos/tosca/resources/CORDSubscriber.py b/xos/tosca/resources/CORDSubscriber.py
new file mode 100644
index 0000000..45f5ee3
--- /dev/null
+++ b/xos/tosca/resources/CORDSubscriber.py
@@ -0,0 +1,24 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import User
+from cord.models import CordSubscriberRoot
+
+from xosresource import XOSResource
+
+class XOSCORDSubscriber(XOSResource):
+ provides = "tosca.nodes.CORDSubscriber"
+ xos_model = CordSubscriberRoot
+ copyin_props = ["service_specific_id", "firewall_enable", "url_filter_enable", "cdn_enable", "url_filter_level"]
+
+ def postprocess(self, obj):
+ pass
+
+ def can_delete(self, obj):
+ return super(XOSCORDSubscriber, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/CORDUser.py b/xos/tosca/resources/CORDUser.py
new file mode 100644
index 0000000..78883f2
--- /dev/null
+++ b/xos/tosca/resources/CORDUser.py
@@ -0,0 +1,63 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import User
+from cord.models import CordSubscriberRoot
+
+from xosresource import XOSResource
+
+class XOSCORDUser(XOSResource):
+ provides = "tosca.nodes.CORDUser"
+
+ def get_model_class_name(self):
+ return "CORDUser"
+
+ def get_subscriber_root(self, throw_exception=True):
+ sub_name = self.get_requirement("tosca.relationships.SubscriberDevice", throw_exception=throw_exception)
+ sub = self.get_xos_object(CordSubscriberRoot, name=sub_name, throw_exception=throw_exception)
+ return sub
+
+ def get_existing_objs(self):
+ result = []
+ sub = self.get_subscriber_root(throw_exception=False)
+ if not sub:
+ return []
+ for user in sub.users:
+ if user["name"] == self.nodetemplate.name:
+ result.append(user)
+ return result
+
+ def get_xos_args(self):
+ args = {"name": self.nodetemplate.name,
+ "level": self.get_property("level"),
+ "mac": self.get_property("mac")}
+ return args
+
+
+ def create(self):
+ xos_args = self.get_xos_args()
+ sub = self.get_subscriber_root()
+
+ sub.create_user(**xos_args)
+ sub.save()
+
+ self.info("Created CORDUser %s for Subscriber %s" % (self.nodetemplate.name, sub.name))
+
+ def update(self, obj):
+ pass
+
+ def delete(self, obj):
+ if (self.can_delete(obj)):
+ self.info("destroying CORDUser %s" % obj["name"])
+ sub = self.get_subscriber_root()
+ sub.delete_user(obj["id"])
+ sub.save()
+
+ def can_delete(self, obj):
+ return True
+
diff --git a/xos/tosca/resources/VOLTTenant.py b/xos/tosca/resources/VOLTTenant.py
new file mode 100644
index 0000000..611bf23
--- /dev/null
+++ b/xos/tosca/resources/VOLTTenant.py
@@ -0,0 +1,46 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import User
+from cord.models import VOLTTenant, VOLTService, CordSubscriberRoot
+
+from xosresource import XOSResource
+
+class XOSVOLTTenant(XOSResource):
+ provides = "tosca.nodes.VOLTTenant"
+ xos_model = VOLTTenant
+ copyin_props = ["service_specific_id"]
+ name_field = None
+
+ def get_xos_args(self, throw_exception=True):
+ args = super(XOSVOLTTenant, 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(VOLTService, throw_exception=throw_exception, name=provider_name)
+
+ subscriber_name = self.get_requirement("tosca.relationships.BelongsToSubscriber")
+ if subscriber_name:
+ args["subscriber_root"] = self.get_xos_object(CordSubscriberRoot, throw_exception=throw_exception, name=subscriber_name)
+
+ return args
+
+ def get_existing_objs(self):
+ args = self.get_xos_args(throw_exception=False)
+ provider_service = args.get("provider", None)
+ service_specific_id = args.get("service_specific_id", None)
+ if (provider_service) and (service_specific_id):
+ return [ self.get_xos_object(provider_service=provider_service, service_specific_id=service_specific_id) ]
+ return []
+
+ def postprocess(self, obj):
+ pass
+
+ def can_delete(self, obj):
+ return super(XOSVOLTTenant, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/network.py b/xos/tosca/resources/network.py
index e5b8b80..f483b6c 100644
--- a/xos/tosca/resources/network.py
+++ b/xos/tosca/resources/network.py
@@ -6,17 +6,19 @@
from translator.toscalib.tosca_template import ToscaTemplate
import pdb
-from core.models import Slice,User,Network,NetworkTemplate
+from core.models import Slice,User,Network,NetworkTemplate,NetworkSlice
from xosresource import XOSResource
class XOSNetwork(XOSResource):
provides = ["tosca.nodes.network.Network", "tosca.nodes.network.Network.XOS"]
xos_model = Network
+ copyin_props = ["ports", "labels"]
def get_xos_args(self):
- args = {"name": self.nodetemplate.name,
- "autoconnect": False,}
+ args = super(XOSNetwork, self).get_xos_args()
+
+ args["autoconnect"] = False
slice_name = self.get_requirement("tosca.relationships.MemberOfSlice")
if slice_name:
@@ -40,7 +42,13 @@
return args
def postprocess(self, obj):
- pass
+ for sliceName in self.get_requirements("tosca.relationships.ConnectsToSlice"):
+ slice = self.get_xos_object(Slice, name=sliceName)
+ netSlices = NetworkSlice.objects.filter(network=obj, slice = slice)
+ if not netSlices:
+ self.info("Attached Network %s to Slice %s" % (obj, slice))
+ ns = NetworkSlice(network = obj, slice=slice)
+ ns.save()
# v = self.get_property("permitted_slices")
# if v:
diff --git a/xos/tosca/resources/service.py b/xos/tosca/resources/service.py
index 4b32b8a..884c6db 100644
--- a/xos/tosca/resources/service.py
+++ b/xos/tosca/resources/service.py
@@ -13,7 +13,7 @@
class XOSService(XOSResource):
provides = "tosca.nodes.Service"
xos_model = Service
- copyin_props = ["view_url", "kind", "enabled", "published", "public_key"]
+ copyin_props = ["view_url", "icon_url", "kind", "enabled", "published", "public_key", "versionNumber"]
def postprocess(self, obj):
for provider_service_name in self.get_requirements("tosca.relationships.TenantOfService"):
@@ -21,7 +21,7 @@
existing_tenancy = CoarseTenant.get_tenant_objects().filter(provider_service = provider_service, subscriber_service = obj)
if existing_tenancy:
- self.info("Tenancy relationship from %s to %s already exists" % (str(service), str(provider_service)))
+ self.info("Tenancy relationship from %s to %s already exists" % (str(obj), str(provider_service)))
else:
tenancy = CoarseTenant(provider_service = provider_service,
subscriber_service = obj)
diff --git a/xos/tosca/resources/subscriber.py b/xos/tosca/resources/subscriber.py
new file mode 100644
index 0000000..5ec0462
--- /dev/null
+++ b/xos/tosca/resources/subscriber.py
@@ -0,0 +1,23 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import Subscriber,User
+
+from xosresource import XOSResource
+
+class XOSSubscriber(XOSResource):
+ provides = "tosca.nodes.Subscriber"
+ xos_model = Subscriber
+ copyin_props = ["service_specific_id"]
+
+ def postprocess(self, obj):
+ pass
+
+ def can_delete(self, obj):
+ return super(XOSService, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/user.py b/xos/tosca/resources/user.py
index 125df4f..53323c5 100644
--- a/xos/tosca/resources/user.py
+++ b/xos/tosca/resources/user.py
@@ -12,15 +12,11 @@
class XOSUser(XOSResource):
provides = "tosca.nodes.User"
xos_model = User
+ name_field = "email"
+ copyin_props = ["password", "firstname", "lastname", "phone", "user_url", "public_key", "is_active", "is_admin", "login_page"]
def get_xos_args(self):
- args = {"email": self.nodetemplate.name}
-
- # copy simple string properties from the template into the arguments
- for prop in ["password", "firstname", "lastname", "phone", "user_url", "public_key", "is_active", "is_admin", "login_page"]:
- v = self.get_property(prop)
- if v:
- args[prop] = v
+ args = super(XOSUser, self).get_xos_args()
site_name = self.get_requirement("tosca.relationships.MemberOfSite")
if site_name:
diff --git a/xos/tosca/resources/vbngservice.py b/xos/tosca/resources/vbngservice.py
index 70f47aa..1fc8d25 100644
--- a/xos/tosca/resources/vbngservice.py
+++ b/xos/tosca/resources/vbngservice.py
@@ -12,5 +12,5 @@
class XOSVBGNService(XOSService):
provides = "tosca.nodes.VBNGService"
xos_model = VBNGService
- copyin_props = ["view_url", "icon_url", "vnbg_url"]
+ copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", "vbng_url"]
diff --git a/xos/tosca/resources/vcpeservice.py b/xos/tosca/resources/vcpeservice.py
index e163520..6cc7390 100644
--- a/xos/tosca/resources/vcpeservice.py
+++ b/xos/tosca/resources/vcpeservice.py
@@ -12,5 +12,5 @@
class XOSVcpeService(XOSService):
provides = "tosca.nodes.VCPEService"
xos_model = VCPEService
- copyin_props = ["view_url", "icon_url", "backend_network_label", "enabled", "published", "public_key"]
+ copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", "backend_network_label"]
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 159156b..ef59485 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -71,10 +71,13 @@
def get_xos_args(self):
return {}
+ def get_model_class_name(self):
+ return self.xos_model.__name__
+
def create_or_update(self):
existing_objs = self.get_existing_objs()
if existing_objs:
- self.info("%s %s already exists" % (self.xos_model.__name__, self.nodetemplate.name))
+ self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
self.update(existing_objs[0])
else:
self.create()
@@ -128,7 +131,7 @@
v = self.try_intrinsic_function(v)
- if v:
+ if v is not None:
args[prop] = v
return args
@@ -144,7 +147,11 @@
self.postprocess(xos_obj)
def update(self, obj):
- pass
+ xos_args = self.get_xos_args()
+ for (k,v) in xos_args.items():
+ setattr(obj, k, v)
+ self.postprocess(obj)
+ obj.save()
def delete(self, obj):
if (self.can_delete(obj)):
diff --git a/xos/tosca/samples/ceilometer.yaml b/xos/tosca/samples/ceilometer.yaml
new file mode 100644
index 0000000..aeb4acb
--- /dev/null
+++ b/xos/tosca/samples/ceilometer.yaml
@@ -0,0 +1,37 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ service_ceilometer:
+ type: tosca.nodes.Service
+ requirements:
+ properties:
+ view_url: /admin/ceilometer/ceilometerservice/$id$/
+ kind: ceilometer
+# public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+# artifacts:
+# pubkey: /opt/xos/observers/vcpe/vcpe_public_key
+
+
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ mysite:
+ type: tosca.nodes.Site
+
+ mysite_ceilometer:
+ description: Ceilometer Proxy Slice
+ type: tosca.nodes.Slice
+ requirements:
+ - ceilometer_service:
+ node: service_ceilometer
+ relationship: tosca.relationships.MemberOfService
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
diff --git a/xos/tosca/samples/cord-cloudlab.yaml b/xos/tosca/samples/cord-cloudlab.yaml
new file mode 100644
index 0000000..20e8057
--- /dev/null
+++ b/xos/tosca/samples/cord-cloudlab.yaml
@@ -0,0 +1,101 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ # CORD Services
+ service_volt:
+ type: tosca.nodes.Service
+ requirements:
+ - vcpe_tenant:
+ node: service_vcpe
+ relationship: tosca.relationships.TenantOfService
+ properties:
+ view_url: /admin/cord/voltservice/$id$/
+ kind: vOLT
+
+ service_vcpe:
+ type: tosca.nodes.VCPEService
+ requirements:
+ - vbng_tenant:
+ node: service_vbng
+ relationship: tosca.relationships.TenantOfService
+ properties:
+ view_url: /admin/cord/vcpeservice/$id$/
+ backend_network_label: hpc_client
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ artifacts:
+ pubkey: /opt/xos/observers/vcpe/vcpe_public_key
+
+ service_vbng:
+ type: tosca.nodes.VBNGService
+ properties:
+ view_url: /admin/cord/vbngservice/$id$/
+ vbng_url: http://10.0.3.136:8181/onos/virtualbng/
+
+ mysite:
+ type: tosca.nodes.Site
+
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ # networks required by vCPE
+ lan_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+
+ wan_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+
+ hpc_client_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+
+ mysite_vcpe:
+ description: vCPE Controller Slice
+ type: tosca.nodes.Slice
+ requirements:
+ - vcpe_service:
+ node: service_vcpe
+ relationship: tosca.relationships.MemberOfService
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
diff --git a/xos/tosca/samples/cord.yaml b/xos/tosca/samples/cord.yaml
index 885435d..eaa536a 100644
--- a/xos/tosca/samples/cord.yaml
+++ b/xos/tosca/samples/cord.yaml
@@ -18,6 +18,25 @@
view_url: /admin/cord/voltservice/$id$/
kind: vOLT
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ # networks required by vCPE
+ lan_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+
service_vcpe:
type: tosca.nodes.VCPEService
requirements:
@@ -51,4 +70,68 @@
node: mysite
relationship: tosca.relationships.MemberOfSite
+ # Now let's add a subscriber
+
+ My House:
+ type: tosca.nodes.CORDSubscriber
+ properties:
+ service_specific_id: 1234
+ firewall_enable: true
+ cdn_enable: true
+ url_filter_enable: true
+ url_filter_level: R
+
+ Mom's PC:
+ type: tosca.nodes.CORDUser
+ properties:
+ mac: 010203040506
+ level: PG_13
+ requirements:
+ - household:
+ node: My House
+ relationship: tosca.relationships.SubscriberDevice
+
+ Dad's PC:
+ type: tosca.nodes.CORDUser
+ properties:
+ mac: 90E2Ba82F975
+ level: PG_13
+ requirements:
+ - household:
+ node: My House
+ relationship: tosca.relationships.SubscriberDevice
+
+ Jack's Laptop:
+ type: tosca.nodes.CORDUser
+ properties:
+ mac: 685B359D91D5
+ level: PG_13
+ requirements:
+ - household:
+ node: My House
+ relationship: tosca.relationships.SubscriberDevice
+
+ Jill's Laptop:
+ type: tosca.nodes.CORDUser
+ properties:
+ mac: 34363BC9B6A6
+ level: PG_13
+ requirements:
+ - household:
+ node: My House
+ relationship: tosca.relationships.SubscriberDevice
+
+ My Volt:
+ type: tosca.nodes.VOLTTenant
+ properties:
+ service_specific_id: 1234
+ requirements:
+ - provider_service:
+ node: service_volt
+ relationship: tosca.relationships.MemberOfService
+ - subscriber:
+ node: My House
+ relationship: tosca.relationships.BelongsToSubscriber
+
+
diff --git a/xos/tosca/tests/alltests.py b/xos/tosca/tests/alltests.py
new file mode 100644
index 0000000..59ad49d
--- /dev/null
+++ b/xos/tosca/tests/alltests.py
@@ -0,0 +1,14 @@
+from coarsetenancytest import CoarseTenancyTest
+from porttest import PortTest
+from networktest import NetworkTest
+from servicetest import ServiceTest
+from usertest import UserTest
+from computetest import ComputeTest
+
+if __name__ == "__main__":
+ NetworkTest()
+ PortTest()
+ CoarseTenancyTest()
+ ServiceTest()
+ UserTest()
+ ComputeTest()
diff --git a/xos/tosca/tests/basetest.py b/xos/tosca/tests/basetest.py
new file mode 100644
index 0000000..f88a63b
--- /dev/null
+++ b/xos/tosca/tests/basetest.py
@@ -0,0 +1,130 @@
+import os
+import random
+import string
+import sys
+
+# add the parent parent directory to sys.path
+# XXX this is very hackish :(
+import os,sys,inspect
+currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+parentdir = os.path.dirname(currentdir)
+sys.path.append(parentdir)
+parentparentdir = os.path.dirname(parentdir)
+sys.path.append(parentparentdir)
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+import django
+django.setup()
+
+from tosca.engine import XOSTosca
+from core.models import User
+
+class BaseToscaTest(object):
+ username = "padmin@vicci.org"
+ base_yaml = \
+"""tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: tosca test case
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+"""
+
+ def __init__(self):
+ self.runtest()
+
+ def make_nodetemplate(self, name, type, props={}, reqs=[], caps={}, artifacts={}):
+ yml = " %s:\n type: %s\n" % (name, type)
+ if props:
+ yml = yml + " properties:\n"
+ for (k,v) in props.items():
+ yml = yml + " %s: %s\n" % (k, v)
+
+ if reqs:
+ yml = yml + " requirements:\n"
+ i=0
+ for (name,relat) in reqs:
+ yml = yml + " - req%d:\n" % i
+ yml = yml + " node: %s\n" % name
+ yml = yml + " relationship: %s\n" % relat
+ i = i + 1
+
+ if caps:
+ yml = yml + " capabilities:\n"
+ for (cap,capdict) in caps.items():
+ yml = yml + " %s:\n" % cap
+ yml = yml + " properties:\n"
+ for (k,v) in capdict.items():
+ yml = yml + " %s: %s\n" % (k,v)
+
+ if artifacts:
+ yml = yml + " artifacts:\n"
+ for (k,v) in artifacts.items():
+ yml = yml + " %s: %s\n" % (k,v)
+
+ return yml
+
+ def make_compute(self, slice, name, caps={}, props={}, reqs=[], num_cpus="1", disk_size="10 GB", mem_size="4 MB"):
+ reqs = reqs[:]
+ caps = caps.copy()
+
+ caps.update( {"host": {"num_cpus": num_cpus, "disk_size": disk_size, "mem_size": mem_size},
+ "os": {"architecture": "x86_64", "type": "linux", "distribution": "rhel", "version": "6.5"}} )
+ reqs.append( (slice, "tosca.relationships.MemberOfSlice") )
+
+ return self.make_nodetemplate(name, "tosca.nodes.Compute",
+ caps= caps,
+ props = props,
+ reqs= reqs)
+
+ def make_random_string(self,desired_len):
+ return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(desired_len))
+
+ def assert_noobj(self, cls, name):
+ objs = cls.objects.filter(name=name)
+ assert(not objs)
+
+ def assert_obj(self, cls, name, **kwargs):
+ obj = cls.objects.get(name=name)
+ assert(obj)
+ for (k,v) in kwargs.items():
+ if (getattr(obj,k,None) != v):
+ print "Object %s property '%s' is '%s' and should be '%s'" % (obj, k, getattr(obj,k,None), v)
+ assert(False)
+ return obj
+
+ def try_to_delete(self, cls, **kwargs):
+ objs = cls.objects.filter(**kwargs)
+ for obj in objs:
+ obj.delete(purge=True)
+
+ def execute(self, yml):
+ u = User.objects.get(email=self.username)
+
+ #print self.base_yaml+yml
+
+ xt = XOSTosca(self.base_yaml+yml, parent_dir=parentdir, log_to_console=False)
+ xt.execute(u)
+
+ def destroy(self, yml):
+ u = User.objects.get(email=self.username)
+
+ #print self.base_yaml+yml
+
+ xt = XOSTosca(self.base_yaml+yml, parent_dir=parentdir, log_to_console=False)
+ xt.destroy(u)
+
+ def runtest(self):
+ for test in self.tests:
+ print "running", test
+ self.cleanup()
+ try:
+ getattr(self,test)()
+ finally:
+ self.cleanup()
+
+ def cleanup(self):
+ pass
diff --git a/xos/tosca/tests/coarsetenancytest.py b/xos/tosca/tests/coarsetenancytest.py
new file mode 100644
index 0000000..3da288f
--- /dev/null
+++ b/xos/tosca/tests/coarsetenancytest.py
@@ -0,0 +1,49 @@
+from basetest import BaseToscaTest
+
+from core.models import Service, CoarseTenant
+
+class CoarseTenancyTest(BaseToscaTest):
+ tests = ["create_coarsetenant",
+ "update_coarsetenant"]
+
+ def cleanup(self):
+ self.try_to_delete(Service, name="test_svc1")
+ self.try_to_delete(Service, name="test_svc2")
+
+ def create_coarsetenant(self):
+ self.assert_noobj(Service, "test_svc1")
+ self.assert_noobj(Service, "test_svc2")
+ self.execute(self.make_nodetemplate("test_svc1", "tosca.nodes.Service", reqs=[("test_svc2", "tosca.relationships.TenantOfService")]) +
+ self.make_nodetemplate("test_svc2", "tosca.nodes.Service"))
+ svc1 = self.assert_obj(Service, "test_svc1", kind="generic", published=True, enabled=True)
+ svc2 = self.assert_obj(Service, "test_svc2", kind="generic", published=True, enabled=True)
+
+ ct = CoarseTenant.objects.filter(provider_service=svc2, subscriber_service=svc1)
+ assert(ct)
+
+ def update_coarsetenant(self):
+ # first make the services without the coarse tenancy relationship
+ self.assert_noobj(Service, "test_svc1")
+ self.assert_noobj(Service, "test_svc2")
+ self.execute(self.make_nodetemplate("test_svc1", "tosca.nodes.Service") +
+ self.make_nodetemplate("test_svc2", "tosca.nodes.Service"))
+ svc1 = self.assert_obj(Service, "test_svc1", kind="generic", published=True, enabled=True)
+ svc2 = self.assert_obj(Service, "test_svc2", kind="generic", published=True, enabled=True)
+ ct = CoarseTenant.objects.filter(provider_service=svc2, subscriber_service=svc1)
+ assert(not ct)
+
+ # now add the relationship
+ self.execute(self.make_nodetemplate("test_svc1", "tosca.nodes.Service", reqs=[("test_svc2", "tosca.relationships.TenantOfService")])+
+ self.make_nodetemplate("test_svc2", "tosca.nodes.Service"))
+ updated_svc1 = self.assert_obj(Service, "test_svc1", kind="generic", published=True, enabled=True)
+
+ assert(svc1.id == updated_svc1.id)
+
+ ct = CoarseTenant.objects.filter(provider_service=svc2, subscriber_service=svc1)
+ assert(ct)
+
+
+if __name__ == "__main__":
+ CoarseTenancyTest()
+
+
diff --git a/xos/tosca/tests/computetest.py b/xos/tosca/tests/computetest.py
new file mode 100644
index 0000000..43a5143
--- /dev/null
+++ b/xos/tosca/tests/computetest.py
@@ -0,0 +1,96 @@
+from basetest import BaseToscaTest
+
+from core.models import Sliver, Slice
+
+class ComputeTest(BaseToscaTest):
+ tests = [ # "create_compute_m1_tiny", XXX m1.tiny does not exist on cloudlab
+ "create_compute_m1_small",
+ "create_compute_m1_large_8192MB",
+ "create_compute_m1_large_8GB",
+ "destroy_compute",
+ "create_compute_scalable",
+ "destroy_compute_scalable",
+ ]
+
+ def cleanup(self):
+ self.try_to_delete(Sliver, name="test_compute1")
+ self.try_to_delete(Sliver, name="test_compute1-0")
+ self.try_to_delete(Sliver, name="test_compute1-1")
+ self.try_to_delete(Sliver, name="test_compute1-2")
+ self.try_to_delete(Sliver, name="test_compute1-3")
+ self.try_to_delete(Slice, name="testsite_slice1")
+
+ def get_base_templates(self):
+ return self.make_nodetemplate("testsite", "tosca.nodes.Site") + \
+ self.make_nodetemplate("testsite_slice1", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")])
+
+ def create_compute_m1_tiny(self):
+ self.assert_noobj(Sliver, "test_compute1")
+ self.execute(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1", disk_size="1 GB", mem_size="500 MB"))
+ sliver = self.assert_obj(Sliver, "test_compute1")
+ assert(sliver.flavor.name == "m1.tiny")
+
+ def create_compute_m1_small(self):
+ self.assert_noobj(Sliver, "test_compute1")
+ self.execute(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1", disk_size="1 GB", mem_size="513 MB"))
+ sliver = self.assert_obj(Sliver, "test_compute1")
+ assert(sliver.flavor.name == "m1.small")
+
+ def create_compute_m1_large_8192MB(self):
+ self.assert_noobj(Sliver, "test_compute1")
+ self.execute(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1", mem_size="8192 MB"))
+ sliver = self.assert_obj(Sliver, "test_compute1")
+ assert(sliver.flavor.name == "m1.large")
+
+ def create_compute_m1_large_8GB(self):
+ self.assert_noobj(Sliver, "test_compute1")
+ self.execute(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB"))
+ sliver = self.assert_obj(Sliver, "test_compute1")
+ assert(sliver.flavor.name == "m1.large")
+
+ def destroy_compute(self):
+ self.execute(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1"))
+ self.assert_obj(Sliver, "test_compute1")
+ self.destroy(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1"))
+ self.assert_noobj(Sliver, "test_compute1")
+
+ def create_compute_scalable(self):
+ self.assert_noobj(Sliver, "test_compute1-1")
+ self.assert_noobj(Sliver, "test_compute1-2")
+ self.assert_noobj(Sliver, "test_compute1-3")
+ self.execute(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB",
+ caps={"scalable": {"min_instances": 2, "max_instances": 3, "default_instances": 2}}))
+ # there should be two instances
+ sliver0 = self.assert_obj(Sliver, "test_compute1-0")
+ sliver1 = self.assert_obj(Sliver, "test_compute1-1")
+ self.assert_noobj(Sliver, "test_compute1-2")
+
+ def destroy_compute_scalable(self):
+ self.assert_noobj(Sliver, "test_compute1-1")
+ self.assert_noobj(Sliver, "test_compute1-2")
+ self.assert_noobj(Sliver, "test_compute1-3")
+ self.execute(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB",
+ caps={"scalable": {"min_instances": 2, "max_instances": 3, "default_instances": 2}}))
+ # there should be two instances
+ sliver0 = self.assert_obj(Sliver, "test_compute1-0")
+ sliver1 = self.assert_obj(Sliver, "test_compute1-1")
+
+ self.destroy(self.get_base_templates() +
+ self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB",
+ caps={"scalable": {"min_instances": 2, "max_instances": 3, "default_instances": 2}}))
+
+ self.assert_noobj(Sliver, "test_compute1-0")
+ self.assert_noobj(Sliver, "test_compute1-1")
+
+if __name__ == "__main__":
+ ComputeTest()
+
+
diff --git a/xos/tosca/tests/networktest.py b/xos/tosca/tests/networktest.py
new file mode 100644
index 0000000..fa446f9
--- /dev/null
+++ b/xos/tosca/tests/networktest.py
@@ -0,0 +1,151 @@
+from basetest import BaseToscaTest
+
+from core.models import Network, Slice, NetworkTemplate, NetworkSlice
+
+class NetworkTest(BaseToscaTest):
+ tests = ["create_network_minimal",
+ "create_network_maximal",
+ "create_network_connected",
+ "create_network_connected_two_slices",
+ "update_network_labels",
+ "destroy_network"]
+
+ def cleanup(self):
+ self.try_to_delete(Network, name="test_net")
+ self.try_to_delete(Slice, name="testsite_slice1")
+ self.try_to_delete(Slice, name="testsite_slice2")
+
+ @property
+ def slice1(self):
+ return Slice.objects.get(name="testsite_slice1")
+
+ @property
+ def slice2(self):
+ return Slice.objects.get(name="testsite_slice2")
+
+ @property
+ def private(Self):
+ return NetworkTemplate.objects.get(name="Private")
+
+
+ def get_base_templates(self):
+ return self.make_nodetemplate("testsite", "tosca.nodes.Site") + \
+ self.make_nodetemplate("testsite_slice1", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+ self.make_nodetemplate("testsite_slice2", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+ self.make_nodetemplate("Private", "tosca.nodes.NetworkTemplate")
+
+ def create_network_minimal(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+ net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+ ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+ assert(not ns)
+
+ def create_network_maximal(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+ props={"ports": "tcp/1234, udp/5678",
+ "labels": "foo,bar",
+ "permit_all_slices": False},
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+ net=self.assert_obj(Network, "test_net",
+ owner=self.slice1,
+ template=self.private,
+ ports="tcp/1234, udp/5678",
+ labels="foo,bar",
+ permit_all_slices=False)
+
+ ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+ assert(not ns)
+
+ def create_network_connected(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate"),
+ ("testsite_slice1", "tosca.relationships.ConnectsToSlice")]))
+
+ net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+ ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+ assert(ns)
+
+ def create_network_connected_two_slices(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate"),
+ ("testsite_slice1", "tosca.relationships.ConnectsToSlice"),
+ ("testsite_slice2", "tosca.relationships.ConnectsToSlice")]))
+
+ net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+ ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+ assert(ns)
+
+ ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+ assert(ns)
+
+ def update_network_labels(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+ net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels=None)
+
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+ props={"labels": "testlabel"},
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+ updated_net = self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels="testlabel")
+
+ assert(net.id == updated_net.id)
+
+ def update_network_ports(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+ net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels=None, ports=None)
+
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+ props={"port": "tcp/2222, udp/3333"},
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+ updated_net = self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels=None, ports="tcp/2222, udp/3333")
+
+ assert(net.id == updated_net.id)
+
+ def destroy_network(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+ net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+ self.destroy(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+ self.assert_noobj(Network, "test_net")
+
+if __name__ == "__main__":
+ NetworkTest()
+
+
diff --git a/xos/tosca/tests/porttest.py b/xos/tosca/tests/porttest.py
new file mode 100644
index 0000000..a0d23e3
--- /dev/null
+++ b/xos/tosca/tests/porttest.py
@@ -0,0 +1,162 @@
+from basetest import BaseToscaTest
+
+from core.models import Network, Slice, NetworkTemplate, NetworkSlice, Port, Sliver
+
+class PortTest(BaseToscaTest):
+ tests = ["create_port_minimal",
+ "create_two_ports",
+ "create_four_ports",
+ "add_port_after_network"]
+
+ def cleanup(self):
+ self.try_to_delete(Sliver, name="test_compute1")
+ self.try_to_delete(Sliver, name="test_compute2")
+ self.try_to_delete(Network, name="test_net")
+ self.try_to_delete(Slice, name="testsite_slice1")
+ self.try_to_delete(Slice, name="testsite_slice2")
+
+ @property
+ def slice1(self):
+ return Slice.objects.get(name="testsite_slice1")
+
+ @property
+ def slice2(self):
+ return Slice.objects.get(name="testsite_slice2")
+
+ @property
+ def private(self):
+ return NetworkTemplate.objects.get(name="Private")
+
+ @property
+ def test_slice1_1(self):
+ return Sliver.objects.get(name="test_slice1-1")
+
+ @property
+ def test_slice1_2(self):
+ return Sliver.objects.get(name="test_slice1-2")
+
+ @property
+ def test_slice2_1(self):
+ return Sliver.objects.get(name="test_slice2-1")
+
+ @property
+ def test_slice2_2(self):
+ return Sliver.objects.get(name="test_slice2-2")
+
+ def get_base_templates(self):
+ return self.make_nodetemplate("testsite", "tosca.nodes.Site") + \
+ self.make_nodetemplate("testsite_slice1", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+ self.make_nodetemplate("testsite_slice2", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+ self.make_nodetemplate("Private", "tosca.nodes.NetworkTemplate") + \
+ self.make_compute("testsite_slice1", "test_slice1-1") + \
+ self.make_compute("testsite_slice1", "test_slice1-2") +\
+ self.make_compute("testsite_slice2", "test_slice2-1") + \
+ self.make_compute("testsite_slice2", "test_slice2-2")
+
+ def create_port_minimal(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+ self.make_nodetemplate("test_port", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice1-1", "tosca.relationships.network.BindsTo")]))
+
+ net=self.assert_obj(Network, "test_net")
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+ assert(len(port)==1)
+ port=port[0]
+
+ def create_two_ports(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+ self.make_nodetemplate("test_port1", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice1-1", "tosca.relationships.network.BindsTo")]) +
+ self.make_nodetemplate("test_port2", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice1-2", "tosca.relationships.network.BindsTo")]))
+
+ net=self.assert_obj(Network, "test_net")
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+ assert(len(port)==1)
+ port=port[0]
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice1_2)
+ assert(len(port)==1)
+ port=port[0]
+
+ def create_four_ports(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+ self.make_nodetemplate("test_port1", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice1-1", "tosca.relationships.network.BindsTo")]) +
+ self.make_nodetemplate("test_port2", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice1-2", "tosca.relationships.network.BindsTo")]) +
+ self.make_nodetemplate("test_port3", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice2-1", "tosca.relationships.network.BindsTo")]) +
+ self.make_nodetemplate("test_port4", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice2-2", "tosca.relationships.network.BindsTo")]))
+
+ net=self.assert_obj(Network, "test_net")
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+ assert(len(port)==1)
+ port=port[0]
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice1_2)
+ assert(len(port)==1)
+ port=port[0]
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice2_2)
+ assert(len(port)==1)
+ port=port[0]
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice2_2)
+ assert(len(port)==1)
+ port=port[0]
+
+ def add_port_after_network(self):
+ self.assert_noobj(Network, "test_net")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+
+ orig_net=self.assert_obj(Network, "test_net")
+
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+ reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+ ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+ self.make_nodetemplate("test_port1", "tosca.nodes.network.Port",
+ reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+ ("test_slice1-1", "tosca.relationships.network.BindsTo")]))
+
+ net=self.assert_obj(Network, "test_net")
+
+ assert(orig_net.id == net.id)
+
+ port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+ assert(len(port)==1)
+ port=port[0]
+
+
+if __name__ == "__main__":
+ PortTest()
+
+
diff --git a/xos/tosca/tests/servicetest.py b/xos/tosca/tests/servicetest.py
new file mode 100644
index 0000000..276463d
--- /dev/null
+++ b/xos/tosca/tests/servicetest.py
@@ -0,0 +1,74 @@
+from basetest import BaseToscaTest
+
+from core.models import Service
+
+class ServiceTest(BaseToscaTest):
+ tests = ["create_service_minimal",
+ "create_service_notpublished",
+ "create_service_notenabled",
+ "create_service_public_key",
+ "update_service_notpublished",
+ "create_service_maximal",
+ "destroy_service"]
+
+ def cleanup(self):
+ self.try_to_delete(Service, name="test_svc")
+
+ def create_service_minimal(self):
+ self.assert_noobj(Service, "test_svc")
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+ self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True)
+
+ def create_service_notpublished(self):
+ self.assert_noobj(Service, "test_svc")
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"published": False}))
+ self.assert_obj(Service, "test_svc", kind="generic", published=False, enabled=True)
+
+ def create_service_notenabled(self):
+ self.assert_noobj(Service, "test_svc")
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"enabled": False}))
+ self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=False)
+
+ def create_service_public_key(self):
+ self.assert_noobj(Service, "test_svc")
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"public_key": "foobar"}))
+ self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True, public_key="foobar")
+
+ def update_service_notpublished(self):
+ self.assert_noobj(Service, "test_svc")
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+ original_obj = self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True)
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"published": False}))
+ updated_obj = self.assert_obj(Service, "test_svc", kind="generic", published=False, enabled=True)
+ assert(original_obj.id == updated_obj.id)
+
+ def create_service_maximal(self):
+ self.assert_noobj(Service, "test_svc")
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service",
+ {"kind": "testkind",
+ "published": False,
+ "enabled": False,
+ "view_url": "http://foo/",
+ "icon_url": "http://bar/",
+ "public_key": "foobar",
+ "versionNumber": "1.2"} ))
+ self.assert_obj(Service, "test_svc",
+ kind="testkind",
+ published=False,
+ enabled=False,
+ view_url="http://foo/",
+ icon_url="http://bar/",
+ public_key="foobar",
+ versionNumber="1.2")
+
+ def destroy_service(self):
+ self.assert_noobj(Service, "test_svc")
+ self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+ self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True)
+ self.destroy(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+ self.assert_noobj(Service, "test_svc")
+
+if __name__ == "__main__":
+ ServiceTest()
+
+
diff --git a/xos/tosca/tests/usertest.py b/xos/tosca/tests/usertest.py
new file mode 100644
index 0000000..f36eb06
--- /dev/null
+++ b/xos/tosca/tests/usertest.py
@@ -0,0 +1,98 @@
+from basetest import BaseToscaTest
+
+from core.models import User
+
+class UserTest(BaseToscaTest):
+ tests = ["create_user_minimal",
+ "create_user_maximal",
+ "create_user_key_artifact",
+ "destroy_user",
+ ]
+
+ def cleanup(self):
+ self.try_to_delete(User, email="test@user.com")
+
+ def get_base_templates(self):
+ return self.make_nodetemplate("testsite", "tosca.nodes.Site")
+
+ def assert_nouser(self, email):
+ assert(not User.objects.filter(email=email))
+
+ def assert_user(self, email, **kwargs):
+ obj = User.objects.get(email=email)
+ assert(obj)
+ for (k,v) in kwargs.items():
+ if (getattr(obj,k,None) != v):
+ print "Object %s property '%s' is '%s' and should be '%s'" % (obj, k, getattr(obj,k,None), v)
+ assert(False)
+ return obj
+
+ def create_user_minimal(self):
+ self.assert_nouser("test@user.com")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+ props = {"firstname": "test", "lastname": "user", "password": "letmein"},
+ reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+ user = self.assert_user("test@user.com",
+ firstname="test",
+ lastname="user",
+ is_active=True,
+ is_admin=False)
+
+
+ def create_user_maximal(self):
+ self.assert_nouser("test@user.com")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+ props = {"firstname": "test",
+ "lastname": "user",
+ "password": "letmein",
+ "phone": "123-456-7890",
+ "user_url": "http://foo.bar/",
+ "public_key": "thisismykey",
+ "is_active": False,
+ "is_admin": True},
+ reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+ user = self.assert_user("test@user.com",
+ firstname="test",
+ lastname="user",
+ phone="123-456-7890",
+ user_url="http://foo.bar/",
+ public_key="thisismykey",
+ # is_active=False, XXX investigate -- this is failing
+ is_admin=True)
+
+ def create_user_key_artifact(self):
+ self.assert_nouser("test@user.com")
+ pubkey = self.make_random_string(400)
+ file("/tmp/pubkey", "w").write(pubkey)
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+ props = {"firstname": "test", "lastname": "user", "password": "letmein", "public_key": "{ get_artifact: [ SELF, pubkey, LOCAL_FILE] }" },
+ artifacts = {"pubkey": "/tmp/pubkey"},
+ reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+ user = self.assert_user("test@user.com",
+ firstname="test",
+ lastname="user",
+ is_active=True,
+ is_admin=False,
+ public_key=pubkey)
+
+ def destroy_user(self):
+ self.assert_nouser("test@user.com")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+ props = {"firstname": "test", "lastname": "user", "password": "letmein"},
+ reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+ user = self.assert_user("test@user.com")
+ self.destroy(self.get_base_templates() +
+ self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+ props = {"firstname": "test", "lastname": "user", "password": "letmein"},
+ reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+ self.assert_nouser("test@user.com")
+
+
+if __name__ == "__main__":
+ UserTest()
+
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index ab454e2..ba5ff1d 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -148,6 +148,7 @@
'core',
'hpc',
'cord',
+ 'ceilometer',
'requestrouter',
# 'urlfilter',
# 'servcomp',