Merge branch 'master' of https://github.com/open-cloud/xos into AddHelloWorldService
diff --git a/xos/configurations/common/Makefile.cloudlab b/xos/configurations/common/Makefile.cloudlab
index 929bc14..5712765 100644
--- a/xos/configurations/common/Makefile.cloudlab
+++ b/xos/configurations/common/Makefile.cloudlab
@@ -1,4 +1,4 @@
-all: prereqs admin-openrc flat_name nodes_yaml public_key
+all: prereqs admin-openrc flat_name nodes_yaml public_key private_key
prereqs:
make -f Makefile.prereqs
@@ -7,7 +7,7 @@
sudo cat /root/setup/admin-openrc.sh > admin-openrc.sh
flat_name:
- sudo bash -c "source /root/setup/admin-openrc.sh ; neutron net-list" |grep flat|awk '{print $$4}' > flat_net_name
+ sudo bash -c "source /root/setup/admin-openrc.sh ; neutron net-list" |grep flat|awk '{printf "%s",$$4}' > flat_net_name
nodes_yaml:
bash ./make-cloudlab-nodes-yaml.sh
@@ -15,6 +15,8 @@
public_key: ~/.ssh/id_rsa.pub
cp ~/.ssh/id_rsa.pub .
+private_key: ~/.ssh/id_rsa
+ cp ~/.ssh/id_rsa .
+
~/.ssh/id_rsa.pub:
cat /dev/zero | ssh-keygen -q -N ""
-
diff --git a/xos/configurations/cord/Dockerfile.cord b/xos/configurations/cord/Dockerfile.cord
new file mode 100644
index 0000000..dad9895
--- /dev/null
+++ b/xos/configurations/cord/Dockerfile.cord
@@ -0,0 +1,13 @@
+RUN mkdir -p /root/setup
+ADD xos/configurations/common/admin-openrc.sh /root/setup/
+ADD xos/configurations/common/flat_net_name /root/setup/
+ADD xos/configurations/common/cloudlab-nodes.yaml /opt/xos/configurations/commmon/
+ADD xos/configurations/common/id_rsa.pub /root/setup/padmin_public_key
+ADD xos/configurations/common/id_rsa.pub /opt/xos/observers/vcpe/vcpe_public_key
+ADD xos/configurations/common/id_rsa /opt/xos/observers/vcpe/vcpe_private_key
+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
+
+CMD /usr/bin/make -C /opt/xos/configurations/cord -f Makefile.inside; /bin/bash
+
+#CMD ["/bin/bash"]
diff --git a/xos/configurations/cord/Makefile b/xos/configurations/cord/Makefile
new file mode 100644
index 0000000..40ff5a1
--- /dev/null
+++ b/xos/configurations/cord/Makefile
@@ -0,0 +1,22 @@
+MYIP:=$(shell hostname -i)
+RUNNING_CONTAINER:=$(shell sudo docker ps|grep "xos"|awk '{print $$NF}')
+LAST_CONTAINER=$(shell sudo docker ps -l -q)
+
+test: common_cloudlab
+ echo "# Autogenerated -- do not edit" > Dockerfile
+ cat ../common/Dockerfile.common Dockerfile.cord >> Dockerfile
+ cd ../../..; sudo docker build -t xos -f xos/configurations/cord/Dockerfile .
+ sudo docker run -d --add-host="ctl:$(MYIP)" -p 9999:8000 xos
+ bash ../common/wait_for_xos.sh
+
+common_cloudlab:
+ make -C ../common -f Makefile.cloudlab
+
+stop:
+ sudo docker stop $(RUNNING_CONTAINER)
+
+showlogs:
+ sudo docker logs $(LAST_CONTAINER)
+
+enter:
+ sudo docker exec -t -i $(RUNNING_CONTAINER) bash
diff --git a/xos/configurations/cord/Makefile.inside b/xos/configurations/cord/Makefile.inside
new file mode 100644
index 0000000..a4bb5f1
--- /dev/null
+++ b/xos/configurations/cord/Makefile.inside
@@ -0,0 +1,10 @@
+all: setup_xos run_develserver
+
+setup_xos:
+ bash /opt/xos/scripts/docker_setup_xos
+ python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab.yaml
+ python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab-nodes.yaml
+ python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord/cord.yaml
+
+run_develserver:
+ cd /opt/xos; python manage.py runserver 0.0.0.0:8000 --insecure
diff --git a/xos/configurations/cord/README b/xos/configurations/cord/README
new file mode 100644
index 0000000..7c80c0f
--- /dev/null
+++ b/xos/configurations/cord/README
@@ -0,0 +1,4 @@
+This configuration configures XOS with the CORD services. It is intended to be
+run on CloudLab, on the "ctl" node set up by the OpenStack profile. It launches
+an XOS container on Cloudlab that runs the XOS develserver. The container is
+left running in the background.
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
new file mode 100644
index 0000000..56ab11d
--- /dev/null
+++ b/xos/configurations/cord/cord.yaml
@@ -0,0 +1,165 @@
+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
+
+ 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
+
+ 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
+
+ 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
+
+ # Now let's add a subscriber
+
+ My House:
+ type: tosca.nodes.CORDSubscriber
+ properties:
+ service_specific_id: 1234
+ firewall_enable: false
+ cdn_enable: false
+ url_filter_enable: false
+ 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
+ vlan_id: 4321
+ requirements:
+ - provider_service:
+ node: service_volt
+ relationship: tosca.relationships.MemberOfService
+ - subscriber:
+ node: My House
+ relationship: tosca.relationships.BelongsToSubscriber
diff --git a/xos/configurations/devel/Makefile b/xos/configurations/devel/Makefile
index 90dabcc..3ce188b 100644
--- a/xos/configurations/devel/Makefile
+++ b/xos/configurations/devel/Makefile
@@ -6,7 +6,7 @@
echo "# Autogenerated -- do not edit" > Dockerfile
cat ../common/Dockerfile.common Dockerfile.devel >> Dockerfile
cd ../../..; sudo docker build -t xos -f xos/configurations/devel/Dockerfile .
- sudo docker run -d --add-host="0.0.0.0:$(MYIP)" -p 9999:8000 xos
+ sudo docker run -d --add-host="ctl:$(MYIP)" -p 9999:8000 xos
bash ../common/wait_for_xos.sh
common_cloudlab:
diff --git a/xos/configurations/kilo-install/Dockerfile.kilo-install b/xos/configurations/kilo-install/Dockerfile.kilo-install
index e4d301b..fabfcd8 100644
--- a/xos/configurations/kilo-install/Dockerfile.kilo-install
+++ b/xos/configurations/kilo-install/Dockerfile.kilo-install
@@ -1,6 +1,5 @@
RUN mkdir -p /root/setup
ADD xos/configurations/common/admin-openrc.sh /root/setup/
-RUN bash -c 'echo "nat-net" > /root/setup/flat_net_name'
ADD xos/configurations/common/cloudlab-nodes.yaml /opt/xos/configurations/commmon/
ADD xos/configurations/common/id_rsa.pub /root/setup/padmin_public_key
diff --git a/xos/configurations/kilo-install/Makefile b/xos/configurations/kilo-install/Makefile
index 7d11490..108df7a 100644
--- a/xos/configurations/kilo-install/Makefile
+++ b/xos/configurations/kilo-install/Makefile
@@ -2,11 +2,15 @@
RUNNING_CONTAINER:=$(shell sudo docker ps|grep "xos"|awk '{print $$NF}')
LAST_CONTAINER=$(shell sudo docker ps -l -q)
+# For installing XOS on a node that is not the OpenStack head node
+MGMT_SUBNET="192.168.122.0/24"
+HEAD_NODE_IP="130.127.133.61"
+
test: common_cloudlab images
echo "# Autogenerated -- do not edit" > Dockerfile
cat ../common/Dockerfile.common Dockerfile.kilo-install >> Dockerfile
cd ../../..; sudo docker build -t xos -f xos/configurations/kilo-install/Dockerfile .
- sudo docker run -d --add-host="0.0.0.0:$(MYIP)" -p 9999:8000 -v /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro xos
+ sudo docker run -d -p 9999:8000 -v /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro xos
bash ../common/wait_for_xos.sh
common_cloudlab:
@@ -28,3 +32,7 @@
--disk-format qcow2 \
--file /proj/xos-PG0/acb/images/trusty-server-multi-nic.img \
--container-format bare"
+
+remote_head_node:
+ sudo iptables -t nat -A OUTPUT -p tcp -d $MGMT_SUBNET -j DNAT --to-destination $HEAD_NODE_IP
+ sudo iptables -t nat -A PREROUTING -p tcp -d $MGMT_SUBNET -j DNAT --to-destination $HEAD_NODE_IP
diff --git a/xos/configurations/kilo-install/Makefile.inside b/xos/configurations/kilo-install/Makefile.inside
index 40b2672..c49841a 100644
--- a/xos/configurations/kilo-install/Makefile.inside
+++ b/xos/configurations/kilo-install/Makefile.inside
@@ -2,7 +2,7 @@
setup_xos:
bash /opt/xos/scripts/docker_setup_xos
- python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab.yaml
+ python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/kilo-install/cloudlab.yaml
python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab-nodes.yaml
update_certificates:
diff --git a/xos/configurations/kilo-install/cloudlab.yaml b/xos/configurations/kilo-install/cloudlab.yaml
new file mode 100644
index 0000000..f92442c
--- /dev/null
+++ b/xos/configurations/kilo-install/cloudlab.yaml
@@ -0,0 +1,76 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+# Note:
+# assumes the following have been created and filled with appropriate data:
+# /root/setup/admin_openrc
+# /root/setup/flat_net_name
+# /root/setup/padmin_public_key
+
+description: >
+ * Adds OpenCloud Sites, Deployments, and Controllers.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ trusty-server-multi-nic:
+ type: tosca.nodes.Image
+ properties:
+ disk_format: QCOW2
+ container_format: BARE
+
+ MyDeployment:
+ type: tosca.nodes.Deployment
+ properties:
+ flavors: m1.large, m1.medium, m1.small
+ requirements:
+ - image:
+ node: trusty-server-multi-nic
+ relationship: tosca.relationships.SupportsImage
+
+ CloudLab:
+ type: tosca.nodes.Controller
+ requirements:
+ - deployment:
+ node: MyDeployment
+ relationship: tosca.relationships.ControllerDeployment
+ properties:
+ backend_type: OpenStack
+ version: Juno
+ auth_url: { get_script_env: [ SELF, adminrc, OS_AUTH_URL, LOCAL_FILE] }
+ admin_user: { get_script_env: [ SELF, adminrc, OS_USERNAME, LOCAL_FILE] }
+ admin_password: { get_script_env: [ SELF, adminrc, OS_PASSWORD, LOCAL_FILE] }
+ admin_tenant: { get_script_env: [ SELF, adminrc, OS_TENANT_NAME, LOCAL_FILE] }
+ domain: Default
+ artifacts:
+ adminrc: /root/setup/admin-openrc.sh
+
+ mysite:
+ type: tosca.nodes.Site
+ properties:
+ display_name: MySite
+ site_url: http://opencloud.us/
+ requirements:
+ - deployment:
+ node: MyDeployment
+ relationship: tosca.relationships.SiteDeployment
+ requirements:
+ - controller:
+ node: CloudLab
+ relationship: tosca.relationships.UsesController
+
+ padmin@vicci.org:
+ type: tosca.nodes.User
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ properties:
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE ] }
+ is_admin: true
+ is_active: true
+ firstname: XOS
+ lastname: admin
+ artifacts:
+ pubkey: /root/setup/padmin_public_key
diff --git a/xos/configurations/opencloud/Dockerfile b/xos/configurations/opencloud/Dockerfile
index ad85718..5b09507 100644
--- a/xos/configurations/opencloud/Dockerfile
+++ b/xos/configurations/opencloud/Dockerfile
@@ -31,8 +31,8 @@
libyaml-dev \
pkg-config \
supervisor \
- python-crypto
- python-httplib2 \
+ python-crypto \
+ python-httplib2>=0.9.1 \
python-jinja2 \
python-paramiko \
python-pip \
@@ -47,7 +47,7 @@
python-ceilometerclient
RUN pip install \
- django==1.7
+ django==1.7 \
djangorestframework==2.4.4 \
markdown \
pyyaml \
@@ -69,13 +69,14 @@
pygraphviz \
dnslib
+RUN easy_install --upgrade httplib2
+
RUN easy_install \
- django_evolution
+ django_evolution \
python_gflags \
google_api_python_client \
httplib2.ca_certs_locater
-RUN easy_install --upgrade httplib2
# Install custom Ansible
RUN git clone -b release1.8.2 git://github.com/ansible/ansible.git /opt/ansible
@@ -103,7 +104,7 @@
ADD observer.conf /etc/supervisor/conf.d/
# Get XOS
-ADD xos /opt/xos
+RUN git clone git://github.com/open-cloud/xos.git /tmp/xos && mv /tmp/xos/xos /opt/
# Initscript is broken in Ubuntu
#ADD observer-initscript /etc/init.d/xosobserver
@@ -134,7 +135,6 @@
RUN apt-get install -y m4
RUN pip install python-dateutil
RUN bash /opt/xos/tosca/install_tosca.sh
-RUN /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org ${TOSCA_CONFIG_PATH}
EXPOSE 8000
diff --git a/xos/configurations/opencloud/Makefile b/xos/configurations/opencloud/Makefile
index aba30dc..863f2b7 100644
--- a/xos/configurations/opencloud/Makefile
+++ b/xos/configurations/opencloud/Makefile
@@ -1,8 +1,13 @@
+RUNNING_CONTAINER:=$(shell sudo docker ps|grep "opencloud_server"|awk '{print $$NF}')
+
.PHONY: build
build: ; docker build --rm -t opencloud .
.PHONY: run
-run: ; docker run --rm -d -e TOSCA_CONFIG_PATH=$TOSCA_CONFIG_PATH --name opencloud_server opencloud
+run: ; docker run --rm --name opencloud_server opencloud
+
+.PHONY: runtosca
+runtosca: ; docker exec -it $RUNNING_CONTAINER /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org $TOSCA_CONFIG_PATH
.PHONY: stop
stop: ; docker stop opencloud_server
diff --git a/xos/configurations/opencloud/ansible-hosts b/xos/configurations/opencloud/ansible-hosts
new file mode 100644
index 0000000..0dd74f1
--- /dev/null
+++ b/xos/configurations/opencloud/ansible-hosts
@@ -0,0 +1,2 @@
+[localhost]
+127.0.0.1
diff --git a/xos/configurations/opencloud/observer.conf b/xos/configurations/opencloud/observer.conf
new file mode 100644
index 0000000..92545eb
--- /dev/null
+++ b/xos/configurations/opencloud/observer.conf
@@ -0,0 +1,2 @@
+[program:observer]
+command=python /opt/xos/xos-observer.py
diff --git a/xos/core/fixtures/initial_data.json b/xos/core/fixtures/initial_data.json
index 213245c..86658bb 100644
--- a/xos/core/fixtures/initial_data.json
+++ b/xos/core/fixtures/initial_data.json
@@ -334,7 +334,7 @@
"translation": "none",
"backend_status": "0 - Provisioning in progress",
"shared_network_name": null,
- "controller_kind": "none",
+ "controller_kind": null,
"enacted": null
},
"model": "core.networktemplate",
@@ -356,7 +356,7 @@
"translation": "NAT",
"backend_status": "0 - Provisioning in progress",
"shared_network_name": "nat-net",
- "controller_kind": "none",
+ "controller_kind": null,
"enacted": null
},
"model": "core.networktemplate",
@@ -378,7 +378,7 @@
"translation": "none",
"backend_status": "0 - Provisioning in progress",
"shared_network_name": "ext-net",
- "controller_kind": "none",
+ "controller_kind": null,
"enacted": null
},
"model": "core.networktemplate",
diff --git a/xos/core/models/controlleruser.py b/xos/core/models/controlleruser.py
index 6b11b24..1031e12 100644
--- a/xos/core/models/controlleruser.py
+++ b/xos/core/models/controlleruser.py
@@ -26,7 +26,7 @@
if user.is_admin:
qs = ControllerUser.objects.all()
else:
- users = Users.select_by_user(user)
+ users = User.select_by_user(user)
qs = ControllerUser.objects.filter(user__in=users)
return qs
diff --git a/xos/core/models/flavor.py b/xos/core/models/flavor.py
index 3d6b9bb..8251eb1 100644
--- a/xos/core/models/flavor.py
+++ b/xos/core/models/flavor.py
@@ -18,6 +18,10 @@
app_label = "core"
ordering = ('order', 'name')
+ def __init__(self, *args, **kwargs):
+ super(Flavor, self).__init__(*args, **kwargs)
+ self.no_sync=True
+
def __unicode__(self): return u'%s' % (self.name)
""" FlavorParameterType and FlavorParameter are below for completeness sake,
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index fe60fce..2bdf17f 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -7,6 +7,7 @@
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.core.exceptions import ValidationError
+from django.db.models import Q
# If true, then IP addresses will be allocated by the model. If false, then
# we will assume the observer handles it.
@@ -203,7 +204,8 @@
qs = NetworkSlice.objects.all()
else:
slice_ids = [s.id for s in Slice.select_by_user(user)]
- qs = NetworkSlice.objects.filter(id__in=slice_ids)
+ network_ids = [network.id for network in Network.select_by_user(user)]
+ qs = NetworkSlice.objects.filter(Q(slice__in=slice_ids) | Q(network__in=network_ids))
return qs
class Port(PlCoreBase):
@@ -246,8 +248,11 @@
if user.is_admin:
qs = Port.objects.all()
else:
- instance_ids = [s.id for s in Port.select_by_user(user)]
- qs = Port.objects.filter(id__in=instance_ids)
+ instances = Instance.select_by_user(user)
+ instance_ids = [instance.id for instance in instances]
+ networks = Network.select_by_user(user)
+ network_ids = [network.id for network in networks]
+ qs = Port.objects.filter(Q(instance__in=instance_ids) | Q(network__in=network_ids))
return qs
class Router(PlCoreBase):
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index 42855a9..26ff191 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -168,6 +168,9 @@
# given a default of 'allow site <site_of_creator>'
accessControl = models.TextField(max_length=200, blank=False, null=False, default="allow all",
help_text="Access control list that specifies which sites/users may use nodes in this deployment")
+ def __init__(self, *args, **kwargs):
+ super(Deployment, self).__init__(*args, **kwargs)
+ self.no_sync=True
def get_acl(self):
return AccessControlList(self.accessControl)
@@ -261,7 +264,10 @@
admin_tenant = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the tenant the admin user belongs to")
domain = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the domain this controller belongs to")
deployment = models.ForeignKey(Deployment,related_name='controllerdeployments')
-
+
+ def __init__(self, *args, **kwargs):
+ super(Controller, self).__init__(*args, **kwargs)
+ self.no_sync=True
def __unicode__(self): return u'%s %s %s' % (self.name, self.backend_type, self.version)
diff --git a/xos/helloworld/view.py b/xos/helloworld/view.py
new file mode 100644
index 0000000..b3eec29
--- /dev/null
+++ b/xos/helloworld/view.py
@@ -0,0 +1,58 @@
+from django.http import HttpResponse
+from django.views.generic import TemplateView, View
+from django import template
+from monitor import driver
+from core.models import *
+from helloworld.models import *
+import json
+import os
+import time
+import tempfile
+
+class HelloWorldView(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
+
+ try:
+ hello_name = request.GET['hello_name']
+ world_name = request.GET['world_name']
+ instance_id_str = request.GET['instance_id']
+ instance_id = int(instance_id_str)
+
+ i = Instance.objects.get(pk=instance_id)
+ i.pk=None
+ i.userData=None
+ i.instance_id=None
+ i.instance_name=None
+ i.enacted=None
+ i.save()
+ h = Hello(name=hello_name,sliver_backref=i)
+ w = World(hello=h,name=world_name)
+ h.save()
+ w.save()
+
+ t = template.Template(head_template + 'Done. New instance id: %r'%i.pk + self.tail_template)
+ except KeyError:
+ html = """<form>
+ Hello string: <input type="text" name="hello_name" placeholder="Planet"><br>
+ World string: <input type="text" name="world_name" placeholder="Earth"><br>
+ Id of instance to copy: <input type="text" name="instance_id" placeholder="3"><br>
+ <input type="submit" value="Submit">
+ </form>"""
+
+ t = template.Template(head_template + html + self.tail_template)
+
+ response_kwargs = {}
+ response_kwargs.setdefault('content_type', self.content_type)
+ return self.response_class(
+ request = request,
+ template = t,
+ **response_kwargs)
diff --git a/xos/observers/hello_world/run.sh b/xos/observers/hello_world/run.sh
deleted file mode 100755
index f77d751..0000000
--- a/xos/observers/hello_world/run.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#if [[ ! -e ./hpc-backend.py ]]; then
-# ln -s ../xos-observer.py hpc-backend.py
-#fi
-
-export XOS_DIR=/opt/xos
-python hpc-observer.py -C $XOS_DIR/observers/hpc/hpc_observer_config
diff --git a/xos/observers/hello_world/helloworld-observer.py b/xos/observers/helloworld/helloworld-observer.py
similarity index 100%
rename from xos/observers/hello_world/helloworld-observer.py
rename to xos/observers/helloworld/helloworld-observer.py
diff --git a/xos/observers/hello_world/helloworld_config b/xos/observers/helloworld/helloworld_config
similarity index 94%
rename from xos/observers/hello_world/helloworld_config
rename to xos/observers/helloworld/helloworld_config
index 97248ae..671af51 100644
--- a/xos/observers/hello_world/helloworld_config
+++ b/xos/observers/helloworld/helloworld_config
@@ -37,7 +37,7 @@
images_directory=/opt/xos/images
dependency_graph=/opt/xos/model-deps
logfile=/var/log/xos_backend.log
-steps_dir=/opt/xos/observers/hello_world/steps
+steps_dir=/opt/xos/observers/helloworld/steps
[gui]
disable_minidashboard=True
diff --git a/xos/observers/hello_world/model-deps b/xos/observers/helloworld/model-deps
similarity index 100%
rename from xos/observers/hello_world/model-deps
rename to xos/observers/helloworld/model-deps
diff --git a/xos/observers/hello_world/nohup.out b/xos/observers/helloworld/nohup.out
similarity index 100%
rename from xos/observers/hello_world/nohup.out
rename to xos/observers/helloworld/nohup.out
diff --git a/xos/observers/helloworld/run.sh b/xos/observers/helloworld/run.sh
new file mode 100755
index 0000000..f56ffe3
--- /dev/null
+++ b/xos/observers/helloworld/run.sh
@@ -0,0 +1,6 @@
+#if [[ ! -e ./hpc-backend.py ]]; then
+# ln -s ../xos-observer.py hpc-backend.py
+#fi
+
+export XOS_DIR=/opt/xos
+python helloworld-observer.py -C $XOS_DIR/observers/helloworld/helloworld_config
diff --git a/xos/observers/hello_world/start.sh b/xos/observers/helloworld/start.sh
similarity index 100%
rename from xos/observers/hello_world/start.sh
rename to xos/observers/helloworld/start.sh
diff --git a/xos/observers/hello_world/steps/sync_hello.py b/xos/observers/helloworld/steps/sync_hello.py
similarity index 65%
rename from xos/observers/hello_world/steps/sync_hello.py
rename to xos/observers/helloworld/steps/sync_hello.py
index f59ec5c..1fb8c2b 100644
--- a/xos/observers/hello_world/steps/sync_hello.py
+++ b/xos/observers/helloworld/steps/sync_hello.py
@@ -18,7 +18,9 @@
requested_interval=0
def sync_record(self, record):
- open('/tmp/hello-synchronizer','w').write(record.name)
+ instance = record.sliver_backref
+ instance.userData="packages:\n - apache2\nruncmd:\n - update-rc.d apache2 enable\n - service apache2 start\nwrite_files:\n- content: Hello %s\n path: /var/www/html/hello.txt"%record.name
+ instance.save()
def delete_record(self, m):
return
diff --git a/xos/observers/hello_world/steps/sync_world.py b/xos/observers/helloworld/steps/sync_world.py
similarity index 100%
rename from xos/observers/hello_world/steps/sync_world.py
rename to xos/observers/helloworld/steps/sync_world.py
diff --git a/xos/observers/hello_world/stop.sh b/xos/observers/helloworld/stop.sh
similarity index 100%
rename from xos/observers/hello_world/stop.sh
rename to xos/observers/helloworld/stop.sh
diff --git a/xos/openstack_observer/steps/sync_controller_networks.py b/xos/openstack_observer/steps/sync_controller_networks.py
index 1e7805f..ef238ee 100644
--- a/xos/openstack_observer/steps/sync_controller_networks.py
+++ b/xos/openstack_observer/steps/sync_controller_networks.py
@@ -69,7 +69,7 @@
if (controller_network.network.template.name!='Private'):
logger.info("skipping network controller %s because it is not private" % controller_network)
# We only sync private networks
- return
+ return SyncStep.SYNC_WITHOUT_RUNNING
if not controller_network.controller.admin_user:
logger.info("controller %r has no admin_user, skipping" % controller_network.controller)
diff --git a/xos/openstack_observer/steps/sync_instances.py b/xos/openstack_observer/steps/sync_instances.py
index 7aa4bb7..1209448 100644
--- a/xos/openstack_observer/steps/sync_instances.py
+++ b/xos/openstack_observer/steps/sync_instances.py
@@ -56,10 +56,12 @@
controller=instance.node.site_deployment.controller)
for controller_network in controller_networks:
+
+ # Lenient exception - causes slow backoff
if controller_network.network.template.visibility == 'private' and \
controller_network.network.template.translation == 'none':
if not controller_network.net_id:
- raise Exception("Private Network %s has no id; Try again later" % controller_network.network.name)
+ raise DeferredException("Private Network %s has no id; Try again later" % controller_network.network.name)
nics.append(controller_network.net_id)
# now include network template
@@ -108,7 +110,7 @@
userData = self.get_userdata(instance, pubkeys)
if instance.userData:
- userData = instance.userData
+ userData += instance.userData
controller = instance.node.site_deployment.controller
fields = {'endpoint':controller.auth_url,
diff --git a/xos/openstack_observer/syncstep.py b/xos/openstack_observer/syncstep.py
index 66225d0..7accbfa 100644
--- a/xos/openstack_observer/syncstep.py
+++ b/xos/openstack_observer/syncstep.py
@@ -45,6 +45,12 @@
psmodel Model name the step synchronizes
dependencies list of names of models that must be synchronized first if the current model depends on them
"""
+
+ # map_sync_outputs can return this value to cause a step to be marked
+ # successful without running ansible. Used for sync_network_controllers
+ # on nat networks.
+ SYNC_WITHOUT_RUNNING = "sync_without_running"
+
slow=False
def get_prop(self, prop):
try:
@@ -128,6 +134,8 @@
pass
tenant_fields = self.map_sync_inputs(o)
+ if tenant_fields == SyncStep.SYNC_WITHOUT_RUNNING:
+ return
main_objs=self.observes
if (type(main_objs) is list):
main_objs=main_objs[0]
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index b929eda..00805e7 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -12,7 +12,11 @@
no-create:
type: boolean
default: false
- description: Do not allow Tosca to create this object)
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object)
# Service
define(xos_base_service_caps,
scalable:
@@ -55,16 +59,20 @@
kind:
type: string
default: generic
+ description: Kind of subscriber
service_specific_id:
type: string
- required: false)
+ required: false
+ description: Service specific ID opaque to XOS but meaningful to service)
define(xos_base_tenant_props,
kind:
type: string
default: generic
+ description: Kind of tenant
service_specific_id:
type: string
- required: false)
+ required: false
+ description: Service specific ID opaque to XOS but meaningful to service)
# end m4 macros
#
@@ -157,6 +165,9 @@
CORD: User. The CORD user represents an individual device beloning
to the CORD Subscriber. Each device may have its own parental
controls.
+ capabilities:
+ device:
+ type: tosca.capabilities.xos.Device
properties:
level:
type: string
@@ -257,7 +268,7 @@
topology_kind:
type: string
default: BigSwitch
- descrption: Describes the topology of the network.
+ description: Describes the topology of the network.
controller_kind:
type: string
required: false
@@ -269,7 +280,8 @@
# using derived_from.
derived_from: tosca.nodes.Root
description: >
- The TOSCA Network node represents a simple, logical network service.
+ This is a variant of the TOSCA Network object that includes additional
+ XOS-specific properties.
properties:
ip_version:
type: integer
@@ -402,6 +414,7 @@
controller:
type: tosca.capabilities.xos.Controller
properties:
+ xos_base_props
backend_type:
type: string
required: false
@@ -439,39 +452,41 @@
site:
type: tosca.capabilities.xos.Site
properties:
- display_name:
- type: string
- required: false
- description: Name of the site.
- site_url:
- type: string
- required: false
- description: URL of site web page.
- enabled:
- type: boolean
- default: true
- hosts_nodes:
- type: boolean
- default: true
- description: If True, then this site hosts nodes where Instances may be instantiated.
- hosts_users:
- type: boolean
- default: true
- description: If True, then this site hosts users who may use XOS.
- is_public:
- type: boolean
- default: true
- # location, longitude, latitude
+ xos_base_props
+ display_name:
+ type: string
+ required: false
+ description: Name of the site.
+ site_url:
+ type: string
+ required: false
+ description: URL of site web page.
+ enabled:
+ type: boolean
+ default: true
+ hosts_nodes:
+ type: boolean
+ default: true
+ description: If True, then this site hosts nodes where Instances may be instantiated.
+ hosts_users:
+ type: boolean
+ default: true
+ description: If True, then this site hosts users who may use XOS.
+ is_public:
+ type: boolean
+ default: true
+ # location, longitude, latitude
tosca.nodes.Slice:
derived_from: tosca.nodes.Root
description: >
An XOS Slice. A slice is a collection of instances that share
common attributes.
- capability:
+ capabilities:
slice:
type: tosca.capabilities.xos.Slice
properties:
+ xos_base_props
enabled:
type: boolean
default: true
@@ -493,7 +508,9 @@
description: >
An XOS Node. Nodes are physical machines that host virtual machines
and/or containers.
- capability:
+ properties:
+ xos_base_props
+ capabilities:
node:
type: tosca.capabilities.xos.Node
@@ -539,6 +556,7 @@
tosca.relationships.ConnectsToSlice:
derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Slice ]
# tosca.relationships.OwnsNetwork:
# derived_from: tosca.relationships.Root
@@ -550,25 +568,27 @@
tosca.relationships.AdminPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
tosca.relationships.AccessPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
tosca.relationships.PIPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Site ]
tosca.relationships.TechPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Site ]
tosca.relationships.SubscriberDevice:
derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Subscriber ]
tosca.relationships.BelongsToSubscriber:
derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Subscriber ]
tosca.capabilities.xos.Service:
derived_from: tosca.capabilities.Root
@@ -606,6 +626,10 @@
derived_from: tosca.capabilities.Root
description: An XOS Subscriber
+ tosca.capabilities.xos.Device:
+ derived_from: tosca.capabilities.Root
+ description: A device belonging to an XOS subscriber
+
tosca.capabilities.xos.Node:
derived_from: tosca.capabilities.Root
description: An XOS Node
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 2c674eb..2a904c1 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -189,9 +189,11 @@
kind:
type: string
default: generic
+ description: Kind of subscriber
service_specific_id:
type: string
required: false
+ description: Service specific ID opaque to XOS but meaningful to service
tosca.nodes.CORDSubscriber:
derived_from: tosca.nodes.Root
@@ -206,9 +208,11 @@
kind:
type: string
default: generic
+ description: Kind of subscriber
service_specific_id:
type: string
required: false
+ description: Service specific ID opaque to XOS but meaningful to service
firewall_enable:
type: boolean
default: false
@@ -232,6 +236,9 @@
CORD: User. The CORD user represents an individual device beloning
to the CORD Subscriber. Each device may have its own parental
controls.
+ capabilities:
+ device:
+ type: tosca.capabilities.xos.Device
properties:
level:
type: string
@@ -251,9 +258,11 @@
kind:
type: string
default: generic
+ description: Kind of tenant
service_specific_id:
type: string
required: false
+ description: Service specific ID opaque to XOS but meaningful to service
vlan_id:
type: string
required: false
@@ -337,7 +346,7 @@
topology_kind:
type: string
default: BigSwitch
- descrption: Describes the topology of the network.
+ description: Describes the topology of the network.
controller_kind:
type: string
required: false
@@ -349,7 +358,8 @@
# using derived_from.
derived_from: tosca.nodes.Root
description: >
- The TOSCA Network node represents a simple, logical network service.
+ This is a variant of the TOSCA Network object that includes additional
+ XOS-specific properties.
properties:
ip_version:
type: integer
@@ -450,6 +460,10 @@
type: boolean
default: false
description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
accessControl:
type: string
default: allow all
@@ -489,6 +503,18 @@
controller:
type: tosca.capabilities.xos.Controller
properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
backend_type:
type: string
required: false
@@ -526,39 +552,63 @@
site:
type: tosca.capabilities.xos.Site
properties:
- display_name:
- type: string
- required: false
- description: Name of the site.
- site_url:
- type: string
- required: false
- description: URL of site web page.
- enabled:
- type: boolean
- default: true
- hosts_nodes:
- type: boolean
- default: true
- description: If True, then this site hosts nodes where Instances may be instantiated.
- hosts_users:
- type: boolean
- default: true
- description: If True, then this site hosts users who may use XOS.
- is_public:
- type: boolean
- default: true
- # location, longitude, latitude
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
+ display_name:
+ type: string
+ required: false
+ description: Name of the site.
+ site_url:
+ type: string
+ required: false
+ description: URL of site web page.
+ enabled:
+ type: boolean
+ default: true
+ hosts_nodes:
+ type: boolean
+ default: true
+ description: If True, then this site hosts nodes where Instances may be instantiated.
+ hosts_users:
+ type: boolean
+ default: true
+ description: If True, then this site hosts users who may use XOS.
+ is_public:
+ type: boolean
+ default: true
+ # location, longitude, latitude
tosca.nodes.Slice:
derived_from: tosca.nodes.Root
description: >
An XOS Slice. A slice is a collection of instances that share
common attributes.
- capability:
+ capabilities:
slice:
type: tosca.capabilities.xos.Slice
properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
enabled:
type: boolean
default: true
@@ -580,7 +630,20 @@
description: >
An XOS Node. Nodes are physical machines that host virtual machines
and/or containers.
- capability:
+ properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
+ capabilities:
node:
type: tosca.capabilities.xos.Node
@@ -626,6 +689,7 @@
tosca.relationships.ConnectsToSlice:
derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Slice ]
# tosca.relationships.OwnsNetwork:
# derived_from: tosca.relationships.Root
@@ -637,25 +701,27 @@
tosca.relationships.AdminPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
tosca.relationships.AccessPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
tosca.relationships.PIPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Site ]
tosca.relationships.TechPrivilege:
derived_from: tosca.relationships.Root
- valid_target_types: [ tosca.capabiltys.xos.Site ]
+ valid_target_types: [ tosca.capabilities.xos.Site ]
tosca.relationships.SubscriberDevice:
derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Subscriber ]
tosca.relationships.BelongsToSubscriber:
derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Subscriber ]
tosca.capabilities.xos.Service:
derived_from: tosca.capabilities.Root
@@ -693,6 +759,10 @@
derived_from: tosca.capabilities.Root
description: An XOS Subscriber
+ tosca.capabilities.xos.Device:
+ derived_from: tosca.capabilities.Root
+ description: A device belonging to an XOS subscriber
+
tosca.capabilities.xos.Node:
derived_from: tosca.capabilities.Root
description: An XOS Node
diff --git a/xos/tosca/definitions/TOSCA_definition_1_0.yaml b/xos/tosca/definitions/TOSCA_definition_1_0.yaml
index c21f0bc..97a8c44 100644
--- a/xos/tosca/definitions/TOSCA_definition_1_0.yaml
+++ b/xos/tosca/definitions/TOSCA_definition_1_0.yaml
@@ -1,4 +1,4 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
@@ -47,6 +47,11 @@
tosca.nodes.Compute:
derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA Compute node represents one or more real or virtual processors
+ of software applications or services along with other essential local
+ resources. Collectively, the resources the compute node represents can
+ logically be viewed as a (real or virtual) server.
attributes:
private_address:
type: string
diff --git a/xos/tosca/makedocs.py b/xos/tosca/makedocs.py
index f7dc11b..6a9e959 100644
--- a/xos/tosca/makedocs.py
+++ b/xos/tosca/makedocs.py
@@ -24,8 +24,14 @@
def __init__(self, fn="./custom_types/xos.yaml", templatedir="./doctemplates/html", templatename="toscadoctemplate.html", destfn="tosca_reference.html"):
self.env = jinja2.Environment(loader=jinja2.FileSystemLoader(templatedir))
+ self.node_types = {}
+ self.root_types = yaml.load(file("definitions/TOSCA_definition_1_0.yaml").read())
+ for x in self.root_types.keys():
+ if x in ["tosca.nodes.Compute", "tosca.nodes.network.Network"]:
+ self.node_types[x] = self.root_types[x]
+
self.custom_types = yaml.load(file(fn).read())
- self.node_types = self.custom_types.get("node_types")
+ self.node_types.update(self.custom_types.get("node_types"))
self.destfn = destfn
self.templatename = templatename
diff --git a/xos/tosca/resources/controller.py b/xos/tosca/resources/controller.py
index da4ed64..9a20ea5 100644
--- a/xos/tosca/resources/controller.py
+++ b/xos/tosca/resources/controller.py
@@ -44,6 +44,10 @@
if obj.controllersite.exists():
self.info("Controller %s has active sites; skipping delete" % obj.name)
return
+ for sd in obj.sitedeployments.all():
+ if sd.nodes.exists():
+ self.info("Controller %s has active nodes; skipping delete" % obj.name)
+ return
super(XOSController, self).delete(obj)
diff --git a/xos/tosca/resources/deployment.py b/xos/tosca/resources/deployment.py
index 152b1f9..ed6734c 100644
--- a/xos/tosca/resources/deployment.py
+++ b/xos/tosca/resources/deployment.py
@@ -51,13 +51,13 @@
self.postprocess_privileges(DeploymentRole, DeploymentPrivilege, rolemap, obj, "deployment")
def delete(self, obj):
- if self.get_property("no-delete"):
- self.info("Deployment %s is marked no-delete")
- return
-
if obj.sites.exists():
self.info("Deployment %s has active sites; skipping delete" % obj.name)
return
+ for sd in obj.sitedeployments.all():
+ if sd.nodes.exists():
+ self.info("Deployment %s has active nodes; skipping delete" % obj.name)
+ return
#if obj.nodes.exists():
# self.info("Deployment %s has active nodes; skipping delete" % obj.name)
# return
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 62f18db..3553ab1 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -60,6 +60,12 @@
def get_property(self, name):
return self.nodetemplate.get_property_value(name)
+ def get_property_default(self, name, default=None):
+ props = self.nodetemplate.get_properties()
+ if props and name in props.keys():
+ return props[name].value
+ return default
+
def get_xos_object(self, cls, throw_exception=True, **kwargs):
objs = cls.objects.filter(**kwargs)
if not objs:
@@ -80,12 +86,21 @@
def create_or_update(self):
existing_objs = self.get_existing_objs()
if existing_objs:
- self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
- self.update(existing_objs[0])
+ if self.get_property_default("no-update", False):
+ self.info("%s %s already exists. Skipping update due to 'no-update' property" % (self.get_model_class_name(), self.nodetemplate.name))
+ else:
+ self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
+ self.update(existing_objs[0])
else:
- self.create()
+ if self.get_property_default("no-create", False):
+ self.info("%s %s does not exist, but 'no-create' is specified" % (self.get_model_class_name(), self.nodetemplate.name))
+ else:
+ self.create()
def can_delete(self, obj):
+ if self.get_property_default("no-delete",False):
+ self.info("%s %s is marked 'no-delete'. Skipping delete." % (self.get_model_class_name(), self.nodetemplate.name))
+ return False
return True
def postprocess_privileges(self, roleclass, privclass, rolemap, obj, toFieldName):
diff --git a/xos/tosca/tests/controllertest.py b/xos/tosca/tests/controllertest.py
index 5746ada..2b7ba55 100644
--- a/xos/tosca/tests/controllertest.py
+++ b/xos/tosca/tests/controllertest.py
@@ -5,7 +5,9 @@
class ControllerTest(BaseToscaTest):
tests = ["create_controller_minimal",
"create_controller_maximal",
- "destroy_controller"]
+ "create_controller_nocreate",
+ "destroy_controller",
+ "destroy_controller_nodelete"]
def cleanup(self):
self.try_to_delete(Controller, name="testcon")
@@ -53,6 +55,45 @@
domain="mydomain",
deployment=dep)
+ def create_controller_nocreate(self):
+ self.assert_noobj(Controller, "testcon")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+ reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+ props={"no-create": True}))
+ dep = self.assert_obj(Deployment, "testdep")
+ self.assert_noobj(Controller, "testcon")
+
+ def update_controller(self):
+ self.assert_noobj(Controller, "testcon")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+ reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
+ dep = self.assert_obj(Deployment, "testdep")
+ orig_con = self.assert_obj(Controller, "testcon",
+ backend_type="",
+ version="",
+ auth_url=None,
+ admin_user=None,
+ admin_password=None,
+ admin_tenant=None,
+ domain=None,
+ deployment=dep)
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+ reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+ props={"version": "1.1"}))
+ con = self.assert_obj(Controller, "testcon",
+ backend_type="",
+ version="1.1",
+ auth_url=None,
+ admin_user=None,
+ admin_password=None,
+ admin_tenant=None,
+ domain=None,
+ deployment=dep)
+ assert(orig_con.id == con.id)
+
def destroy_controller(self):
self.assert_noobj(Controller, "testcon")
self.execute(self.get_base_templates() +
@@ -64,6 +105,24 @@
reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
self.assert_noobj(Controller, "testcon")
+ def destroy_controller_nodelete(self):
+ self.assert_noobj(Controller, "testcon")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+ reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
+ orig_con = self.assert_obj(Controller, "testcon")
+ # NOTE: Had to specify no-delete on the deployment as well, otherwise
+ # the deployment deletion would cause the controller to be deleted
+ # as well. I'm thinking this is as it should be, but it's a little
+ # counter-inutitive.
+ self.destroy(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+ props={"no-delete": True}) +
+ self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+ reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+ props={"no-delete": True}))
+ con = self.assert_obj(Controller, "testcon")
+ assert(orig_con.id == con.id)
+
if __name__ == "__main__":
ControllerTest()
diff --git a/xos/tosca/tests/deploymenttest.py b/xos/tosca/tests/deploymenttest.py
index 0156613..91caf75 100644
--- a/xos/tosca/tests/deploymenttest.py
+++ b/xos/tosca/tests/deploymenttest.py
@@ -10,6 +10,9 @@
"create_deployment_one_image",
"create_deployment_two_images",
"create_deployment_privilege",
+ "create_deployment_nocreate",
+ "update_deployment",
+ "update_deployment_noupdate",
"destroy_deployment",
"destroy_deployment_nodelete"
]
@@ -88,6 +91,35 @@
dps = DeploymentPrivilege.objects.filter(user=user, deployment=dep)
assert(len(dps) == 1)
+ def create_deployment_nocreate(self):
+ self.assert_noobj(Deployment, "testdep")
+ self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+ props={"no-create": True}))
+ self.assert_noobj(Deployment, "testdep")
+
+ def update_deployment(self):
+ self.assert_noobj(Deployment, "testdep")
+ self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
+ orig_dep = self.assert_obj(Deployment, "testdep",
+ accessControl="allow all")
+ self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+ props={"accessControl": "allow padmin@vicci.org"}))
+ dep = self.assert_obj(Deployment, "testdep",
+ accessControl="allow padmin@vicci.org")
+ assert(dep.id == orig_dep.id)
+
+ def update_deployment_noupdate(self):
+ self.assert_noobj(Deployment, "testdep")
+ self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
+ orig_dep = self.assert_obj(Deployment, "testdep",
+ accessControl="allow all")
+ self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+ props={"accessControl": "allow padmin@vicci.org",
+ "no-update": True}))
+ dep = self.assert_obj(Deployment, "testdep",
+ accessControl="allow all")
+ assert(dep.id == orig_dep.id)
+
def destroy_deployment(self):
self.assert_noobj(Deployment, "testdep")
self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
diff --git a/xos/tosca/tests/nodetest.py b/xos/tosca/tests/nodetest.py
index d49dab1..76c56a8 100644
--- a/xos/tosca/tests/nodetest.py
+++ b/xos/tosca/tests/nodetest.py
@@ -4,7 +4,9 @@
class NodeTest(BaseToscaTest):
tests = ["create_node_minimal",
+ "create_node_nocreate",
"destroy_node",
+ "destroy_node_nodelete",
]
def cleanup(self):
@@ -43,7 +45,18 @@
self.make_nodetemplate("testnode", "tosca.nodes.Node",
reqs=[("testsite", "tosca.relationships.MemberOfSite"),
("testdep", "tosca.relationships.MemberOfDeployment")]))
- self.assert_obj(Node, "testnode")
+ node = self.assert_obj(Node, "testnode")
+ assert(node.site_deployment is not None)
+ assert(node.site is not None)
+
+ def create_node_nocreate(self):
+ self.assert_noobj(Node, "testnode")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testnode", "tosca.nodes.Node",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+ ("testdep", "tosca.relationships.MemberOfDeployment")],
+ props={"no-create": True}))
+ self.assert_noobj(Node, "testnode")
def destroy_node(self):
self.assert_noobj(Node, "testnode")
@@ -58,6 +71,20 @@
("testdep", "tosca.relationships.MemberOfDeployment")]))
self.assert_noobj(Node, "testnode")
+ def destroy_node_nodelete(self):
+ self.assert_noobj(Node, "testnode")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testnode", "tosca.nodes.Node",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+ ("testdep", "tosca.relationships.MemberOfDeployment")]))
+ self.assert_obj(Node, "testnode")
+ self.destroy(self.get_base_templates() +
+ self.make_nodetemplate("testnode", "tosca.nodes.Node",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+ ("testdep", "tosca.relationships.MemberOfDeployment")],
+ props={"no-delete": True}))
+ self.assert_obj(Node, "testnode")
+
if __name__ == "__main__":
NodeTest()
diff --git a/xos/tosca/tests/sitetest.py b/xos/tosca/tests/sitetest.py
index c9a4743..5321159 100644
--- a/xos/tosca/tests/sitetest.py
+++ b/xos/tosca/tests/sitetest.py
@@ -7,7 +7,11 @@
"create_site_privilege_tech",
"create_site_privilege_admin",
"create_site_privilege_pi",
+ "create_site_nocreate",
+ "update_site",
+ "update_site_noupdate",
"destroy_site",
+ "destroy_site_nodelete"
]
def cleanup(self):
@@ -60,6 +64,31 @@
assert(len(sps) == 1)
assert(sps[0].role.role == "pi")
+ def create_site_nocreate(self):
+ self.assert_noobj(Site, "testsite")
+ self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+ props={"no-create": True}))
+ site = self.assert_noobj(Site, "testsite")
+
+ def update_site(self):
+ self.assert_noobj(Site, "testsite")
+ self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+ orig_site = self.assert_obj(Site, "testsite", site_url=None)
+ self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+ props={"site_url": "http://foo.com/"}))
+ site = self.assert_obj(Site, "testsite", site_url="http://foo.com/")
+ assert(orig_site.id == site.id)
+
+ def update_site_noupdate(self):
+ self.assert_noobj(Site, "testsite")
+ self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+ orig_site = self.assert_obj(Site, "testsite", site_url=None)
+ self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+ props={"site_url": "http://foo.com/",
+ "no-update": True}))
+ site = self.assert_obj(Site, "testsite", site_url=None)
+ assert(orig_site.id == site.id)
+
def destroy_site(self):
self.assert_noobj(Site, "testsite")
self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
@@ -67,6 +96,14 @@
self.destroy(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
self.assert_noobj(Site, "testsite")
+ def destroy_site_nodelete(self):
+ self.assert_noobj(Site, "testsite")
+ self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+ site = self.assert_obj(Site, "testsite")
+ self.destroy(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+ props={"no-delete": True}))
+ self.assert_obj(Site, "testsite")
+
if __name__ == "__main__":
SiteTest()
diff --git a/xos/tosca/tests/slicetest.py b/xos/tosca/tests/slicetest.py
index 315f862..98de4e6 100644
--- a/xos/tosca/tests/slicetest.py
+++ b/xos/tosca/tests/slicetest.py
@@ -6,7 +6,11 @@
tests = ["create_slice_minimal",
"create_slice_maximal",
"create_slice_privilege",
- "destroy_slice"]
+ "create_slice_nocreate",
+ "update_slice",
+ "update_slice_noupdate",
+ "destroy_slice",
+ "destroy_slice_nodelete"]
def cleanup(self):
self.try_to_delete(Slice, name="testsite_testslice")
@@ -43,6 +47,41 @@
dps = SlicePrivilege.objects.filter(user=user, slice=slice)
assert(len(dps) == 1)
+ def create_slice_nocreate(self):
+ self.assert_noobj(Slice, "testsite_testslice")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+ props={"no-create": True}))
+ self.assert_noobj(Slice, "testsite_testslice")
+
+ def update_slice(self):
+ self.assert_noobj(Slice, "testsite_testslice")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+ orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+ props={"description": "foo"}))
+ slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="foo", slice_url="", max_instances=10)
+ assert(orig_slice.id == slice.id)
+
+ def update_slice_noupdate(self):
+ self.assert_noobj(Slice, "testsite_testslice")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+ orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+ props={"description": "foo",
+ "no-update": True}))
+ slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+ assert(orig_slice.id == slice.id)
+
def destroy_slice(self):
self.assert_noobj(Slice, "testsite_testslice")
self.execute(self.get_base_templates() +
@@ -54,6 +93,19 @@
reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
self.assert_noobj(Slice, "testsite_testslice")
+ def destroy_slice_nodelete(self):
+ self.assert_noobj(Slice, "testsite_testslice")
+ self.execute(self.get_base_templates() +
+ self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+ orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+ self.destroy(self.get_base_templates() +
+ self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+ reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+ props={"no-delete": True}))
+ slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+ assert(slice.id == orig_slice.id)
+
if __name__ == "__main__":
SliceTest()
diff --git a/xos/xos/urls.py b/xos/xos/urls.py
index 0adf32d..6ba98d8 100644
--- a/xos/xos/urls.py
+++ b/xos/xos/urls.py
@@ -8,6 +8,7 @@
from core.views.legacyapi import LegacyXMLRPC
from core.views.services import ServiceGridView, ServiceGraphView
+from helloworld.view import *
#from core.views.analytics import AnalyticsAjaxView
from core.models import *
from rest_framework import generics
@@ -27,6 +28,7 @@
# Examples:
url(r'^stats', 'core.views.stats.Stats', name='stats'),
url(r'^observer', 'core.views.observer.Observer', name='observer'),
+ url(r'^helloworld', HelloWorldView.as_view(), name='helloWorld'),
url(r'^serviceGrid', ServiceGridView.as_view(), name='serviceGrid'),
url(r'^serviceGraph.png', ServiceGraphView.as_view(), name='serviceGraph'),
url(r'^hpcConfig', 'core.views.hpc_config.HpcConfig', name='hpcConfig'),