Fixing merge conflicts
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index b69b7fd..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,144 +0,0 @@
-FROM ubuntu:14.04.3
-MAINTAINER Andy Bavier <acb@cs.princeton.edu>
-
-# XXX Workaround for docker bug:
-# https://github.com/docker/docker/issues/6345
-# Kernel 3.15 breaks docker, uss the line below as a workaround
-# until there is a fix
-RUN ln -s -f /bin/true /usr/bin/chfn
-# XXX End workaround
-
-# Install.
-RUN apt-get update && apt-get install -y \
- git \
- postgresql \
- python-psycopg2 \
- graphviz \
- graphviz-dev \
- libxslt1.1 \
- libxslt1-dev \
- python-pip \
- tar \
- gcc \
- python-httplib2 \
- geoip-database \
- libgeoip1 \
- wget \
- curl \
- python-dev \
- libyaml-dev \
- pkg-config \
- python-pycurl
-
-RUN pip install django==1.7
-RUN pip install djangorestframework==2.4.4
-RUN pip install markdown # Markdown support for the browseable API.
-RUN pip install pyyaml # YAML content-type support.
-RUN pip install django-filter # Filtering support
-RUN pip install lxml # XML manipulation library
-RUN pip install netaddr # IP Addr library
-RUN pip install pytz
-RUN pip install django-timezones
-RUN pip install requests
-RUN pip install django-crispy-forms
-RUN pip install django-geoposition
-RUN pip install django-extensions
-RUN pip install django-suit
-RUN pip install django-bitfield
-RUN pip install django-ipware
-RUN pip install django-encrypted-fields
-RUN pip install python-keyczar
-RUN pip install pygraphviz
-RUN pip install dnslib
-
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-keystoneclient
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-novaclient
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-neutronclient
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-glanceclient
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-ceilometerclient
-
-RUN pip install django_rest_swagger
-
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-setuptools
-RUN easy_install django_evolution
-RUN easy_install python_gflags
-RUN easy_install --upgrade httplib2
-RUN easy_install google_api_python_client
-RUN easy_install httplib2.ca_certs_locater
-
-# Install custom Ansible
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-crypto
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-yaml
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-client
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-paramiko
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-jinja2
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-httplib2
-RUN git clone -b release1.8.2 git://github.com/ansible/ansible.git /opt/ansible
-RUN git clone -b release1.8.2 git://github.com/ansible/ansible-modules-extras.git /opt/ansible/lib/ansible/modules/extras
-RUN git clone -b release1.8.2 git://github.com/ansible/ansible-modules-extras.git /opt/ansible/v2/ansible/modules/extras
-RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/lib/ansible/modules/core
-RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/v2/ansible/modules/core
-ADD ansible-hosts /etc/ansible/hosts
-
-ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
-
-# For Observer
-RUN git clone git://git.planet-lab.org/fofum.git /tmp/fofum
-RUN cd /tmp/fofum; python setup.py install
-RUN rm -rf /tmp/fofum
-
-RUN mkdir -p /usr/local/share /bin
-ADD http://phantomjs.googlecode.com/files/phantomjs-1.7.0-linux-x86_64.tar.bz2 /usr/local/share/
-RUN tar jxvf /usr/local/share/phantomjs-1.7.0-linux-x86_64.tar.bz2 -C /usr/local/share/
-RUN rm -f /usr/local/share/phantomjs-1.7.0-linux-x86_64.tar.bz2
-RUN ln -s /usr/local/share/phantomjs-1.7.0-linux-x86_64 /usr/local/share/phantomjs
-RUN ln -s /usr/local/share/phantomjs/bin/phantomjs /bin/phantomjs
-
-# Supervisor
-RUN DEBIAN_FRONTEND=noninteractive apt-get install -y supervisor
-ADD observer.conf /etc/supervisor/conf.d/
-
-# Get XOS
-ADD xos /opt/xos
-
-# Initscript is broken in Ubuntu
-#ADD observer-initscript /etc/init.d/xosobserver
-
-RUN chmod +x /opt/xos/scripts/opencloud
-RUN /opt/xos/scripts/opencloud genkeys
-
-# Workaround for AUFS issue
-# https://github.com/docker/docker/issues/783#issuecomment-56013588
-RUN mkdir /etc/ssl/private-copy; mv /etc/ssl/private/* /etc/ssl/private-copy/; rm -r /etc/ssl/private; mv /etc/ssl/private-copy /etc/ssl/private; chmod -R 0700 /etc/ssl/private; chown -R postgres /etc/ssl/private
-
-# Set postgres password to match default value in settings.py
-RUN service postgresql start; sudo -u postgres psql -c "alter user postgres with password 'password';"
-
-# Turn DEBUG on so that devel server will serve static files
-# (not necessary if --insecure is passed to 'manage.py runserver')
-# RUN sed -i 's/DEBUG = False/DEBUG = True/' /opt/xos/xos/settings.py
-
-# Cruft to workaround problems with migrations, should go away...
-RUN /opt/xos/scripts/opencloud remigrate
-
-# git clone uses cached copy, doesn't pick up latest
-RUN git -C /opt/ansible pull
-RUN git -C /opt/ansible/lib/ansible/modules/core pull
-RUN git -C /opt/ansible/v2/ansible/modules/core pull
-
-# install Tosca engine
-RUN apt-get install -y m4
-RUN pip install python-dateutil
-RUN bash /opt/xos/tosca/install_tosca.sh
-
-EXPOSE 8000
-
-# Set environment variables.
-ENV HOME /root
-
-# Define working directory.
-WORKDIR /root
-
-# Define default command.
-#CMD ["/bin/bash"]
-CMD /opt/xos/scripts/docker_start_xos
diff --git a/Dockerfile.cord b/Dockerfile.cord
deleted file mode 100644
index ee0879d..0000000
--- a/Dockerfile.cord
+++ /dev/null
@@ -1,7 +0,0 @@
-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
-ADD xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf /etc/supervisor/conf.d/
-RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/monitoring_channel/monitoring_channel_observer_config
diff --git a/README.Docker b/README.Docker
deleted file mode 100644
index 0076bca..0000000
--- a/README.Docker
+++ /dev/null
@@ -1,49 +0,0 @@
-The Dockerfile in this directory will build a Docker image for running
-XOS using the Django development server. It copies whatever files are
-in the local repository into the image. Here's how to do it:
-
-1. A minimal initial_data.json is provided. The login credentials
- for this initial_data.json are username=padmin@vicci.org,
- password=letmein.
-
- This initial_data.json doesn't contain any nodes and is suitable
- for fresh installations. To obtain an initial_data.json (for demo
- purposes) that contains an interesting set of Nodes and Slices,
- a dump can be made on portal.opencloud.us:
-
- 1) log in to portal, and run:
- $ sudo /opt/xos/scripts/opencloud dumpdata
-
- 2) replace the initial_data.json file with the dumpdata
- file produced above.
-
-2. $ docker build -t xos .
-
-3. $ docker run -t -i -p 8000:8000 xos
-
-4. Now you will have a bash prompt as root inside the XOS container.
- Start up XOS:
-
- # /opt/xos/scripts/opencloud runserver
-
-You can access the XOS login at http:<server>:8000, where <server> is
-the name of the server running Docker.
-
-5. From another terminal window, you can run following command to find
-the running container id
-
- $ docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- a3b668454d21 xos:latest "/bin/bash" 3 hours ago Up 3 hours 0.0.0.0:8000->8000/tcp romantic_bohr
-
-and then you can have another bash prompt (in a different TTY) as root inside the XOS container.
-
- $ docker exec -it a3b668454d21 bash
-
-and start observer
-
- # python /opt/xos/xos-observer.py
-
-STILL TO DO
------------
-* Test Observer
diff --git a/cloudlab-init.sh b/cloudlab-init.sh
deleted file mode 100755
index 20ba51a..0000000
--- a/cloudlab-init.sh
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/bin/bash
-set -x
-
-# 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
-which docker > /dev/null || wget -qO- https://get.docker.com/ | sh
-sudo usermod -aG docker $(whoami)
-
-sudo apt-get -y 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
- cp ~/.ssh/id_rsa.pub xos/observers/monitoring_channel/monitoring_channel_public_key
- cp ~/.ssh/id_rsa xos/observers/monitoring_channel/monitoring_channel_private_key
-fi
-
-cp ~/.ssh/id_rsa xos/observers/helloworldservice/helloworldservice_private_key
-
-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 )
-MYFLATLANIF=$( sudo bash -c "netstat -i" |grep flat|awk '{print $1}' )
-MYFLATLANIP=$( ifconfig $MYFLATLANIF | grep "inet addr" | awk -F: '{print $2}' | awk '{print $1}' )
-sudo docker run -d --add-host="ctl:$MYIP" -p 9999:8000 $IMAGE
-
-echo "Waiting for XOS to come up"
-until http $XOS &> /dev/null
-do
- sleep 1
-done
-
-# 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
-sudo cp /root/setup/admin-openrc.sh /tmp
-sudo chmod a+r /tmp/admin-openrc.sh
-#sudo sed -i 's/:5000/:35357/' /tmp/admin-openrc.sh
-source /tmp/admin-openrc.sh
-
-if [ "$CORD" -ne 1 ]
-then
- http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Kilo auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default
-else
- sudo cp /root/setup/settings /tmp
- sudo chmod a+r /tmp/settings
- source /tmp/settings
- source /tmp/admin-openrc.sh
- http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Kilo auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default rabbit_host=$MYFLATLANIP rabbit_user=$RABBIT_USER rabbit_password=$RABBIT_PASS
-fi
-
-# Add controller to site
-http --auth $AUTH PATCH $XOS/xos/sitedeployments/1/ controller=$XOS/xos/controllers/1/
-
-# Add image
-http --auth $AUTH POST $XOS/xos/images/ name=trusty-server-multi-nic disk_format=QCOW2 container_format=BARE
-
-# Activate image
-http --auth $AUTH POST $XOS/xos/imagedeploymentses/ deployment=$XOS/xos/deployments/1/ image=$XOS/xos/images/1/
-
-# Add node
-NODES=$( sudo bash -c "source /root/setup/admin-openrc.sh ; nova hypervisor-list" |grep cloudlab|awk '{print $4}' )
-for NODE in $NODES
-do
- http --auth $AUTH POST $XOS/xos/nodes/ name=$NODE site_deployment=$XOS/xos/sitedeployments/1/
-done
-
-# Modify networktemplate/2
-# 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=$( sudo docker ps|grep $IMAGE|awk '{print $NF}' )
- sudo docker exec $DOCKER bash -c "cd /opt/xos/tosca; python run.py padmin@vicci.org samples/cord-cloudlab.yaml; python run.py padmin@vicci.org samples/ceilometer.yaml"
-fi
diff --git a/containers/README b/containers/README
new file mode 100644
index 0000000..9891efe
--- /dev/null
+++ b/containers/README
@@ -0,0 +1,51 @@
+
+1. Introduction
+
+ XOS is comprised of 3 core services:
+
+ * A database backend (postgres)
+ * A webserver front end (django)
+ * A synchronizer daemon that interacts with the openstack backend.
+
+ We have created separate dockerfiles for each of these services, making it easier to
+ build the services independently and also deploy and run them in isolated environments.
+
+2. Database Container
+
+ To build and run the database container:
+
+ $ cd postgres; make build && make run;
+
+3. XOS container
+
+ To build and run the xos webserver container:
+
+ $ cd xos; make build && make run;
+
+ You should now be able to access the login page by visiting http://localhost:80 and
+ log in using the default paadmin account. It may be helpful to bootstrap xos with
+ some sample data; deployment, controllers, sites, slices, etc. You can get started by
+ loading tosca configuration for the opencloud demo dataset:
+
+ $ cd xos; make runtosca;
+
+ Or you can create you own tosca configuraton file and customize the dataset however you
+ want. You can all load your own tosca configuration by setting the TOSCA_CONFIG_PATH
+ environment variable before executing the make command:
+
+ $ cd xos; TOSCA_CONFIG_PATH=/path/to/tosca/config.yaml make runtosca
+
+4. Synchronizer container
+
+ The syncornonizer shares many of the same dependencies as the xos container. The synchronizer
+ container takes advantage of this by building itself on top of the xos image. This means
+ you must build the xos image before building the synchronizer image. The XOS and
+ synchronizer containers can run on separate hosts, but you must build the xos image
+ on the host that you plan to run the synchronizer container. Assuming you have already
+ built the xos container, executing the following will build and run the synchronizer container:
+
+ $ cd synchronizer; make build && make run
+
+
+
+
diff --git a/containers/observer/Makefile b/containers/observer/Makefile
deleted file mode 100644
index e7fedf5..0000000
--- a/containers/observer/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-CONTAINER_NAME:=observer-server
-
-.PHONY: build
-build: ; docker build --rm -t observer .
-
-.PHONY: run
-run: ; docker run -d --name ${CONTAINER_NAME} observer
-
-.PHONY: stop
-stop: ; docker stop ${CONTAINER_NAME}
-
-.PHONY: rm
-rm: ; docker rm ${CONTAINER_NAME}
diff --git a/containers/observer/Dockerfile b/containers/synchronizer/Dockerfile
similarity index 87%
rename from containers/observer/Dockerfile
rename to containers/synchronizer/Dockerfile
index 7ec6592..44b058e 100644
--- a/containers/observer/Dockerfile
+++ b/containers/synchronizer/Dockerfile
@@ -25,7 +25,7 @@
# For Observer
RUN mkdir -p /usr/local/share /bin /etc/ansible
-RUN cp /tmp/xos/containers/observer/conf/ansible-hosts /etc/ansible/hosts
+RUN cp /tmp/xos/containers/synchronizer/conf/ansible-hosts /etc/ansible/hosts
ADD http://phantomjs.googlecode.com/files/phantomjs-1.7.0-linux-x86_64.tar.bz2 /usr/local/share/
@@ -39,6 +39,6 @@
# Supervisor
-RUN cp /tmp/xos/containers/observer/conf/observer.conf /etc/supervisor/conf.d/
+RUN cp /tmp/xos/containers/synchronizer/conf/synchronizer.conf /etc/supervisor/conf.d/
-CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/observer.conf
+CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/synchronizer.conf
diff --git a/containers/synchronizer/Makefile b/containers/synchronizer/Makefile
new file mode 100644
index 0000000..14520d9
--- /dev/null
+++ b/containers/synchronizer/Makefile
@@ -0,0 +1,13 @@
+CONTAINER_NAME:=synchronizer-server
+
+.PHONY: build
+build: ; docker build --rm -t synchronizer .
+
+.PHONY: run
+run: ; docker run -d --name ${CONTAINER_NAME} synchronizer
+
+.PHONY: stop
+stop: ; docker stop ${CONTAINER_NAME}
+
+.PHONY: rm
+rm: ; docker rm ${CONTAINER_NAME}
diff --git a/containers/observer/conf/ansible-hosts b/containers/synchronizer/conf/ansible-hosts
similarity index 100%
rename from containers/observer/conf/ansible-hosts
rename to containers/synchronizer/conf/ansible-hosts
diff --git a/containers/observer/conf/observer.conf b/containers/synchronizer/conf/synchronizer.conf
similarity index 62%
rename from containers/observer/conf/observer.conf
rename to containers/synchronizer/conf/synchronizer.conf
index 48f61dd..cda6716 100644
--- a/containers/observer/conf/observer.conf
+++ b/containers/synchronizer/conf/synchronizer.conf
@@ -3,7 +3,7 @@
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=true
-[program:observer]
+[program:synchronizer]
command=python /opt/xos/xos-observer.py
-stderr_logfile=/var/log/supervisor/observer.err.log
-stdout_logfile=/var/log/supervisor/observer.out.log
+stderr_logfile=/var/log/supervisor/synchronizer.err.log
+stdout_logfile=/var/log/supervisor/synchronizer.out.log
diff --git a/xos/ceilometer/admin.py b/xos/ceilometer/admin.py
index 987877c..3ed70b2 100644
--- a/xos/ceilometer/admin.py
+++ b/xos/ceilometer/admin.py
@@ -81,6 +81,18 @@
form = MonitoringChannelForm
suit_form_tabs = (('general','Details'),)
+ actions=['delete_selected_objects']
+
+ def get_actions(self, request):
+ actions = super(MonitoringChannelAdmin, self).get_actions(request)
+ if 'delete_selected' in actions:
+ del actions['delete_selected']
+ return actions
+
+ def delete_selected_objects(self, request, queryset):
+ for obj in queryset:
+ obj.delete()
+ delete_selected_objects.short_description = "Delete Selected MonitoringChannel Objects"
def queryset(self, request):
return MonitoringChannel.get_tenant_objects_by_user(request.user)
diff --git a/xos/ceilometer/models.py b/xos/ceilometer/models.py
index a838c4e..3da29dd 100644
--- a/xos/ceilometer/models.py
+++ b/xos/ceilometer/models.py
@@ -29,7 +29,7 @@
sync_attributes = ("private_ip", "private_mac",
"ceilometer_ip", "ceilometer_mac",
- "nat_ip", "nat_mac",)
+ "nat_ip", "nat_mac", "ceilometer_port",)
default_attributes = {}
def __init__(self, *args, **kwargs):
@@ -37,6 +37,7 @@
if ceilometer_services:
self._meta.get_field("provider_service").default = ceilometer_services[0].id
super(MonitoringChannel, self).__init__(*args, **kwargs)
+ self.set_attribute("use_same_instance_for_multiple_tenants", True)
def save(self, *args, **kwargs):
if not self.creator:
@@ -62,7 +63,7 @@
@property
def addresses(self):
- if not self.instance:
+ if (not self.id) or (not self.instance):
return {}
addresses = {}
@@ -132,10 +133,17 @@
return ", ".join(self.tenant_list)
@property
+ def ceilometer_port(self):
+ # TODO: Find a better logic to choose unique ceilometer port number for each instance
+ if not self.id:
+ return None
+ return 8888+self.id
+
+ @property
def ceilometer_url(self):
if not self.ceilometer_ip:
return None
- return "http://" + self.private_ip + ":8888/"
+ return "http://" + self.private_ip + ":" + str(self.ceilometer_port) + "/"
def model_policy_monitoring_channel(pk):
# TODO: this should be made in to a real model_policy
diff --git a/xos/configurations/common/fixtures.yaml b/xos/configurations/common/fixtures.yaml
new file mode 100644
index 0000000..0d77ca2
--- /dev/null
+++ b/xos/configurations/common/fixtures.yaml
@@ -0,0 +1,20 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Some basic fixtures
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ s_tag:
+ type: tosca.nodes.NetworkParameterType
+
+ c_tag:
+ type: tosca.nodes.NetworkParameterType
+
+ next_hop:
+ type: tosca.nodes.NetworkParameterType
+
+ device:
+ type: tosca.nodes.NetworkParameterType
diff --git a/xos/configurations/cord/Makefile.inside b/xos/configurations/cord/Makefile.inside
index c412f25..b8e23f8 100644
--- a/xos/configurations/cord/Makefile.inside
+++ b/xos/configurations/cord/Makefile.inside
@@ -2,6 +2,7 @@
setup_xos:
bash /opt/xos/scripts/docker_setup_xos
+ python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/fixtures.yaml
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
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 8945eea..46acde9 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -7,7 +7,6 @@
topology_template:
node_templates:
-
# CORD Services
service_volt:
type: tosca.nodes.Service
@@ -29,6 +28,7 @@
view_url: /admin/cord/vcpeservice/$id$/
backend_network_label: hpc_client
public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ private_key_fn: /opt/xos/observers/vcpe/vcpe_private_key
artifacts:
pubkey: /opt/xos/observers/vcpe/vcpe_public_key
@@ -127,14 +127,15 @@
node: service_volt
relationship: tosca.relationships.UsedByService
properties:
- dependencies: org.onosproject.olt
+ dependencies: org.onosproject.openflow, org.onosproject.olt
config_network-cfg.json: >
{
"devices" : {
"of:0000000000000001" : {
"accessDevice" : {
"uplink" : "2",
- "vlan" : "400"
+ "vlan" : "222",
+ "defaultVlan" : "1"
},
"basic" : {
"driver" : "default"
@@ -402,6 +403,15 @@
node: mysite_clients
relationship: tosca.relationships.MemberOfSlice
+ # docker image for vcpe containers
+ andybavier/docker-vcpe:
+ # TODO: need to attach this to mydeployment
+ type: tosca.nodes.Image
+ properties:
+ kind: container
+ container_format: na
+ disk_format: na
+
# A subscriber
My House:
type: tosca.nodes.CORDSubscriber
diff --git a/xos/configurations/cord/dataplane/dataplane.yaml b/xos/configurations/cord/dataplane/dataplane.yaml
index 4799515..026ec89 100644
--- a/xos/configurations/cord/dataplane/dataplane.yaml
+++ b/xos/configurations/cord/dataplane/dataplane.yaml
@@ -58,6 +58,7 @@
vars:
controller_ip: "{{ hostvars['onos_volt']['ansible_ssh_host'] }}"
controller_port: 6653
+ vcpe_lan_ip: "{{ hostvars['vcpe']['lan_ip'] }}"
tags:
- volt
tasks:
@@ -73,6 +74,7 @@
with_items:
- git
- python-netifaces
+ - openvswitch-switch
- name: Checkout the Mininet repo
git: repo=https://github.com/mininet/mininet.git
@@ -87,14 +89,151 @@
script: scripts/if_from_ip.py {{ subscriber_ip }}
register: subscriber_net
- - name: Find lan_network interface
- script: scripts/if_from_ip.py {{ lan_ip }}
- register: lan_net
+ - name: Create bridge br-sub
+ openvswitch_bridge:
+ bridge=br-sub
+ state=present
+
+ - name: Add subscriber_net to br-sub
+ openvswitch_port:
+ bridge=br-sub
+ port={{ subscriber_net.stdout }}
+ state=present
+
+ # The CPqD switch is expecting that packets coming from the client have
+ # VLAN tag 1. However Neutron's OvS configuration eats VLAN-tagged packets.
+ # So tag them with VLAN 1 here before sending to CPqD.
+ #
+ # Note that the VLAN tag is 0 in the real-world setup, but the CPqD switch
+ # seems to have a problem with these packets.
+
+ # Using OvS to tag packets with VLAN ID 1 is not quite working for some reason.
+ # The packets from the client get tagged OK, but only the first packet from the
+ # VCPE gets its tag stripped off. Very weird. That's why we are using veth
+ # devices instead.
+ #- name: Add tag 1 to br-sub port
+ # shell: ovs-vsctl set port {{ subscriber_net.stdout }} tag=1
+
+ - name: Create a pair of veth devices
+ shell: ifconfig veth0 >> /dev/null || ip link add veth0 type veth peer name veth1
+
+ - name: Create veth0.1
+ shell: ifconfig veth0.1 >> /dev/null || ip link add link veth0 name veth0.1 type vlan id 1
+
+ - name: Bring the interfaces up
+ shell: ip link set {{ item }} up
+ with_items:
+ - veth0
+ - veth1
+ - veth0.1
+
+ - name: Add veth0.1 to br-sub
+ openvswitch_port:
+ bridge=br-sub
+ port=veth0.1
+ state=present
+
+ - name: Create bridge br-lan
+ openvswitch_bridge:
+ bridge=br-lan
+ state=present
+
+ - name: Create tunnel port on br-lan
+ openvswitch_port:
+ bridge=br-lan
+ port=gre0
+ state=present
+
+ - name: Set up GRE tunnel to vCPE
+ shell: ovs-vsctl set Interface gre0 type=gre options:remote_ip={{ vcpe_lan_ip }}
+
+ - name: Check if br-lan has an IPv6 address
+ shell: ip addr show br-lan|grep inet6|awk '{print $2}'
+ register: ipv6
+
+ - name: Remove br-lan IPv6 address if present
+ shell: ifconfig br-lan inet6 del {{ ipv6.stdout }}
+ when: ipv6.stdout != ""
- name: Run the datapath
- command: /usr/local/bin/ofdatapath -i {{ subscriber_net.stdout_lines[0] }},{{ lan_net.stdout_lines[0] }} punix:/tmp/s1 -d 000000000001 --no-slicing -D -P
+ command: /usr/local/bin/ofdatapath -i veth1,br-lan punix:/tmp/s1 -d 000000000001 --no-slicing -D -P
creates=/usr/local/var/run/ofdatapath.pid
- name: Run the control program
command: /usr/local/bin/ofprotocol unix:/tmp/s1 tcp:{{ controller_ip }}:{{ controller_port }} --fail=closed --listen=punix:/tmp/s1.listen -D -P
creates=/usr/local/var/run/ofprotocol.pid
+
+- hosts: client
+ sudo: yes
+ tags:
+ - client
+ tasks:
+
+ - name: Fix /etc/hosts
+ lineinfile:
+ dest=/etc/hosts
+ regexp="127.0.0.1 localhost"
+ line="127.0.0.1 localhost {{ ansible_hostname }}"
+
+ - name: Install packages
+ apt: name={{ item }}
+ state=latest
+ update_cache=yes
+ with_items:
+ - openvswitch-switch
+ - python-netifaces
+
+ - name: Create br-sub
+ openvswitch_bridge:
+ bridge=br-sub
+ state=present
+
+ - name: Find subscriber_network interface
+ script: scripts/if_from_ip.py {{ subscriber_ip }}
+ register: client_net
+
+ - name: Hook up subscriber-network to OvS
+ openvswitch_port:
+ bridge=br-sub
+ port={{ client_net.stdout }}
+ state=present
+
+ # Run dhclient on br-sub internal interface to issue DHCP request to vCPE
+
+#
+# This play is just for testing. The vCPE configuration below will be
+# integrated with the vCPE Synchronizer.
+#
+# Need to change the data model to store both s-tag and c-tag
+#
+- hosts: vcpe
+ sudo: yes
+ vars:
+ volt_lan_ip: "{{ hostvars['switch_volt']['lan_ip'] }}"
+ tags:
+ - vcpe
+ tasks:
+
+ - name: Install packages
+ apt: name={{ item }}
+ state=latest
+ update_cache=yes
+ with_items:
+ - openvswitch-switch
+
+ - name: Create br-lan
+ openvswitch_bridge:
+ bridge=br-lan
+ state=present
+
+ - name: Create tunnel port
+ openvswitch_port:
+ bridge=br-lan
+ port=gre0
+ state=present
+
+ - name: Configure GRE tunnel to vOLT switch
+ shell: ovs-vsctl set Interface gre0 type=gre options:remote_ip={{ volt_lan_ip }}
+
+ - name: Restart vCPEs
+ script: scripts/restart-vcpes.sh
diff --git a/xos/configurations/cord/dataplane/scripts/if_from_ip.py b/xos/configurations/cord/dataplane/scripts/if_from_ip.py
index be1da48..28524fe 100644
--- a/xos/configurations/cord/dataplane/scripts/if_from_ip.py
+++ b/xos/configurations/cord/dataplane/scripts/if_from_ip.py
@@ -8,7 +8,7 @@
for iface in netifaces.interfaces():
addrs = netifaces.ifaddresses(iface)
if 2 in addrs and addrs[2][0]['addr'] == addr:
- print iface
-
+ sys.stdout.write(iface)
+
if __name__ == "__main__":
main(sys.argv[1:])
diff --git a/xos/configurations/cord/dataplane/scripts/restart-vcpes.sh b/xos/configurations/cord/dataplane/scripts/restart-vcpes.sh
new file mode 100644
index 0000000..d1c9fce
--- /dev/null
+++ b/xos/configurations/cord/dataplane/scripts/restart-vcpes.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+for VCPE in $( docker ps|grep vcpe|awk '{print $NF}' )
+do
+ service $VCPE stop
+ sleep 1
+ service $VCPE start
+done
diff --git a/xos/configurations/cord/install_ceilometer_vcpe_notification_listener.sh b/xos/configurations/cord/install_ceilometer_vcpe_notification_listener.sh
index 50a4132..1992e80 100755
--- a/xos/configurations/cord/install_ceilometer_vcpe_notification_listener.sh
+++ b/xos/configurations/cord/install_ceilometer_vcpe_notification_listener.sh
@@ -11,6 +11,6 @@
echo "Copying the ceilometer vcpe notification agent files /usr/lib/python2.7/dist-packages/ceilometer"
tar -xzf ceilometer_vcpe_notification_agent.tar.gz
sudo mv ceilometer/network/ext_services /usr/lib/python2.7/dist-packages/ceilometer/network/
-sudo mv ceilometer-2015.1.1.egg-info/entry_points.txt /usr/lib/python2.7/dist-packages/ceilometer-2015.1.1.egg-info/
+sudo mv ceilometer-2015.1.1.egg-info/entry_points.txt /usr/lib/python2.7/dist-packages/ceilometer-*egg-info/
echo "Restarting ceilometer-agent-notification"
sudo service ceilometer-agent-notification restart
diff --git a/xos/cord/admin.py b/xos/cord/admin.py
index 981f275..686d8ae 100644
--- a/xos/cord/admin.py
+++ b/xos/cord/admin.py
@@ -162,7 +162,6 @@
bbs_account = forms.CharField(required=False)
creator = forms.ModelChoiceField(queryset=User.objects.all())
instance = forms.ModelChoiceField(queryset=Instance.objects.all(),required=False)
- use_cobm = forms.BooleanField(required=False)
last_ansible_hash = forms.CharField(required=False)
def __init__(self,*args,**kwargs):
@@ -174,7 +173,6 @@
self.fields['bbs_account'].initial = self.instance.bbs_account
self.fields['creator'].initial = self.instance.creator
self.fields['instance'].initial = self.instance.instance
- self.fields['use_cobm'].initial = self.instance.use_cobm
self.fields['last_ansible_hash'].initial = self.instance.last_ansible_hash
if (not self.instance) or (not self.instance.pk):
# default fields for an 'add' form
@@ -182,13 +180,11 @@
self.fields['creator'].initial = get_request().user
if VCPEService.get_service_objects().exists():
self.fields["provider_service"].initial = VCPEService.get_service_objects().all()[0]
- self.fields['use_cobm'].initial = False
def save(self, commit=True):
self.instance.creator = self.cleaned_data.get("creator")
self.instance.instance = self.cleaned_data.get("instance")
self.instance.last_ansible_hash = self.cleaned_data.get("last_ansible_hash")
- self.instance.use_cobm = self.cleaned_data.get("use_cobm")
return super(VCPETenantForm, self).save(commit=commit)
class Meta:
@@ -198,7 +194,7 @@
list_display = ('backend_status_icon', 'id', 'subscriber_tenant' )
list_display_links = ('backend_status_icon', 'id')
fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', 'subscriber_tenant', 'service_specific_id', # 'service_specific_attribute',
- 'bbs_account', 'creator', 'use_cobm', 'instance', 'last_ansible_hash'],
+ 'bbs_account', 'creator', 'instance', 'last_ansible_hash'],
'classes':['suit-tab suit-tab-general']})]
readonly_fields = ('backend_status_text', 'service_specific_attribute', 'bbs_account')
form = VCPETenantForm
diff --git a/xos/cord/models.py b/xos/cord/models.py
index afbe3ef..a4e7501 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, TenantWithContainer, Node, Image, User, Flavor, Subscriber
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port
from core.models.plcorebase import StrippedCharField
import os
from django.db import models, transaction
@@ -351,7 +351,6 @@
vcpe = VCPETenant(provider_service = vcpeServices[0],
subscriber_tenant = self)
vcpe.caller = self.creator
- # vcpe.use_cobm = True # XXX XXX XXX remove before checking XXX XXX XXX
vcpe.save()
def manage_subscriber(self):
@@ -679,6 +678,36 @@
self.bbs_account = None
super(VCPETenant, self).save()
+ def find_or_make_port(self, instance, network, **kwargs):
+ port = Port.objects.filter(instance=instance, network=network)
+ if port:
+ port = port[0]
+ else:
+ port = Port(instance=instance, network=network, **kwargs)
+ port.save()
+ return port
+
+ def save_instance(self, instance):
+ with transaction.atomic():
+ instance.volumes = "/etc/dnsmasq.d"
+ super(VCPETenant, self).save_instance(instance)
+
+ if instance.isolation in ["container", "container_vm"]:
+ lan_networks = [x for x in instance.slice.networks.all() if "lan" in x.name]
+ if not lan_networks:
+ raise XOSProgrammingError("No lan_network")
+ port = self.find_or_make_port(instance, lan_networks[0], ip="192.168.0.1", port_id="unmanaged")
+ port.set_parameter("c_tag", self.volt.c_tag)
+ port.set_parameter("s_tag", self.volt.s_tag)
+ port.set_parameter("device", "eth1")
+
+ wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
+ if not wan_networks:
+ raise XOSProgrammingError("No wan_network")
+ port = self.find_or_make_port(instance, wan_networks[0])
+ port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
+ port.set_parameter("device", "eth0")
+
def save(self, *args, **kwargs):
if not self.creator:
if not getattr(self, "caller", None):
diff --git a/xos/core/admin.py b/xos/core/admin.py
index ce7759c..26c6dba 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -880,7 +880,7 @@
class ServiceAdmin(XOSBaseAdmin):
list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
list_display_links = ('backend_status_icon', 'name', )
- fieldList = ["backend_status_text","name","kind","description","versionNumber","enabled","published","view_url","icon_url","public_key","service_specific_attribute","service_specific_id"]
+ fieldList = ["backend_status_text","name","kind","description","versionNumber","enabled","published","view_url","icon_url","public_key","private_key_fn","service_specific_attribute","service_specific_id"]
fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
readonly_fields = ('backend_status_text', )
@@ -1051,7 +1051,7 @@
class SliceAdmin(XOSBaseAdmin):
form = SliceForm
- fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_instances']
+ fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_instances', "default_isolation"]
fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
readonly_fields = ('backend_status_text', )
list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_instances')
@@ -1273,7 +1273,7 @@
fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
readonly_fields = ("backend_status_icon", "ip", "mac")
model = Port
- selflink_fieldname = "network"
+ #selflink_fieldname = "network"
extra = 0
verbose_name_plural = "Ports"
verbose_name = "Port"
@@ -1282,7 +1282,7 @@
class InstanceAdmin(XOSBaseAdmin):
form = InstanceForm
fieldsets = [
- ('Instance Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'isolation', 'flavor', 'image', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'ssh_command', ], 'classes': ['suit-tab suit-tab-general'], }),
+ ('Instance Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'isolation', 'flavor', 'image', 'node', 'parent', 'all_ips_string', 'instance_id', 'instance_name', 'ssh_command', ], 'classes': ['suit-tab suit-tab-general'], }),
('Container Settings', {'fields': ['volumes'], 'classes': ['suit-tab suit-tab-container'], }),
]
readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
@@ -1764,7 +1764,7 @@
fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
readonly_fields = ("backend_status_icon", "ip", "mac")
model = Port
- selflink_fieldname = "instance"
+ #selflink_fieldname = "instance"
extra = 0
verbose_name_plural = "Ports"
verbose_name = "Port"
@@ -1843,10 +1843,25 @@
list_display_links = ('backend_status_icon', 'name', )
user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
user_readonly_inlines = []
+ inlines = [NetworkParameterInline,]
fieldsets = [
(None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
'classes':['suit-tab suit-tab-general']}),]
- suit_form_tabs = (('general','Network Template Details'), )
+ suit_form_tabs = (('general','Network Template Details'), ('netparams', 'Parameters') )
+
+class PortAdmin(XOSBaseAdmin):
+ list_display = ("backend_status_icon", "name", "id", "ip")
+ list_display_links = ('backend_status_icon', 'id')
+ readonly_fields = ("subnet", )
+ inlines = [NetworkParameterInline]
+
+ fieldsets = [
+ (None, {'fields': ['backend_status_text', 'network', 'instance', 'ip', 'port_id', 'mac'],
+ 'classes':['suit-tab suit-tab-general']}),
+ ]
+
+ readonly_fields = ('backend_status_text', )
+ suit_form_tabs = (('general', 'Port Details'), ('netparams', 'Parameters'))
class FlavorAdmin(XOSBaseAdmin):
list_display = ("backend_status_icon", "name", "flavor", "order", "default")
@@ -2035,6 +2050,7 @@
admin.site.register(Service, ServiceAdmin)
#admin.site.register(Reservation, ReservationAdmin)
admin.site.register(Network, NetworkAdmin)
+admin.site.register(Port, PortAdmin)
admin.site.register(Router, RouterAdmin)
admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
admin.site.register(Program, ProgramAdmin)
diff --git a/xos/core/models/container.py b/xos/core/models/container.py
deleted file mode 100644
index ae1d198..0000000
--- a/xos/core/models/container.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import os
-from django.db import models
-from django.db.models import Q
-from django.core import exceptions
-from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager
-from core.models.plcorebase import StrippedCharField
-from core.models import Image
-from core.models import Slice, SlicePrivilege
-from core.models import Node
-from core.models import Site
-from core.models import Deployment
-from core.models import Controller
-from core.models import User
-from core.models import Tag
-from core.models import Flavor
-from django.contrib.contenttypes import generic
-from xos.config import Config
-from monitor import driver as monitor
-from django.core.exceptions import PermissionDenied, ValidationError
-
-config = Config()
-
-
-# Create your models here.
-class Container(PlCoreBase):
- name = StrippedCharField(max_length=200, help_text="Container name")
- slice = models.ForeignKey(Slice, related_name='containers')
- node = models.ForeignKey(Node, related_name='containers')
- creator = models.ForeignKey(User, related_name='containers', blank=True, null=True)
- docker_image = StrippedCharField(null=True, blank=True, max_length=200, help_text="name of docker container to instantiate")
- volumes = models.TextField(null=True, blank=True, help_text="Comma-separated list of volumes")
-
- def __unicode__(self):
- return u'container-%s' % str(self.id)
-
- def save(self, *args, **kwds):
- if not self.name:
- self.name = self.slice.name
- if not self.creator and hasattr(self, 'caller'):
- self.creator = self.caller
- if not self.creator:
- raise ValidationError('container has no creator')
-
- if (self.slice.creator != self.creator):
- # Check to make sure there's a slice_privilege for the user. If there
- # isn't, then keystone will throw an exception inside the observer.
- slice_privs = SlicePrivilege.objects.filter(slice=self.slice, user=self.creator)
- if not slice_privs:
- raise ValidationError('container creator has no privileges on slice')
-
-# XXX smbaker - disabled for now, was causing fault in tenant view create slice
-# if not self.controllerNetwork.test_acl(slice=self.slice):
-# raise exceptions.ValidationError("Deployment %s's ACL does not allow any of this slice %s's users" % (self.controllerNetwork.name, self.slice.name))
-
- super(Container, self).save(*args, **kwds)
-
- def can_update(self, user):
- return True
-
- @staticmethod
- def select_by_user(user):
- if user.is_admin:
- qs = Container.objects.all()
- else:
- slices = Slice.select_by_user(user)
- qs = Container.objects.filter(slice__in=slices)
- return qs
-
- def get_public_keys(self):
- slice_memberships = SlicePrivilege.objects.filter(slice=self.slice)
- pubkeys = set([sm.user.public_key for sm in slice_memberships if sm.user.public_key])
-
- if self.creator.public_key:
- pubkeys.add(self.creator.public_key)
-
- if self.slice.creator.public_key:
- pubkeys.add(self.slice.creator.public_key)
-
- if self.slice.service and self.slice.service.public_key:
- pubkeys.add(self.slice.service.public_key)
-
- return pubkeys
-
-
diff --git a/xos/core/models/instance.py b/xos/core/models/instance.py
index 927c8e3..cd7dd26 100644
--- a/xos/core/models/instance.py
+++ b/xos/core/models/instance.py
@@ -80,7 +80,7 @@
# Create your models here.
class Instance(PlCoreBase):
- ISOLATION_CHOICES = (('vm', 'Virtual Machine'), ('container', 'Container'), )
+ ISOLATION_CHOICES = (('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))
objects = InstanceManager()
deleted_objects = InstanceDeletionManager()
@@ -90,7 +90,6 @@
instance_name = StrippedCharField(blank=True, null=True, max_length=200, help_text="OpenStack generated name")
ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
image = models.ForeignKey(Image, related_name='instances')
- #key = models.ForeignKey(Key, related_name='instances')
creator = models.ForeignKey(User, related_name='instances', blank=True, null=True)
slice = models.ForeignKey(Slice, related_name='instances')
deployment = models.ForeignKey(Deployment, verbose_name='deployment', related_name='instance_deployment')
@@ -101,6 +100,7 @@
userData = models.TextField(blank=True, null=True, help_text="user_data passed to instance during creation")
isolation = models.CharField(null=False, blank=False, max_length=30, choices=ISOLATION_CHOICES, default="vm")
volumes = models.TextField(null=True, blank=True, help_text="Comma-separated list of directories to expose to parent context")
+ parent = models.ForeignKey("Instance", null=True, blank=True, help_text="Parent Instance for containers nested inside of VMs")
def __unicode__(self):
if self.name and Slice.objects.filter(id=self.slice_id) and (self.name != self.slice.name):
@@ -124,6 +124,19 @@
if not self.creator:
raise ValidationError('instance has no creator')
+ if (self.isolation == "container") or (self.isolation == "container_vm"):
+ if (self.image.kind != "container"):
+ raise ValidationError("Container instance must use container image")
+ elif (self.isolation == "vm"):
+ if (self.image.kind != "vm"):
+ raise ValidationError("VM instance must use VM image")
+
+ if (self.isolation == "container_vm") and (not self.parent):
+ raise ValidationError("Container-vm instance must have a parent")
+
+ if (self.parent) and (self.isolation != "container_vm"):
+ raise ValidationError("Parent field can only be set on Container-vm instances")
+
if (self.slice.creator != self.creator):
# Check to make sure there's a slice_privilege for the user. If there
# isn't, then keystone will throw an exception inside the observer.
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index b12068c..6894f9f 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -64,7 +64,38 @@
except Exception,e:
raise ValidationError(str(e))
-class NetworkTemplate(PlCoreBase):
+class ParameterMixin(object):
+ # helper classes for dealing with NetworkParameter
+
+ def get_parameters(self):
+ parameter_dict = {}
+
+ instance_type = ContentType.objects.get_for_model(self)
+ for param in NetworkParameter.objects.filter(content_type__pk=instance_type.id, object_id=self.id):
+ parameter_dict[param.parameter.name] = param.value
+
+ return parameter_dict
+
+ def set_parameter(self, name, value):
+ instance_type = ContentType.objects.get_for_model(self)
+ existing_params = NetworkParameter.objects.filter(parameter__name=name, content_type__pk=instance_type.id, object_id=self.id)
+ if existing_params:
+ p=existing_params[0]
+ p.value = value
+ p.save()
+ else:
+ pt = NetworkParameterType.objects.get(name=name)
+ p = NetworkParameter(parameter=pt, content_type=instance_type, object_id=self.id, value=value)
+ p.save()
+
+ def unset_parameter(self, name):
+ instance_type = ContentType.objects.get_for_model(self)
+ existing_params = NetworkParameter.objects.filter(parameter__name=name, content_type__pk=instance_type.id, object_id=self.id)
+ for p in existing_params:
+ p.delete()
+
+
+class NetworkTemplate(PlCoreBase, ParameterMixin):
VISIBILITY_CHOICES = (('public', 'public'), ('private', 'private'))
TRANSLATION_CHOICES = (('none', 'none'), ('NAT', 'NAT'))
TOPOLOGY_CHOICES = (('bigswitch', 'BigSwitch'), ('physical', 'Physical'), ('custom', 'Custom'))
@@ -97,7 +128,7 @@
def __unicode__(self): return u'%s' % (self.name)
-class Network(PlCoreBase):
+class Network(PlCoreBase, ParameterMixin):
name = models.CharField(max_length=32)
template = models.ForeignKey(NetworkTemplate)
subnet = models.CharField(max_length=32, blank=True)
@@ -147,6 +178,14 @@
qs = Network.objects.filter(owner__in=slices)
return qs
+ def get_parameters(self):
+ # returns parameters from the template, updated by self.
+ p={}
+ if self.template:
+ p = self.template.get_parameters()
+ p.update(ParameterMixin.get_parameters(self))
+ return p
+
class ControllerNetwork(PlCoreBase):
objects = ControllerLinkManager()
deleted_objects = ControllerLinkDeletionManager()
@@ -161,7 +200,7 @@
class Meta:
unique_together = ('network', 'controller')
-
+
@staticmethod
def select_by_user(user):
if user.is_admin:
@@ -208,14 +247,12 @@
qs = NetworkSlice.objects.filter(Q(slice__in=slice_ids) | Q(network__in=network_ids))
return qs
-class Port(PlCoreBase):
+class Port(PlCoreBase, ParameterMixin):
network = models.ForeignKey(Network,related_name='links')
instance = models.ForeignKey(Instance, null=True, blank=True, related_name='ports')
ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
- port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum port id")
+ port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Neutron port id")
mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
- #unattached = models.BooleanField(default=False, help_text="create this port even if no Instance is attached")
- segmentation_id = models.CharField(null=True, blank=True, max_length=256, help_text="GRE segmentation id for port")
class Meta:
unique_together = ('network', 'instance')
@@ -257,6 +294,14 @@
qs = Port.objects.filter(Q(instance__in=instance_ids) | Q(network__in=network_ids))
return qs
+ def get_parameters(self):
+ # returns parameters from the network, updated by self.
+ p={}
+ if self.network:
+ p = self.network.get_parameters()
+ p.update(ParameterMixin.get_parameters(self))
+ return p
+
class Router(PlCoreBase):
name = models.CharField(max_length=32)
owner = models.ForeignKey(Slice, related_name="routers")
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 9646ce8..49c534a 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -52,6 +52,7 @@
view_url = StrippedCharField(blank=True, null=True, max_length=1024)
icon_url = StrippedCharField(blank=True, null=True, max_length=1024)
public_key = models.TextField(null=True, blank=True, max_length=1024, help_text="Public key string")
+ private_key_fn = StrippedCharField(blank=True, null=True, max_length=1024)
# Service_specific_attribute and service_specific_id are opaque to XOS
service_specific_id = StrippedCharField(max_length=30, blank=True, null=True)
@@ -346,6 +347,8 @@
"trusty-server-multi-nic", # CloudLab
]
+ LOOK_FOR_CONTAINER_IMAGES=["andybavier/docker-vcpe"]
+
class Meta:
proxy = True
@@ -353,8 +356,6 @@
super(TenantWithContainer, self).__init__(*args, **kwargs)
self.cached_instance=None
self.orig_instance_id = self.get_initial_attribute("instance_id")
-# self.cached_container=None
-# self.orig_container_id = self.get_initial_attribute("container_id")
@property
@@ -381,30 +382,6 @@
self.cached_instance=None
self.set_attribute("instance_id", value)
-# @property
-# def container(self):
-# from core.models import Container
-# if getattr(self, "cached_container", None):
-# return self.cached_container
-# container_id=self.get_attribute("container_id")
-# if not container_id:
-# return None
-# containers=Container.objects.filter(id=container_id)
-# if not containers:
-# return None
-# container=containers[0]
-# container.caller = self.creator
-# self.cached_container = container
-# return container
-#
-# @container.setter
-# def container(self, value):
-# if value:
-# value = value.id
-# if (value != self.get_attribute("container_id", None)):
-# self.cached_container=None
-# self.set_attribute("container_id", value)
-
@property
def creator(self):
from core.models import User
@@ -433,20 +410,23 @@
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:
+
+ slice = self.provider_service.slices.all()
+ if not slice:
+ raise XOSProgrammingError("provider service has no slice")
+ slice = slice[0]
+
+ if slice.default_isolation in ["container", "container_vm"]:
+ look_for_images = self.LOOK_FOR_CONTAINER_IMAGES
+ else:
+ look_for_images = self.LOOK_FOR_IMAGES
+
+ for image_name in 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))
-
- @property
- def use_cobm(self):
- return self.get_attribute("use_cobm", False)
-
- @use_cobm.setter
- def use_cobm(self, v):
- self.set_attribute("use_cobm", v)
+ raise XOSProgrammingError("No VPCE image (looked for %s)" % str(self.look_for_images))
@creator.setter
def creator(self, value):
@@ -464,15 +444,35 @@
nodes = sorted(nodes, key=lambda node: node.instances.all().count())
return nodes[0]
-# def pick_node_for_container_on_metal(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.containers.all().count())
-# return nodes[0]
+ def save_instance(self, instance):
+ # Override this function to do custom pre-save or post-save processing,
+ # such as creating ports for containers.
+ instance.save()
- def manage_container_in_instance(self):
+ def pick_vm(self):
+ # for container-in-VM, pick a VM
+ raise "Not Implemented"
+
+ def pick_least_loaded_instance_in_slice(self, slices):
+ for slice in slices:
+ if slice.instances.all().count() > 0:
+ for instance in slice.instances.all():
+ #Pick the first instance that has lesser than 5 tenants
+ if self.count_of_tenants_of_an_instance(instance) < 5:
+ return instance
+ return None
+
+ #TODO: Ideally the tenant count for an instance should be maintained using a
+ #many-to-one relationship attribute, however this model being proxy, it does
+ #not permit any new attributes to be defined. Find if any better solutions
+ def count_of_tenants_of_an_instance(self, instance):
+ tenant_count = 0
+ for tenant in self.get_tenant_objects().all():
+ if tenant.get_attribute("instance_id", None) == instance.id:
+ tenant_count += 1
+ return tenant_count
+
+ def manage_container(self):
from core.models import Instance, Flavor
if self.deleted:
@@ -484,105 +484,57 @@
if self.instance is None:
if not self.provider_service.slices.count():
- raise XOSConfigurationError("The VCPE service has no slices")
+ raise XOSConfigurationError("The service has no slices")
- flavors = Flavor.objects.filter(name="m1.small")
- if not flavors:
- raise XOSConfigurationError("No m1.small flavor")
+ new_instance_created = False
+ instance = None
+ if self.get_attribute("use_same_instance_for_multiple_tenants", default=False):
+ #Find if any existing instances can be used for this tenant
+ slices = self.provider_service.slices.all()
+ instance = self.pick_least_loaded_instance_in_slice(slices)
- node =self.pick_node_for_instance()
- instance = Instance(slice = self.provider_service.slices.all()[0],
- node = node,
- image = self.image,
- creator = self.creator,
- deployment = node.site_deployment.deployment,
- flavor = flavors[0])
- instance.save()
+ if not instance:
+ flavors = Flavor.objects.filter(name="m1.small")
+ if not flavors:
+ raise XOSConfigurationError("No m1.small flavor")
+
+ node =self.pick_node_for_instance()
+ slice = self.provider_service.slices.all()[0]
+
+ if slice.default_isolation == "container_vm":
+ parent = self.pick_vm()
+ else:
+ parent = None
+
+ instance = Instance(slice = slice,
+ node = node,
+ image = self.image,
+ creator = self.creator,
+ deployment = node.site_deployment.deployment,
+ flavor = flavors[0],
+ isolation = slice.default_isolation,
+ parent = parent)
+ self.save_instance(instance)
+ new_instance_created = True
try:
self.instance = instance
super(TenantWithContainer, self).save()
except:
- instance.delete()
+ if new_instance_created:
+ instance.delete()
raise
-# def manage_container_on_metal(self):
-# from core.models import Container, Instance, Flavor, Port
-#
-# if self.deleted:
-# return
-#
-# if (self.container is not None):
-# self.container.delete()
-# self.container = None
-#
-# if self.container is None:
-# if not self.provider_service.slices.count():
-# raise XOSConfigurationError("The VCPE service has no slices")
-#
-# slice = self.provider_service.slices.all()[0]
-# node = self.pick_node_for_container_on_metal()
-#
-# # Our current docker network strategy requires that there be some
-# # instance on the server that connects to the networks, so that
-# # the containers can piggyback off of that configuration.
-# instances = Instance.objects.filter(slice=slice, node=node)
-# if not instances:
-# flavors = Flavor.objects.filter(name="m1.small")
-# if not flavors:
-# raise XOSConfigurationError("No m1.small flavor")
-#
-# node =self.pick_node_for_instance()
-# instance = Instance(slice = self.provider_service.slices.all()[0],
-# node = node,
-# image = self.image,
-# creator = self.creator,
-# deployment = node.site_deployment.deployment,
-# flavor = flavors[0])
-# instance.save()
-#
-# # Now make the container...
-# container = Container(slice = slice,
-# node = node,
-# docker_image = "andybavier/docker-vcpe",
-# creator = self.creator,
-# no_sync=True)
-# container.save()
-#
-# # ... and add the ports for the container
-# # XXX probably should be done in model_policy
-# for network in slice.networks.all():
-# if (network.name.endswith("-nat")):
-# continue
-# port = Port(network = network,
-# container = container)
-# port.save()
-#
-# container.no_sync = False
-# container.save()
-#
-# try:
-# self.container = container
-# super(TenantWithContainer, self).save()
-# except:
-# container.delete()
-# raise
-
- def manage_container(self):
-# if self.use_cobm:
-# self.manage_container_on_metal()
-# else:
- self.manage_container_in_instance()
-
def cleanup_container(self):
if self.instance:
- # print "XXX cleanup instance", self.instance
- self.instance.delete()
+ if self.get_attribute("use_same_instance_for_multiple_tenants", default=False):
+ #Delete the instance only if this is last tenant in that instance
+ tenant_count = self.count_of_tenants_of_an_instance(self.instance)
+ if tenant_count == 0:
+ self.instance.delete()
+ else:
+ self.instance.delete()
self.instance = None
-# if self.container:
-# # print "XXX cleanup container", self.container
-# self.container.delete()
-# self.container = None
class CoarseTenant(Tenant):
""" TODO: rename "CoarseTenant" --> "StaticTenant" """
diff --git a/xos/core/models/slice.py b/xos/core/models/slice.py
index 18d3cb6..df36b26 100644
--- a/xos/core/models/slice.py
+++ b/xos/core/models/slice.py
@@ -19,6 +19,8 @@
# Create your models here.
class Slice(PlCoreBase):
+ ISOLATION_CHOICES = (('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))
+
name = StrippedCharField(unique=True, help_text="The Name of the Slice", max_length=80)
enabled = models.BooleanField(default=True, help_text="Status for this Slice")
omf_friendly = models.BooleanField(default=False)
@@ -37,6 +39,8 @@
default_image = models.ForeignKey(Image, related_name = "slices", null=True, blank=True);
mount_data_sets = StrippedCharField(default="GenBank",null=True, blank=True, max_length=256)
+ default_isolation = models.CharField(null=False, blank=False, max_length=30, choices=ISOLATION_CHOICES, default="vm")
+
def __unicode__(self): return u'%s' % (self.name)
@property
diff --git a/xos/model_policies/model_policy_Instance.py b/xos/model_policies/model_policy_Instance.py
index 23761f3..40f52bb 100644
--- a/xos/model_policies/model_policy_Instance.py
+++ b/xos/model_policies/model_policy_Instance.py
@@ -44,5 +44,5 @@
cn.backend_register = '{}'
cn.save()
- if (instance.isolation=="container"):
+ if (instance.isolation in ["container", "container_vm"]):
handle_container_on_metal(instance)
diff --git a/xos/observers/base/SyncInstanceUsingAnsible.py b/xos/observers/base/SyncInstanceUsingAnsible.py
index 901bc97..5bb8250 100644
--- a/xos/observers/base/SyncInstanceUsingAnsible.py
+++ b/xos/observers/base/SyncInstanceUsingAnsible.py
@@ -44,9 +44,11 @@
return o.instance
- def run_playbook(self, o, fields):
+ def run_playbook(self, o, fields, template_name=None):
+ if not template_name:
+ template_name = self.template_name
tStart = time.time()
- run_template_ssh(self.template_name, fields)
+ run_template_ssh(template_name, fields)
logger.info("playbook execution time %d" % int(time.time()-tStart))
def pre_sync_hook(self, o, fields):
@@ -61,14 +63,83 @@
def prepare_record(self, o):
pass
+ def get_node(self,o):
+ return o.node
+
+ def get_node_key(self, node):
+ return "/root/setup/node_key"
+
+ def get_ansible_fields(self, instance):
+ # return all of the fields that tell Ansible how to talk to the context
+ # that's setting up the container.
+
+ if (instance.isolation == "vm"):
+ # legacy where container was configured by sync_vcpetenant.py
+
+ fields = { "instance_name": instance.name,
+ "hostname": instance.node.name,
+ "instance_id": instance.instance_id,
+ "username": "ubuntu",
+ }
+ key_name = self.service_key_name
+ elif (instance.isolation == "container"):
+ # container on bare metal
+ node = self.get_node(instance)
+ hostname = node.name
+ fields = { "hostname": hostname,
+ "baremetal_ssh": True,
+ "instance_name": "rootcontext",
+ "username": "root",
+ "container_name": "%s-%s" % (instance.slice.name, str(instance.id))
+ }
+ key_name = self.get_node_key(node)
+ else:
+ # container in a VM
+ if not instance.parent:
+ raise Exception("Container-in-VM has no parent")
+ if not instance.parent.instance_id:
+ raise Exception("Container-in-VM parent is not yet instantiated")
+ if not instance.parent.slice.service:
+ raise Exception("Container-in-VM parent has no service")
+ if not instance.parent.slice.service.private_key_fn:
+ raise Exception("Container-in-VM parent service has no private_key_fn")
+ fields = { "hostname": instance.parent.node.name,
+ "instance_name": instance.parent.name,
+ "instance_id": instance.parent.instance_id,
+ "username": "ubuntu",
+ "nat_ip": instance.parent.get_ssh_ip(),
+ "container_name": "%s-%s" % (instance.slice.name, str(instance.id))
+ }
+ key_name = instance.parent.slice.service.private_key_fn
+
+ if not os.path.exists(key_name):
+ raise Exception("Node key %s does not exist" % node_key_name)
+
+ key = file(key_name).read()
+
+ fields["private_key"] = key
+
+ # now the ceilometer stuff
+
+ cslice = ControllerSlice.objects.get(slice=instance.slice)
+ if not cslice:
+ raise Exception("Controller slice object for %s does not exist" % instance.slice.name)
+
+ cuser = ControllerUser.objects.get(user=instance.creator)
+ if not cuser:
+ raise Exception("Controller user object for %s does not exist" % instance.creator)
+
+ fields.update({"keystone_tenant_id": cslice.tenant_id,
+ "keystone_user_id": cuser.kuser_id,
+ "rabbit_user": instance.controller.rabbit_user,
+ "rabbit_password": instance.controller.rabbit_password,
+ "rabbit_host": instance.controller.rabbit_host})
+
+ return fields
+
def sync_record(self, o):
logger.info("sync'ing object %s" % str(o))
- if not os.path.exists(self.service_key_name):
- raise Exception("Service key %s does not exist" % self.service_key_name)
-
- service_key = file(self.service_key_name).read()
-
self.prepare_record(o)
instance = self.get_instance(o)
@@ -92,25 +163,9 @@
self.defer_sync(o, "waiting on instance.instance_name")
return
- cslice = ControllerSlice.objects.get(slice=instance.slice)
- if not cslice:
- raise Exception("Controller slice object for %s does not exist" % instance.slice.name)
+ fields = self.get_ansible_fields(instance)
- cuser = ControllerUser.objects.get(user=instance.creator)
- if not cuser:
- raise Exception("Controller user object for %s does not exist" % instance.creator)
-
- fields = { "instance_name": instance.name,
- "hostname": instance.node.name,
- "instance_id": instance.instance_id,
- "private_key": service_key,
- "keystone_tenant_id": cslice.tenant_id,
- "keystone_user_id": cuser.kuser_id,
- "rabbit_user": instance.controller.rabbit_user,
- "rabbit_password": instance.controller.rabbit_password,
- "rabbit_host": instance.controller.rabbit_host,
- "ansible_tag": o.__class__.__name__ + "_" + str(o.id)
- }
+ fields["ansible_tag"] = o.__class__.__name__ + "_" + str(o.id)
# If 'o' defines a 'sync_attributes' list, then we'll copy those
# attributes into the Ansible recipe's field list automatically.
diff --git a/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml
index bbe284f..fb4b73d 100644
--- a/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml
+++ b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml
@@ -15,6 +15,7 @@
headnode_flat_lan_ip: {{ rabbit_host }}
ceilometer_client_acess_ip: {{ ceilometer_ip }}
ceilometer_client_acess_mac: {{ ceilometer_mac }}
+ ceilometer_host_port: {{ ceilometer_port }}
allowed_tenant_ids:
{% for allowed_tenant_id in allowed_tenant_ids %}
- {{ allowed_tenant_id }}
diff --git a/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2 b/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2
index 10d9ef5..f56c247 100755
--- a/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2
+++ b/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2
@@ -15,13 +15,14 @@
MONITORING_CHANNEL=monitoring-channel-{{ unique_id }}
HEADNODEFLATLANIP={{ headnode_flat_lan_ip }}
+HOST_FORWARDING_PORT_FOR_CEILOMETER={{ ceilometer_host_port }}
docker inspect $MONITORING_CHANNEL > /dev/null 2>&1
if [ "$?" == 1 ]
then
#sudo docker build -t monitoring-channel -f Dockerfile.monitoring_channel .
sudo docker pull srikanthvavila/monitoring-channel
- docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p 8888:8000 srikanthvavila/monitoring-channel
+ docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 srikanthvavila/monitoring-channel
else
docker start $MONITORING_CHANNEL
fi
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.py b/xos/observers/vcpe/steps/sync_vcpetenant.py
index 120954b..4f3886e 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.py
@@ -131,7 +131,8 @@
"dnsdemux_ip": dnsdemux_ip,
"cdn_prefixes": cdn_prefixes,
"bbs_addrs": bbs_addrs,
- "full_setup": full_setup}
+ "full_setup": full_setup,
+ "isolation": o.instance.isolation}
# add in the sync_attributes that come from the SubscriberRoot object
@@ -209,7 +210,10 @@
if quick_update:
logger.info("quick_update triggered; skipping ansible recipe")
else:
- super(SyncVCPETenant, self).run_playbook(o, fields)
+ if o.instance.isolation in ["container", "container_vm"]:
+ super(SyncVCPETenant, self).run_playbook(o, fields, "sync_vcpetenant_new.yaml")
+ else:
+ super(SyncVCPETenant, self).run_playbook(o, fields)
o.last_ansible_hash = ansible_hash
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.yaml b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
index e4d3167..c3b7246 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
@@ -117,27 +117,27 @@
{% endif %}
- name: vCPE upstart
- template: src=/opt/xos/observers/vcpe/templates/vcpe.conf.j2 dest=/etc/init/vcpe-{{ vlan_ids[0] }}.conf
+ template: src=/opt/xos/observers/vcpe/templates/vcpe.conf.j2 dest=/etc/init/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}.conf
- name: vCPE startup script
- template: src=/opt/xos/observers/vcpe/templates/start-vcpe.sh.j2 dest=/usr/local/sbin/start-vcpe-{{ vlan_ids[0] }}.sh mode=0755
+ template: src=/opt/xos/observers/vcpe/templates/start-vcpe.sh.j2 dest=/usr/local/sbin/start-vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}.sh mode=0755
notify:
# - restart vcpe
- stop vcpe
- remove container
- start vcpe
- - name: create /etc/vcpe-{{ vlan_ids[0] }}/dnsmasq.d
- file: path=/etc/vcpe-{{ vlan_ids[0] }}/dnsmasq.d state=directory owner=root group=root
+ - name: create /etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d
+ file: path=/etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d state=directory owner=root group=root
- name: vCPE basic dnsmasq config
- copy: src=/opt/xos/observers/vcpe/files/vcpe.dnsmasq dest=/etc/vcpe-{{ vlan_ids[0] }}/dnsmasq.d/vcpe.conf owner=root group=root
+ copy: src=/opt/xos/observers/vcpe/files/vcpe.dnsmasq dest=/etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d/vcpe.conf owner=root group=root
notify:
- stop dnsmasq
- start dnsmasq
- name: dnsmasq config
- template: src=/opt/xos/observers/vcpe/templates/dnsmasq_servers.j2 dest=/etc/vcpe-{{ vlan_ids[0] }}/dnsmasq.d/servers.conf owner=root group=root
+ template: src=/opt/xos/observers/vcpe/templates/dnsmasq_servers.j2 dest=/etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d/servers.conf owner=root group=root
notify:
- stop dnsmasq
- start dnsmasq
@@ -151,23 +151,23 @@
# template: src=/opt/xos/observers/vcpe/templates/firewall_sample.j2 dest=/etc/firewall_sample owner=root group=root
- name: Make sure vCPE service is running
- service: name=vcpe-{{ vlan_ids[0] }} state=started
+ service: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=started
handlers:
- name: stop dnsmasq
- shell: docker exec vcpe-{{ vlan_ids[0] }} /usr/bin/killall dnsmasq
+ shell: docker exec vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} /usr/bin/killall dnsmasq
- name: start dnsmasq
- shell: docker exec vcpe-{{ vlan_ids[0] }} /usr/sbin/service dnsmasq start
+ shell: docker exec vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} /usr/sbin/service dnsmasq start
- name: restart vcpe
- shell: service vcpe-{{ vlan_ids[0] }} stop; sleep 1; service vcpe-{{ vlan_ids[0] }} start
+ shell: service vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} stop; sleep 1; service vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} start
- name: stop vcpe
- service: name=vcpe-{{ vlan_ids[0] }} state=stopped
+ service: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=stopped
- name: remove container
- docker: name=vcpe-{{ vlan_ids[0] }} state=absent image=docker-vcpe
+ docker: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=absent image=docker-vcpe
- name: start vcpe
- service: name=vcpe-{{ vlan_ids[0] }} state=started
+ service: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=started
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant_new.yaml b/xos/observers/vcpe/steps/sync_vcpetenant_new.yaml
new file mode 100644
index 0000000..e96f3c5
--- /dev/null
+++ b/xos/observers/vcpe/steps/sync_vcpetenant_new.yaml
@@ -0,0 +1,85 @@
+---
+- hosts: {{ instance_name }}
+ gather_facts: False
+ connection: ssh
+ user: {{ username }}
+ sudo: yes
+ vars:
+ container_name: {{ container_name }}
+ cdn_enable: {{ cdn_enable }}
+ dnsdemux_ip: {{ dnsdemux_ip }}
+ firewall_enable: {{ firewall_enable }}
+ url_filter_enable: {{ url_filter_enable }}
+ vlan_ids:
+ {% for vlan_id in vlan_ids %}
+ - {{ vlan_id }}
+ {% endfor %}
+ c_tags:
+ {% for c_tag in c_tags %}
+ - {{ c_tag }}
+ {% endfor %}
+ s_tags:
+ {% for s_tag in s_tags %}
+ - {{ s_tag }}
+ {% endfor %}
+ firewall_rules:
+ {% for firewall_rule in firewall_rules.split("\n") %}
+ - {{ firewall_rule }}
+ {% endfor %}
+ cdn_prefixes:
+ {% for prefix in cdn_prefixes %}
+ - {{ prefix }}
+ {% endfor %}
+ bbs_addrs:
+ {% for bbs_addr in bbs_addrs %}
+ - {{ bbs_addr }}
+ {% endfor %}
+ nat_ip: {{ nat_ip }}
+ nat_mac: {{ nat_mac }}
+ lan_ip: {{ lan_ip }}
+ lan_mac: {{ lan_mac }}
+ wan_ip: {{ wan_ip }}
+ wan_mac: {{ wan_mac }}
+ wan_container_mac: {{ wan_container_mac }}
+ wan_next_hop: 10.0.1.253 # FIX ME
+ private_ip: {{ private_ip }}
+ private_mac: {{ private_mac }}
+ hpc_client_ip: {{ hpc_client_ip }}
+ hpc_client_mac: {{ hpc_client_mac }}
+ keystone_tenant_id: {{ keystone_tenant_id }}
+ keystone_user_id: {{ keystone_user_id }}
+ rabbit_user: {{ rabbit_user }}
+ rabbit_password: {{ rabbit_password }}
+ rabbit_host: {{ rabbit_host }}
+
+ tasks:
+ - name: vCPE basic dnsmasq config
+ copy: src=/opt/xos/observers/vcpe/files/vcpe.dnsmasq dest=/var/container_volumes/{{ container_name }}/etc/dnsmasq.d/vcpe.conf owner=root group=root
+ notify:
+ - stop dnsmasq
+ - start dnsmasq
+
+ - name: dnsmasq config
+ template: src=/opt/xos/observers/vcpe/templates/dnsmasq_servers.j2 dest=/var/container_volumes/{{ container_name }}/etc/dnsmasq.d/servers.conf owner=root group=root
+ notify:
+ - stop dnsmasq
+ - start dnsmasq
+
+ handlers:
+ - name: stop dnsmasq
+ shell: docker exec {{ container_name }} /usr/bin/killall dnsmasq
+
+ - name: start dnsmasq
+ shell: docker exec {{ container_name }} /usr/sbin/service dnsmasq start
+
+ - name: restart vcpe
+ shell: service {{ container_name }} stop; sleep 1; service vcpe-{{ vlan_ids[0] }} start
+
+ - name: stop vcpe
+ service: name={{ container_name }} state=stopped
+
+ - name: remove container
+ docker: name={{ container_name }} state=absent image=docker-vcpe
+
+ - name: start vcpe
+ service: name={{ container_name }} state=started
diff --git a/xos/observers/vcpe/templates/start-vcpe.sh.j2 b/xos/observers/vcpe/templates/start-vcpe.sh.j2
index a3533fa..c4128f3 100755
--- a/xos/observers/vcpe/templates/start-vcpe.sh.j2
+++ b/xos/observers/vcpe/templates/start-vcpe.sh.j2
@@ -8,7 +8,9 @@
iptables -L > /dev/null
ip6tables -L > /dev/null
-VCPE=vcpe-{{ vlan_ids[0] }}
+STAG={{ s_tags[0] }}
+CTAG={{ c_tags[0] }}
+VCPE=vcpe-$STAG-$CTAG
docker inspect $VCPE > /dev/null 2>&1
if [ "$?" == 1 ]
@@ -23,14 +25,23 @@
WAN_IFACE=$( mac_to_iface {{ wan_mac }} )
docker exec $VCPE ifconfig eth0 >> /dev/null || pipework $WAN_IFACE -i eth0 $VCPE {{ wan_ip }}/24@{{ wan_next_hop }} {{ 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] }}
+# LAN_IFACE=$( mac_to_iface {{ lan_mac }} )
+# Need to encapsulate VLAN traffic so that Neutron doesn't eat it
+# Assumes that br-lan has been set up appropriately by a previous step
+LAN_IFACE=br-lan
+ifconfig $LAN_IFACE >> /dev/null
+if [ "$?" == 0 ]
+then
+ ifconfig $LAN_IFACE.$STAG >> /dev/null || ip link add link $LAN_IFACE name $LAN_IFACE.$STAG type vlan id $STAG
+ ifconfig $LAN_IFACE.$STAG up
+ docker exec $VCPE ifconfig eth1 >> /dev/null || pipework $LAN_IFACE.$STAG -i eth1 $VCPE 192.168.0.1/24 @$CTAG
+fi
-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
+#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 $HPC_IFACE 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/templates/vcpe.conf.j2 b/xos/observers/vcpe/templates/vcpe.conf.j2
index 1951322..fa7885e 100644
--- a/xos/observers/vcpe/templates/vcpe.conf.j2
+++ b/xos/observers/vcpe/templates/vcpe.conf.j2
@@ -6,5 +6,5 @@
respawn
script
- /usr/local/sbin/start-vcpe-{{ vlan_ids[0] }}.sh
+ /usr/local/sbin/start-vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}.sh
end script
diff --git a/xos/openstack_observer/event_loop.py b/xos/openstack_observer/event_loop.py
index db7459a..0ac626b 100644
--- a/xos/openstack_observer/event_loop.py
+++ b/xos/openstack_observer/event_loop.py
@@ -32,7 +32,7 @@
# Load app models
try:
- app_module_names = Config().observer_applist
+ app_module_names = Config().observer_applist.split(',')
except AttributeError:
app_module_names = []
diff --git a/xos/openstack_observer/steps/sync_container.py b/xos/openstack_observer/steps/sync_container.py
index 26e03e5..fefd415 100644
--- a/xos/openstack_observer/steps/sync_container.py
+++ b/xos/openstack_observer/steps/sync_container.py
@@ -6,6 +6,7 @@
import time
from django.db.models import F, Q
from xos.config import Config
+from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
from observer.syncstep import SyncStep
from observer.ansible import run_template_ssh
from core.models import Service, Slice, Instance
@@ -18,7 +19,7 @@
logger = Logger(level=logging.INFO)
-class SyncContainer(SyncStep):
+class SyncContainer(SyncInstanceUsingAnsible):
provides=[Instance]
observes=Instance
requested_interval=0
@@ -29,15 +30,9 @@
def fetch_pending(self, deletion=False):
objs = super(SyncContainer, self).fetch_pending(deletion)
- objs = [x for x in objs if x.isolation=="container"]
+ objs = [x for x in objs if x.isolation in ["container", "container_vm"]]
return objs
- def get_node(self,o):
- return o.node
-
- def get_node_key(self, node):
- return "/root/setup/node_key"
-
def get_instance_port(self, container_port):
for p in container_port.network.links.all():
if (p.instance) and (p.instance.isolation=="vm") and (p.instance.node == container_port.instance.node) and (p.mac):
@@ -48,56 +43,60 @@
i=0
ports = []
for port in o.ports.all():
- if not port.mac:
+ if (not port.ip):
+ # 'unmanaged' ports may have an ip, but no mac
+ # XXX: are there any ports that have a mac but no ip?
raise Exception("Port on network %s is not yet ready" % port.network.name)
pd={}
- pd["device"] = "eth%d" % i
- pd["mac"] = port.mac
- pd["ip"] = port.ip
+ pd["mac"] = port.mac or ""
+ pd["ip"] = port.ip or ""
+ pd["xos_network_id"] = port.network.id
- instance_port = self.get_instance_port(port)
- if not instance_port:
- raise Exception("No instance on slice for port on network %s" % port.network.name)
+ if o.isolation == "container":
+ # container on bare metal
+ instance_port = self.get_instance_port(port)
+ if not instance_port:
+ raise Exception("No instance on slice for port on network %s" % port.network.name)
- pd["snoop_instance_mac"] = instance_port.mac
- pd["snoop_instance_id"] = instance_port.instance.instance_id
+ pd["snoop_instance_mac"] = instance_port.mac
+ pd["snoop_instance_id"] = instance_port.instance.instance_id
+ pd["src_device"] = ""
+ else:
+ # container in VM
+ pd["snoop_instance_mac"] = ""
+ pd["snoop_instance_id"] = ""
+ pd["src_device"] = "eth%d" % i
+
+ for (k,v) in port.get_parameters().items():
+ pd[k] = v
ports.append(pd)
- i = i + 1
+ # for any ports that don't have a device, assign one
+ used_ports = [x["device"] for x in ports if ("device" in x)]
+ avail_ports = ["eth%d"%i for i in range(0,64) if ("eth%d"%i not in used_ports)]
+ for port in ports:
+ if not port.get("device",None):
+ port["device"] = avail_ports.pop(0)
return ports
def get_extra_attributes(self, o):
fields={}
fields["ansible_tag"] = "container-%s" % str(o.id)
- fields["baremetal_ssh"] = True
- fields["instance_name"] = "rootcontext"
- fields["container_name"] = "%s-%s" % (o.slice.name, str(o.id))
fields["docker_image"] = o.image.name
- fields["username"] = "root"
fields["ports"] = self.get_ports(o)
- fields["volumes"] = [x.strip() for x in o.volumes.split(",")]
+ if o.volumes:
+ fields["volumes"] = [x.strip() for x in o.volumes.split(",")]
+ else:
+ fields["volumes"] = ""
return fields
- def sync_fields(self, o, fields):
- self.run_playbook(o, fields)
-
def sync_record(self, o):
logger.info("sync'ing object %s" % str(o))
- node = self.get_node(o)
- node_key_name = self.get_node_key(node)
-
- if not os.path.exists(node_key_name):
- raise Exception("Node key %s does not exist" % node_key_name)
-
- node_key = file(node_key_name).read()
-
- fields = { "hostname": node.name,
- "private_key": node_key,
- }
+ fields = self.get_ansible_fields(o)
# If 'o' defines a 'sync_attributes' list, then we'll copy those
# attributes into the Ansible recipe's field list automatically.
@@ -107,14 +106,33 @@
fields.update(self.get_extra_attributes(o))
- self.sync_fields(o, fields)
+ self.run_playbook(o, fields)
+
+ o.instance_id = fields["container_name"]
+ o.instance_name = fields["container_name"]
o.save()
- def run_playbook(self, o, fields):
+ def delete_record(self, o):
+ logger.info("delete'ing object %s" % str(o))
+
+ fields = self.get_ansible_fields(o)
+
+ # If 'o' defines a 'sync_attributes' list, then we'll copy those
+ # attributes into the Ansible recipe's field list automatically.
+ if hasattr(o, "sync_attributes"):
+ for attribute_name in o.sync_attributes:
+ fields[attribute_name] = getattr(o, attribute_name)
+
+ fields.update(self.get_extra_attributes(o))
+
+ self.run_playbook(o, fields, "teardown_container.yaml")
+
+ def run_playbook(self, o, fields, template_name=None):
+ if not template_name:
+ template_name = self.template_name
tStart = time.time()
- run_template_ssh(self.template_name, fields, path="container")
+ run_template_ssh(template_name, fields, path="container")
logger.info("playbook execution time %d" % int(time.time()-tStart))
- def delete_record(self, m):
- pass
+
diff --git a/xos/openstack_observer/steps/sync_container.yaml b/xos/openstack_observer/steps/sync_container.yaml
index e005e58..7fb6331 100644
--- a/xos/openstack_observer/steps/sync_container.yaml
+++ b/xos/openstack_observer/steps/sync_container.yaml
@@ -11,10 +11,15 @@
ports:
{% for port in ports %}
- device: {{ port.device }}
- mac: {{ port.mac }}
+ xos_network_id: {{ port.xos_network_id }}
+ mac: {{ port.mac|default("") }}
ip: {{ port.ip }}
snoop_instance_mac: {{ port.snoop_instance_mac }}
snoop_instance_id: {{ port.snoop_instance_id }}
+ src_device: {{ port.src_device }}
+ s_tag: {{ port.s_tag|default("") }}
+ c_tag: {{ port.c_tag|default("") }}
+ next_hop: {{ port.next_hop|default("") }}
{% endfor %}
volumes:
{% for volume in volumes %}
@@ -78,6 +83,10 @@
# state: running
# image: {{ docker_image }}
+ - name: check if systemd is installed
+ stat: path=/usr/bin/systemctl
+ register: systemctl
+
- name: container upstart
template: src=/opt/xos/openstack_observer/templates/container.conf.j2 dest=/etc/init/container-{{ container_name }}.conf
@@ -87,8 +96,12 @@
- name: container startup script
template: src=/opt/xos/openstack_observer/templates/start-container.sh.j2 dest=/usr/local/sbin/start-container-{{ container_name }}.sh mode=0755
+ - name: container teardown script
+ template: src=/opt/xos/openstack_observer/templates/stop-container.sh.j2 dest=/usr/local/sbin/stop-container-{{ container_name }}.sh mode=0755
+
- name: restart systemd
shell: systemctl daemon-reload
+ when: systemctl.stat.exists == True
- name: Make sure container is running
service: name=container-{{ container_name }} state=started
diff --git a/xos/openstack_observer/steps/sync_ports.py b/xos/openstack_observer/steps/sync_ports.py
index 178fa86..bfdde8c 100644
--- a/xos/openstack_observer/steps/sync_ports.py
+++ b/xos/openstack_observer/steps/sync_ports.py
@@ -185,10 +185,6 @@
if neutron_port["fixed_ips"]:
port.ip = neutron_port["fixed_ips"][0]["ip_address"]
port.mac = neutron_port["mac_address"]
-
- neutron_network = driver.shell.quantum.list_networks(cn.net_id)["networks"][0]
- if "provider:segmentation_id" in neutron_network:
- port.segmentation_id = neutron_network["provider:segmentation_id"]
except:
logger.log_exc("failed to create neutron port for %s" % port)
continue
diff --git a/xos/openstack_observer/templates/container.conf.j2 b/xos/openstack_observer/templates/container.conf.j2
index d3ef42d..7cbb880 100644
--- a/xos/openstack_observer/templates/container.conf.j2
+++ b/xos/openstack_observer/templates/container.conf.j2
@@ -6,6 +6,9 @@
respawn
script
- /usr/local/sbin/start-container-{{ container_name }}.sh
+ /usr/local/sbin/start-container-{{ container_name }}.sh ATTACH
end script
+post-stop script
+ /usr/local/sbin/stop-container-{{ container_name }}.sh
+end script
\ No newline at end of file
diff --git a/xos/openstack_observer/templates/container.service.j2 b/xos/openstack_observer/templates/container.service.j2
index 9e2b83c..817d6d7 100644
--- a/xos/openstack_observer/templates/container.service.j2
+++ b/xos/openstack_observer/templates/container.service.j2
@@ -3,7 +3,9 @@
After=docker.service
[Service]
-ExecStart=/bin/bash -c "/usr/local/sbin/start-container-{{ container_name }}.sh"
+ExecStart=/bin/bash -c "/usr/local/sbin/start-container-{{ container_name }}.sh ATTACH"
+ExecStop=/bin/bash -c "/usr/local/sbin/stop-container-{{ container_name }}.sh"
+SuccessExitStatus=0 137
[Install]
WantedBy=multi-user.target
diff --git a/xos/openstack_observer/templates/start-container.sh.j2 b/xos/openstack_observer/templates/start-container.sh.j2
index 86491eb..42d93a2 100644
--- a/xos/openstack_observer/templates/start-container.sh.j2
+++ b/xos/openstack_observer/templates/start-container.sh.j2
@@ -25,16 +25,48 @@
{% if ports %}
{% for port in ports %}
+
+{% if port.next_hop %}
+NEXTHOP_ARG="@{{ port.next_hop }}"
+{% else %}
+NEXTHOP_ARG=""
+{% endif %}
+
+{% if port.c_tag %}
+CTAG_ARG="@{{ port.c_tag }}"
+{% else %}
+CTAG_ARG=""
+{% endif %}
+
+{% if port.src_device %}
+# container-in-VM
+VLAN_ARG=""
+NEXTHOP_ARG=""
+CMD="docker exec $CONTAINER ifconfig {{ port.src_device }} >> /dev/null || pipework {{ port.src_device }} -i {{ port.device }} $CONTAINER {{ port.ip }}/24$NEXTHOP_ARG {{ port.mac }} $CTAG_ARG"
+echo $CMD
+eval $CMD
+
+{% else %}
+# container-on-metal
IP="{{ port.ip }}"
+{% if port.mac %}
MAC="{{ port.mac }}"
+{% else %}
+MAC=""
+{% endif %}
+
DEVICE="{{ port.device }}"
+XOS_NETWORK_ID="{{ port.xos_network_id }}"
INSTANCE_MAC="{{ port.snoop_instance_mac }}"
INSTANCE_ID="{{ port.snoop_instance_id }}"
INSTANCE_TAP=`virsh domiflist $INSTANCE_ID | grep -i $INSTANCE_MAC | awk '{print $1}'`
INSTANCE_TAP=${INSTANCE_TAP:3}
VLAN_ID=`ovs-vsctl show | grep -i -A 1 port.*$INSTANCE_TAP | grep -i tag | awk '{print $2}'`
-TAP="con`echo $CONTAINER_$DEVICE|md5sum|awk '{print $1}'`"
-TAP=${TAP:0:12}
+# One tap for all containers per XOS/neutron network. Included the VLAN_ID in the
+# hash, to cover the case where XOS is reinstalled and the XOS network ids
+# get reused.
+TAP="con`echo ${XOS_NETWORK_ID}_$VLAN_ID|md5sum|awk '{print $1}'`"
+TAP=${TAP:0:10}
echo im=$INSTANCE_MAC ii=$INSTANCE_ID it=$INSTANCE_TAP vlan=$VLAN_ID tap=$TAP con=$CONTAINER dev=$DEVICE mac=$MAC
ovs-vsctl show | grep -i $TAP
if [[ $? == 1 ]]; then
@@ -44,9 +76,17 @@
echo tap exists
fi
-docker exec $CONTAINER ifconfig $DEVICE >> /dev/null || pipework $TAP -i $DEVICE $CONTAINER $IP/24 $MAC
+CMD="docker exec $CONTAINER ifconfig $DEVICE >> /dev/null || pipework $TAP -i $DEVICE $CONTAINER $IP/24$NEXTHOP_ARG $MAC $CTAG_ARG"
+echo $CMD
+eval $CMD
+{% endif %}
{% endfor %}
{% endif %}
# Attach to container
-# docker start -a $CONTAINER
+# (this is only done when using upstart, since upstart expects to be attached
+# to a running service)
+if [[ "$1" == "ATTACH" ]]; then
+ docker start -a $CONTAINER
+fi
+
diff --git a/xos/openstack_observer/templates/stop-container.sh.j2 b/xos/openstack_observer/templates/stop-container.sh.j2
new file mode 100644
index 0000000..9cabb00
--- /dev/null
+++ b/xos/openstack_observer/templates/stop-container.sh.j2
@@ -0,0 +1,4 @@
+CONTAINER={{ container_name }}
+
+docker stop $CONTAINER
+docker rm $CONTAINER
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index aca3e3e..3606601 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -47,6 +47,10 @@
type: string
required: false
description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
versionNumber:
type: string
required: false
@@ -292,6 +296,17 @@
required: false
description: Indicates what page the user should go to on login.
+ tosca.nodes.NetworkParameterType:
+ derived_from: tosca.nodes.Root
+
+ description: >
+ An XOS network parameter type. May be applied to Networks and/or
+ Ports.
+
+ capabilities:
+ network_parameter_type:
+ type: tosca.capabilities.xos.NetworkParameterType
+
tosca.nodes.NetworkTemplate:
derived_from: tosca.nodes.Root
@@ -447,6 +462,10 @@
image:
type: tosca.capabilities.xos.Image
properties:
+ kind:
+ type: string
+ required: false
+ description: Type of image (container | VM)
disk_format:
type: string
required: false
@@ -598,6 +617,31 @@
required: false
description: URL to the dashboard
+ tosca.nodes.Compute.Container:
+ derived_from: tosca.nodes.Compute
+ description: >
+ The TOSCA Compute node represents a container on bare metal.
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ binding:
+ type: tosca.capabilities.network.Bindable
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+
tosca.relationships.MemberOfSlice:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Slice ]
@@ -638,6 +682,10 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Network ]
+ tosca.relationships.UsesImage:
+ derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Image ]
+
tosca.relationships.SupportsImage:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Image ]
@@ -733,3 +781,7 @@
tosca.capabilities.xos.DashboardView:
derived_from: tosca.capabilities.Root
description: An XOS DashboardView
+
+ tosca.capabilities.xos.NetworkParameterType:
+ derived_from: tosca.capabilities.Root
+ description: An XOS NetworkParameterType
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 63b4e0c..7a6030e 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -52,6 +52,10 @@
type: string
required: false
description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
versionNumber:
type: string
required: false
@@ -90,6 +94,10 @@
type: string
required: false
description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
versionNumber:
type: string
required: false
@@ -191,6 +199,10 @@
type: string
required: false
description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
versionNumber:
type: string
required: false
@@ -233,6 +245,10 @@
type: string
required: false
description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
versionNumber:
type: string
required: false
@@ -275,6 +291,10 @@
type: string
required: false
description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
versionNumber:
type: string
required: false
@@ -420,6 +440,17 @@
required: false
description: Indicates what page the user should go to on login.
+ tosca.nodes.NetworkParameterType:
+ derived_from: tosca.nodes.Root
+
+ description: >
+ An XOS network parameter type. May be applied to Networks and/or
+ Ports.
+
+ capabilities:
+ network_parameter_type:
+ type: tosca.capabilities.xos.NetworkParameterType
+
tosca.nodes.NetworkTemplate:
derived_from: tosca.nodes.Root
@@ -586,6 +617,10 @@
image:
type: tosca.capabilities.xos.Image
properties:
+ kind:
+ type: string
+ required: false
+ description: Type of image (container | VM)
disk_format:
type: string
required: false
@@ -792,6 +827,31 @@
required: false
description: URL to the dashboard
+ tosca.nodes.Compute.Container:
+ derived_from: tosca.nodes.Compute
+ description: >
+ The TOSCA Compute node represents a container on bare metal.
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ binding:
+ type: tosca.capabilities.network.Bindable
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+
tosca.relationships.MemberOfSlice:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Slice ]
@@ -832,6 +892,10 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Network ]
+ tosca.relationships.UsesImage:
+ derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Image ]
+
tosca.relationships.SupportsImage:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Image ]
@@ -927,3 +991,7 @@
tosca.capabilities.xos.DashboardView:
derived_from: tosca.capabilities.Root
description: An XOS DashboardView
+
+ tosca.capabilities.xos.NetworkParameterType:
+ derived_from: tosca.capabilities.Root
+ description: An XOS NetworkParameterType
diff --git a/xos/tosca/resources/VOLTTenant.py b/xos/tosca/resources/VOLTTenant.py
index 89c24e3..20c4dfb 100644
--- a/xos/tosca/resources/VOLTTenant.py
+++ b/xos/tosca/resources/VOLTTenant.py
@@ -7,7 +7,7 @@
import pdb
from core.models import User
-from cord.models import VOLTTenant, VOLTService, CordSubscriberRoot
+from cord.models import VOLTTenant, VOLTService, CordSubscriberRoot, VOLT_KIND
from xosresource import XOSResource
@@ -32,10 +32,12 @@
def get_existing_objs(self):
args = self.get_xos_args(throw_exception=False)
- provider_service = args.get("provider", None)
+ provider_service = args.get("provider_service", 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) ]
+ existing_obj = self.get_xos_object(VOLTTenant, kind=VOLT_KIND, provider_service=provider_service, service_specific_id=service_specific_id, throw_exception=False)
+ if existing_obj:
+ return [ existing_obj ]
return []
def postprocess(self, obj):
diff --git a/xos/tosca/resources/compute.py b/xos/tosca/resources/compute.py
index f01a401..37ba390 100644
--- a/xos/tosca/resources/compute.py
+++ b/xos/tosca/resources/compute.py
@@ -13,7 +13,7 @@
from xosresource import XOSResource
class XOSCompute(XOSResource):
- provides = "tosca.nodes.Compute"
+ provides = ["tosca.nodes.Compute", "tosca.nodes.Compute.Container"]
xos_model = Instance
def select_compute_node(self, user, v, hostname=None):
@@ -60,11 +60,15 @@
colocate_host = colocate_instances[0].node.name
self.info("colocating on %s" % colocate_host)
+ imageName = self.get_requirement("tosca.relationships.UsesImage", throw_exception=False)
+ if imageName:
+ image = self.get_xos_object(Image, name=imageName)
+
capabilities = nodetemplate.get_capabilities()
for (k,v) in capabilities.items():
- if (k=="host"):
+ if (k=="host") and (not host):
(compute_node, flavor) = self.select_compute_node(self.user, v, hostname=colocate_host)
- elif (k=="os"):
+ elif (k=="os") and (not image):
image = self.select_image(self.user, v)
if not compute_node:
@@ -80,6 +84,9 @@
args["node"] = compute_node
args["deployment"] = compute_node.site_deployment.deployment
+ if nodetemplate.type == "tosca.nodes.Compute.Container":
+ args["isolation"] = "container"
+
return args
def create(self, name = None, index = None):
@@ -120,3 +127,4 @@
else:
return super(XOSCompute,self).get_existing_objs()
+
diff --git a/xos/tosca/resources/image.py b/xos/tosca/resources/image.py
index bdc66b6..938c5cd 100644
--- a/xos/tosca/resources/image.py
+++ b/xos/tosca/resources/image.py
@@ -15,7 +15,7 @@
class XOSImage(XOSResource):
provides = "tosca.nodes.Image"
xos_model = Image
- copyin_props = ["disk_format", "container_format", "path"]
+ copyin_props = ["disk_format", "container_format", "path", "kind"]
def get_xos_args(self):
args = super(XOSImage, self).get_xos_args()
diff --git a/xos/tosca/resources/networkparametertype.py b/xos/tosca/resources/networkparametertype.py
new file mode 100644
index 0000000..e0cc93e
--- /dev/null
+++ b/xos/tosca/resources/networkparametertype.py
@@ -0,0 +1,38 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import Slice,User,Network,NetworkParameterType
+
+from xosresource import XOSResource
+
+class XOSNetworkParameterType(XOSResource):
+ provides = "tosca.nodes.NetworkParameterType"
+ xos_model = NetworkParameterType
+ copyin_props = []
+
+ def get_xos_args(self):
+ args = super(XOSNetworkParameterType, self).get_xos_args()
+
+ return args
+
+ def create(self):
+ xos_args = self.get_xos_args()
+
+ networkParameterType = NetworkParameterType(**xos_args)
+ networkParameterType.caller = self.user
+ networkParameterType.save()
+
+ self.info("Created NetworkParameterType '%s' " % (str(networkParameterType), ))
+
+ def delete(self, obj):
+ if obj.networkparameters.exists():
+ return
+
+ super(XOSNetworkParameterType, self).delete(obj)
+
+
+
diff --git a/xos/tosca/resources/service.py b/xos/tosca/resources/service.py
index 884c6db..247be08 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", "icon_url", "kind", "enabled", "published", "public_key", "versionNumber"]
+ copyin_props = ["view_url", "icon_url", "kind", "enabled", "published", "public_key", "private_key_fn", "versionNumber"]
def postprocess(self, obj):
for provider_service_name in self.get_requirements("tosca.relationships.TenantOfService"):
diff --git a/xos/tosca/resources/vcpeservice.py b/xos/tosca/resources/vcpeservice.py
index 6cc7390..8df7231 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", "enabled", "published", "public_key", "versionNumber", "backend_network_label"]
+ copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "private_key_fn", "versionNumber", "backend_network_label"]
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 3553ab1..9c4f479 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -77,9 +77,6 @@
def get_existing_objs(self):
return self.xos_model.objects.filter(**{self.name_field: self.nodetemplate.name})
- def get_xos_args(self):
- return {}
-
def get_model_class_name(self):
return self.xos_model.__name__
diff --git a/xos/tosca/samples/container.yaml b/xos/tosca/samples/container.yaml
new file mode 100644
index 0000000..bd69fbe
--- /dev/null
+++ b/xos/tosca/samples/container.yaml
@@ -0,0 +1,42 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Template for deploying a single server with predefined properties.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ mysite:
+ type: tosca.nodes.Site
+
+ mysite_contest:
+ type: tosca.nodes.Slice
+ requirements:
+ - slice:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ andybavier/docker-vcpe:
+ type: tosca.nodes.Image
+ properties:
+ kind: container
+ container_format: na
+ disk_format: na
+
+ my_container:
+ type: tosca.nodes.Compute.Container
+ capabilities:
+ # Host container properties
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 4 MB
+ requirements:
+ - slice:
+ node: mysite_contest
+ relationship: tosca.relationships.MemberOfSlice
+ - image:
+ node: andybavier/docker-vcpe
+ relationship: tosca.relationships.UsesImage
diff --git a/xos/xos_config b/xos/xos_config
index c47fac6..b86d618 100644
--- a/xos/xos_config
+++ b/xos/xos_config
@@ -37,7 +37,6 @@
images_directory=/opt/xos/images
dependency_graph=/opt/xos/model-deps
logfile=/var/log/xos_backend.log
-applist=helloworld
[gui]
disable_minidashboard=True