Merge branch 'master' of github.com:open-cloud/xos
diff --git a/xos/configurations/cord-pod/Makefile b/xos/configurations/cord-pod/Makefile
new file mode 100644
index 0000000..9930c52
--- /dev/null
+++ b/xos/configurations/cord-pod/Makefile
@@ -0,0 +1,22 @@
+.PHONY: xos
+xos: nodes.yaml images.yaml vtn_network_cfg_json
+ sudo docker-compose up -d
+ ../common/wait_for_xos_port.sh 80
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/setup.yaml
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/nodes.yaml
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/images.yaml
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/vtn-external.yaml
+
+nodes.yaml:
+ export SETUPDIR=.; bash ../common/make-nodes-yaml.sh
+
+images.yaml:
+ export SETUPDIR=.; bash ../common/make-images-yaml.sh
+
+vtn_network_cfg_json:
+ export SETUPDIR=.; bash ./make-vtn-networkconfig-json.sh
+
+.PHONY: local_containers
+local_containers:
+ cd ../../../containers/xos; make devel
+ cd ../../../containers/synchronizer; make
diff --git a/xos/configurations/cord-pod/NOTES.txt b/xos/configurations/cord-pod/NOTES.txt
new file mode 100644
index 0000000..d0ecf36
--- /dev/null
+++ b/xos/configurations/cord-pod/NOTES.txt
@@ -0,0 +1,38 @@
+Notes on setup
+
+Requirements:
+* admin-openrc.sh: Admin credentials for your OpenStack cloud
+* id_rsa[.pub]: Keypair for use by the various services
+* node_key: Private key that allows root login to the compute nodes
+
+Steps for bringing up the POD:
+
+OpenStack
+* Configure management net
+ - mgmtbr on head nodes
+ - dnsmasq on head1 using cord config file
+* Install OpenStack using the openstack-cluster-install repo
+ TO ADD:
+ - Create XOS VM
+ - Install git, make, OpenStack clients
+ - Check out XOS inside VM
+ - Copy admin-openrc.sh and id_rsa* to correct location
+* Add trusty-server-multi-nic image to OpenStack
+
+VTN
+* Install Docker on the head node
+* Edit /etc/default/docker so that it points to mgmtbr
+* Create onos-cord Docker container
+ # docker ...
+
+XOS
+* Create xos Docker containers attached to mgmtbr
+ # docker ...
+* Bring up XOS cord-pod configuration
+
+
+
+
+
+Test VTN
+* Try to create a network: neutron net-create testnet
diff --git a/xos/configurations/cord-pod/README.md b/xos/configurations/cord-pod/README.md
new file mode 100644
index 0000000..0fcdb13
--- /dev/null
+++ b/xos/configurations/cord-pod/README.md
@@ -0,0 +1,77 @@
+# XOS Docker Images
+
+## 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.
+
+#### Database Container
+
+To build the database container:
+
+```
+$ cd postgresql; make build
+```
+
+#### XOS Container
+
+To build the XOS webserver container:
+
+```
+$ cd xos; make build
+```
+
+#### Synchronizer Container
+
+The Synchronizer 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. Assuming you have already built the XOS container,
+executing the following will build the Synchronizer container:
+
+```
+$ cd synchronizer; make build
+```
+
+#### Solution Compose File
+
+[Docker Compose](https://docs.docker.com/compose/) is a tool for defining and
+running multi-container Docker applications. With Compose, you use a Compose
+file to configure your application’s services. Then, using a single command, you
+create, start, scale, and manage all the services from your configuration.
+
+Included is a compose file in *YAML* format with content defined by the [Docker
+Compose Format](https://docs.docker.com/compose/compose-file/). With the compose
+file a complete XOS solution based on Docker containers can be instantiated
+using a single command. To start the instance you can use the command:
+
+```
+$ docker-compose up -d
+```
+
+You should now be able to access the login page by visiting
+`http://localhost:8000` and log in using the default `padmin@vicci.org` account
+with password `letmein`.
+
+#### Configuring XOS for OpenStack
+
+If you have your own OpenStack cluster, and you would like to configure XOS to
+control it, copy the `admin-openrc.sh` credentials file for your cluster to
+this directory. Make sure that OpenStack commands work from the local machine
+using the credentials, e.g., `source ./admin-openrc.sh; nova list`. Then run:
+
+```
+$ make
+```
+
+XOS will be launched (the Makefile will run the `docker-compose up -d` command
+for you) and configured with the nodes and images available in your
+OpenStack cloud. You can then log in to XOS as described above and start creating
+slices and instances.
diff --git a/xos/configurations/cord-pod/admin-openrc.sh b/xos/configurations/cord-pod/admin-openrc.sh
new file mode 100644
index 0000000..f27fdac
--- /dev/null
+++ b/xos/configurations/cord-pod/admin-openrc.sh
@@ -0,0 +1,6 @@
+# Replace with the OpenStack admin credentials for your cluster
+export OS_TENANT_NAME=admin
+export OS_USERNAME=admin
+export OS_PASSWORD=admin
+export OS_AUTH_URL=http://localhost:35357/v2.0
+
diff --git a/xos/configurations/cord-pod/docker-compose.yml b/xos/configurations/cord-pod/docker-compose.yml
new file mode 100644
index 0000000..0116a1b
--- /dev/null
+++ b/xos/configurations/cord-pod/docker-compose.yml
@@ -0,0 +1,49 @@
+xos_db:
+ image: xosproject/xos-postgres
+ expose:
+ - "5432"
+
+xos_synchronizer_openstack:
+ command: bash -c "sleep 120; python /opt/xos/synchronizers/openstack/xos-synchronizer.py"
+ image: xosproject/xos-synchronizer-openstack
+ labels:
+ org.xosproject.kind: synchronizer
+ org.xosproject.target: openstack
+ links:
+ - xos_db
+ volumes:
+ - .:/root/setup:ro
+ - ../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro
+
+xos_synchronizer_onos:
+ image: xosproject/xos-synchronizer-openstack
+ command: bash -c "python /opt/xos/synchronizers/onos/onos-synchronizer.py -C /opt/xos/synchronizers/onos/onos_synchronizer_config"
+ labels:
+ org.xosproject.kind: synchronizer
+ org.xosproject.target: onos
+ links:
+ - xos_db
+ volumes:
+ - .:/root/setup:ro
+ - ./id_rsa:/opt/xos/synchronizers/onos/onos_key:ro # private key
+
+# FUTURE
+#xos_swarm_synchronizer:
+# image: xosproject/xos-swarm-synchronizer
+# labels:
+# org.xosproject.kind: synchronizer
+# org.xosproject.target: swarm
+
+xos:
+ command: python /opt/xos/manage.py runserver 0.0.0.0:80 --insecure --makemigrations
+ image: xosproject/xos
+ links:
+ - xos_db
+ ports:
+ - "80:80"
+ volumes:
+ - .:/root/setup:ro
+ - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+ - ../cord/xos_cord_config:/opt/xos/xos_configuration/xos_cord_config:ro
+ - ../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro
+ - ./id_rsa.pub:/opt/xos/synchronizers/onos/onos_key.pub:ro
diff --git a/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh b/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
new file mode 100755
index 0000000..e7aac2e
--- /dev/null
+++ b/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
@@ -0,0 +1,71 @@
+FN=$SETUPDIR/vtn-network-cfg.json
+
+echo "Writing to $FN"
+
+rm -f $FN
+
+cat >> $FN <<EOF
+{
+ "apps" : {
+ "org.onosproject.cordvtn" : {
+ "cordvtn" : {
+ "gatewayMac" : "00:00:00:00:00:01",
+ "nodes" : [
+EOF
+
+NODES=$( sudo bash -c "source $SETUPDIR/admin-openrc.sh ; nova hypervisor-list" |grep -v ID|grep -v +|awk '{print $4}' )
+
+# also configure ONOS to manage the nm node
+#NM="neutron-gateway"
+#NODES="$NODES $NM"
+
+NODECOUNT=0
+for NODE in $NODES; do
+ ((NODECOUNT++))
+done
+
+I=0
+for NODE in $NODES; do
+ echo $NODE
+ NODEIP=`getent hosts $NODE | awk '{ print $1 }'`
+
+ PHYPORT=mlx0
+ LOCALIP=$NODEIP
+
+ ((I++))
+ cat >> $FN <<EOF
+ {
+ "hostname": "$NODE",
+ "ovsdbIp": "$NODEIP",
+ "ovsdbPort": "6641",
+ "bridgeId": "of:000000000000000$I",
+ "phyPortName": "$PHYPORT",
+ "localIp": "$LOCALIP"
+EOF
+ if [[ "$I" -lt "$NODECOUNT" ]]; then
+ echo " }," >> $FN
+ else
+ echo " }" >> $FN
+ fi
+done
+
+# get the openstack admin password and username
+source $SETUPDIR/admin-openrc.sh
+NEUTRON_URL=`keystone endpoint-get --service network|grep publicURL|awk '{print $4}'`
+
+cat >> $FN <<EOF
+ ]
+ }
+ },
+ "org.onosproject.openstackswitching" : {
+ "openstackswitching" : {
+ "do_not_push_flows" : "true",
+ "neutron_server" : "$NEUTRON_URL/v2.0/",
+ "keystone_server" : "$OS_AUTH_URL",
+ "user_name" : "$OS_USERNAME",
+ "password" : "$OS_PASSWORD"
+ }
+ }
+ }
+}
+EOF
diff --git a/xos/configurations/cord-pod/setup.yaml b/xos/configurations/cord-pod/setup.yaml
new file mode 100644
index 0000000..c13f0eb
--- /dev/null
+++ b/xos/configurations/cord-pod/setup.yaml
@@ -0,0 +1,61 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ * Adds OpenCloud Sites, Deployments, and Controllers.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+
+ MyDeployment:
+ type: tosca.nodes.Deployment
+ properties:
+ flavors: m1.large, m1.medium, m1.small
+
+ MyOpenStack:
+ type: tosca.nodes.Controller
+ requirements:
+ - deployment:
+ node: MyDeployment
+ relationship: tosca.relationships.ControllerDeployment
+ properties:
+ backend_type: OpenStack
+ version: Kilo
+ auth_url: { get_script_env: [ SELF, adminrc, OS_AUTH_URL, LOCAL_FILE] }
+ admin_user: { get_script_env: [ SELF, adminrc, OS_USERNAME, LOCAL_FILE] }
+ admin_password: { get_script_env: [ SELF, adminrc, OS_PASSWORD, LOCAL_FILE] }
+ admin_tenant: { get_script_env: [ SELF, adminrc, OS_TENANT_NAME, LOCAL_FILE] }
+ domain: Default
+ artifacts:
+ adminrc: /root/setup/admin-openrc.sh
+
+ mysite:
+ type: tosca.nodes.Site
+ properties:
+ display_name: MySite
+ site_url: http://xosproject.org/
+ requirements:
+ - deployment:
+ node: MyDeployment
+ relationship: tosca.relationships.SiteDeployment
+ requirements:
+ - controller:
+ node: MyOpenStack
+ relationship: tosca.relationships.UsesController
+
+ # This user already exists in XOS with this password
+ # It's an example of how to create new users
+ padmin@vicci.org:
+ type: tosca.nodes.User
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ properties:
+ is_admin: true
+ is_active: true
+ firstname: XOS
+ lastname: admin
+ password: letmein
diff --git a/xos/configurations/cord-pod/vtn-external.yaml b/xos/configurations/cord-pod/vtn-external.yaml
new file mode 100644
index 0000000..ee41ac8
--- /dev/null
+++ b/xos/configurations/cord-pod/vtn-external.yaml
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ service_ONOS_VTN:
+ type: tosca.nodes.ONOSService
+ requirements:
+ properties:
+ kind: onos
+ view_url: /admin/onos/onosservice/$id$/
+ no_container: true
+ rest_hostname: ctl.smbaker-xos-neu.xos-pg0.clemson.cloudlab.us
+
+ VTN_ONOS_app:
+ type: tosca.nodes.ONOSVTNApp
+ requirements:
+ - onos_tenant:
+ node: service_ONOS_VTN
+ relationship: tosca.relationships.TenantOfService
+ properties:
+ dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.lldpprovider, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp, org.onosproject.openstackswitching, org.onosproject.cordvtn
+ rest_onos/v1/network/configuration/: { get_artifact: [ SELF, vtn_network_cfg_json, LOCAL_FILE ] }
+ artifacts:
+ vtn_network_cfg_json: /root/setup/vtn-network-cfg.json
+
+
diff --git a/xos/configurations/opencloud/cdn-content.yaml b/xos/configurations/opencloud/cdn-content.yaml
index d4d1445..ebf6b82 100644
--- a/xos/configurations/opencloud/cdn-content.yaml
+++ b/xos/configurations/opencloud/cdn-content.yaml
@@ -111,37 +111,113 @@
node: main_service_provider
relationship: tosca.relationships.MemberOfServiceProvider
- downloads.onosproject.org:
- type: tosca.nodes.CDNPrefix
- requirements:
- - content_provider:
- node: on_lab_content
+ # Create CDN prefix onlab.vicci.org
+ onlab.vicci.org:
+ type: tosca.nodes.CDNPrefix
+ requirements:
+ - content_provider:
+ node: on_lab_content
relationship: tosca.relationships.MemberOfContentProvider
- - default_origin_server:
- node: http_downloads.onosproject.org
+
+ http_onos-videos.s3.amazonaws.com:
+ type: tosca.nodes.OriginServer
+ requirements:
+ - content_provider:
+ node: on_lab_content
+ relationship: tosca.relationships.MemberOfContentProvider
+
+ # Create origin server s3-us-west-1.amazonaws.com
+ http_s3-us-west-1.amazonaws.com:
+ type: tosca.nodes.OriginServer
+ requirements:
+ - content_provider:
+ node: on_lab_content
+ relationship: tosca.relationships.MemberOfContentProvider
+
+ # Create origin server s3.amazonaws.com
+ http_s3.amazonaws.com:
+ type: tosca.nodes.OriginServer
+ requirements:
+ - content_provider:
+ node: on_lab_content
+ relationship: tosca.relationships.MemberOfContentProvider
+
+ # Test Content Provider
+
+ testcp2:
+ type: tosca.nodes.ContentProvider
+ requirements:
+ - service_provider:
+ node: main_service_provider
+ relationship: tosca.relationships.MemberOfServiceProvider
+
+ http_www.cs.arizona.edu:
+ type: tosca.nodes.OriginServer
+ requirements:
+ - content_provider:
+ node: testcp2
+ relationship: tosca.relationships.MemberOfContentProvider
+
+ test-cdn.opencloud.us:
+ type: tosca.nodes.CDNPrefix
+ requirements:
+ - content_provider:
+ node: testcp2
+ relationship: tosca.relationships.MemberOfContentProvider
+
+ - default_origin_server:
+ node: http_www.cs.arizona.edu
relationship: tosca.relationships.DefaultOriginServer
- onlab.vicci.org:
- type: tosca.nodes.CDNPrefix
- requirements:
- - content_provider:
- node: on_lab_content
- relationship: tosca.relationships.MemberOfContentProvider
- - default_origin_server:
- node: http_onlab.vicci.org
- relationship: tosca.relationships.DefaultOriginServer
+ # Health Checks
- http_downloads.onosproject.org:
- type: tosca.nodes.OriginServer
+ healthcheck_dns_onlab.vicci.org:
+ type: tosca.nodes.HpcHealthCheck
requirements:
- - content_provider:
- node: on_lab_content
- relationship: tosca.relationships.MemberOfContentProvider
+ - hpc_service:
+ node: HyperCache
+ relationship: tosca.relationships.MemberOfService
+ properties:
+ kind: dns
+ resource_name: onlab.vicci.org
- http_onlab.vicci.org:
- type: tosca.nodes.OriginServer
+ healthcheck_dns_test-cdn.opencloud.us:
+ type: tosca.nodes.HpcHealthCheck
requirements:
- - content_provider:
- node: on_lab_content
- relationship: tosca.relationships.MemberOfContentProvider
+ - hpc_service:
+ node: HyperCache
+ relationship: tosca.relationships.MemberOfService
+ properties:
+ kind: dns
+ resource_name: test-cdn.opencloud.us
+ healthcheck_http_test-cdn-index:
+ type: tosca.nodes.HpcHealthCheck
+ requirements:
+ - hpc_service:
+ node: HyperCache
+ relationship: tosca.relationships.MemberOfService
+ properties:
+ kind: http
+ resource_name: test-cdn.opencloud.us:/
+ result_contains: Lowenthal
+
+ healthcheck_http_onlab_onos_image:
+ type: tosca.nodes.HpcHealthCheck
+ requirements:
+ - hpc_service:
+ node: HyperCache
+ relationship: tosca.relationships.MemberOfService
+ properties:
+ kind: http
+ resource_name: onlab.vicci.org:/onos/vm/onos-tutorial-1.1.0r220-ovf.zip
+
+ healthcheck_http_onlab_mininet_image:
+ type: tosca.nodes.HpcHealthCheck
+ requirements:
+ - hpc_service:
+ node: HyperCache
+ relationship: tosca.relationships.MemberOfService
+ properties:
+ kind: http
+ resource_name: onlab.vicci.org:/mininet-vm/mininet-2.1.0-130919-ubuntu-13.04-server-amd64-ovf.zip
diff --git a/xos/configurations/opencloud/cdn-opencloud.yaml b/xos/configurations/opencloud/cdn-opencloud.yaml
index 4a13233..f66e0f2 100644
--- a/xos/configurations/opencloud/cdn-opencloud.yaml
+++ b/xos/configurations/opencloud/cdn-opencloud.yaml
@@ -40,7 +40,7 @@
description: HyperCache Slice
type: tosca.nodes.Slice
properties:
- exposed_ports: tcp 2120:2128, tcp 3200:3209, tcp 8006, tcp 8009, tcp 8015
+ exposed_ports: tcp 2120:2128, tcp 3200:3209, tcp 8006, tcp 8009, tcp 8015, tcp 80
requirements:
- cdn_service:
node: HyperCache
diff --git a/xos/configurations/opencloud/docker-compose.yml b/xos/configurations/opencloud/docker-compose.yml
index 828175e..3813dee 100644
--- a/xos/configurations/opencloud/docker-compose.yml
+++ b/xos/configurations/opencloud/docker-compose.yml
@@ -5,8 +5,8 @@
xos_synchronizer_openstack:
image: xosproject/xos-synchronizer-openstack
- #command: bash -c "update-ca-certificates; sleep 120; python /opt/xos/synchronizers/openstack/xos-synchronizer.py"
- command: sleep 86400
+ command: bash -c "update-ca-certificates; sleep 120; python /opt/xos/synchronizers/openstack/xos-synchronizer.py"
+ #command: sleep 86400
labels:
org.xosproject.kind: synchronizer
org.xosproject.target: openstack
@@ -15,6 +15,35 @@
volumes:
- ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
- /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro
+ - ./files/xos_opencloud_config:/opt/xos/xos_configuration/xos_opencloud_config:ro
+
+xos_synchronizer_hpc:
+ image: xosproject/xos-synchronizer-openstack
+ command: bash -c "sleep 120; python /opt/xos/synchronizers/hpc/hpc-synchronizer.py -C /opt/xos/synchronizers/hpc/hpc_synchronizer_config"
+ #command: sleep 86400
+ labels:
+ org.xosproject.kind: synchronizer
+ org.xosproject.target: hpc
+ links:
+ - xos_db
+ volumes:
+ - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+ - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro
+ - ./files/xos_opencloud_config:/opt/xos/xos_configuration/xos_opencloud_config:ro
+
+xos_watcher_hpc:
+ image: xosproject/xos-synchronizer-openstack
+ command: bash -c "sleep 120; python /opt/xos/synchronizers/hpc/hpc_watcher.py"
+ #command: sleep 86400
+ labels:
+ org.xosproject.kind: watcher
+ org.xosproject.target: hpc
+ links:
+ - xos_db
+ volumes:
+ - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+ - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro
+ - ./files/xos_opencloud_config:/opt/xos/xos_configuration/xos_opencloud_config:ro
# FUTURE
#xos_swarm_synchronizer:
@@ -32,3 +61,4 @@
- xos_db
volumes:
- ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+ - ./files/xos_opencloud_config:/opt/xos/xos_configuration/xos_opencloud_config:ro
diff --git a/xos/configurations/opencloud/files/xos_opencloud_config b/xos/configurations/opencloud/files/xos_opencloud_config
new file mode 100644
index 0000000..62291b6
--- /dev/null
+++ b/xos/configurations/opencloud/files/xos_opencloud_config
@@ -0,0 +1,3 @@
+[server]
+restapi_hostname=portal.opencloud.us
+restapi_port=80
diff --git a/xos/core/admin.py b/xos/core/admin.py
index f0d402e..904d64e 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1840,7 +1840,7 @@
verbose_name_plural = "Controller Networks"
verbose_name = "Controller Network"
suit_classes = 'suit-tab suit-tab-admin-only'
- fields = ['backend_status_icon', 'controller','net_id','subnet_id']
+ fields = ['backend_status_icon', 'controller','net_id','subnet_id','subnet']
readonly_fields = ('backend_status_icon', )
class NetworkForm(forms.ModelForm):
diff --git a/xos/core/models/instance.py b/xos/core/models/instance.py
index 62a86c4..7f13eb8 100644
--- a/xos/core/models/instance.py
+++ b/xos/core/models/instance.py
@@ -101,6 +101,9 @@
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 get_controller (self):
+ return self.node.site_deployment.controller
+
def __unicode__(self):
if self.name and Slice.objects.filter(id=self.slice_id) and (self.name != self.slice.name):
# NOTE: The weird check on self.slice_id was due to a problem when
@@ -183,6 +186,9 @@
# return an address that the synchronizer can use to SSH to the instance
def get_ssh_ip(self):
+ management=self.get_network_ip("management")
+ if management:
+ return management
return self.get_network_ip("nat")
@staticmethod
diff --git a/xos/core/models/plcorebase.py b/xos/core/models/plcorebase.py
index 0822bf5..99acc15 100644
--- a/xos/core/models/plcorebase.py
+++ b/xos/core/models/plcorebase.py
@@ -224,6 +224,9 @@
self._initial = self._dict # for PlModelMixIn
self.silent = False
+ def get_controller(self):
+ return self.controller
+
def can_update(self, user):
return user.can_update_root()
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index b5ba737..1a1b6ef 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -554,6 +554,22 @@
self.set_attribute("instance_id", value)
@property
+ def external_hostname(self):
+ return self.get_attribute("external_hostname", "")
+
+ @external_hostname.setter
+ def external_hostname(self, value):
+ self.set_attribute("external_hostname", value)
+
+ @property
+ def external_container(self):
+ return self.get_attribute("external_container", "")
+
+ @external_container.setter
+ def external_container(self, value):
+ self.set_attribute("external_container", value)
+
+ @property
def creator(self):
from core.models import User
if getattr(self, "cached_creator", None):
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index 1bdef36..b98c40a 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -310,6 +310,11 @@
site = models.ForeignKey(Site,related_name='controllersite')
controller = models.ForeignKey(Controller, null=True, blank=True, related_name='controllersite')
tenant_id = StrippedCharField(null=True, blank=True, max_length=200, db_index=True, help_text="Keystone tenant id")
+
+ def delete(self, *args, **kwds):
+ pdb.set_trace()
+ super(ControllerSite, self).delete(*args, **kwds)
+
class Meta:
unique_together = ('site', 'controller')
diff --git a/xos/core/views/legacyapi.py b/xos/core/views/legacyapi.py
index 4657116..b5592c0 100644
--- a/xos/core/views/legacyapi.py
+++ b/xos/core/views/legacyapi.py
@@ -121,6 +121,13 @@
for ps_node in ps_site.nodes.all():
node_ids.append(ps_id_to_pl_id(ps_node.id))
+ if ps_site.location:
+ longitude = ps_site.location.longitude
+ latitude = ps_site.location.latitude
+ else:
+ longitude = 0
+ latitude = 0
+
site = {"site_id": ps_id_to_pl_id(ps_site.id),
"node_ids": node_ids,
"pcu_ids": [],
@@ -134,8 +141,8 @@
"url": None,
"site_tag_ids": [],
"enabled": True,
- "longitude": float(ps_site.location.longitude),
- "latitude": float(ps_site.location.latitude),
+ "longitude": float(longitude),
+ "latitude": float(latitude),
"slice_ids": slice_ids,
"login_base": ps_site.login_base,
"peer_id": None}
@@ -285,7 +292,7 @@
'hostipmap':hostipmap,
'hostnatmap':hostnatmap,
'hostprivmap':hostprivmap,
- 'instances': instances,
+ 'slivers': instances,
'interfaces': allinterfaces,
'sites': sites,
'nodes': nodes}
diff --git a/xos/core/xoslib/methods/ceilometerview.py b/xos/core/xoslib/methods/ceilometerview.py
index f26d84a..c2ecb15 100644
--- a/xos/core/xoslib/methods/ceilometerview.py
+++ b/xos/core/xoslib/methods/ceilometerview.py
@@ -3,6 +3,7 @@
import urllib2
import pytz
import datetime
+import time
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
@@ -29,17 +30,23 @@
if not monitoring_channel:
raise XOSMissingField("Monitoring channel is missing for this tenant...Create one and invoke this REST API")
#TODO: Wait until URL is completely UP
+ MAX_ATTEMPTS = 5
+ attempts = 0
while True:
try:
- response = urllib2.urlopen(monitoring_channel.ceilometer_url,timeout=1)
+ response = urllib2.urlopen(monitoring_channel.ceilometer_url)
break
except urllib2.HTTPError, e:
- logger.info('SRIKANTH: HTTP error %(reason)s' % {'reason':e.reason})
+ logger.info('HTTP error %(reason)s' % {'reason':e.reason})
break
except urllib2.URLError, e:
- logger.info('SRIKANTH: URL error %(reason)s' % {'reason':e.reason})
+ attempts += 1
+ if attempts >= MAX_ATTEMPTS:
+ raise XOSServiceUnavailable("Ceilometer channel is not ready yet...Try again later")
+ logger.info('URL error %(reason)s' % {'reason':e.reason})
+ time.sleep(1)
pass
- logger.info("SRIKANTH: Ceilometer proxy URL for user %(user)s is %(url)s" % {'user':user.username,'url':monitoring_channel.ceilometer_url})
+ logger.info("Ceilometer proxy URL for user %(user)s is %(url)s" % {'user':user.username,'url':monitoring_channel.ceilometer_url})
return monitoring_channel.ceilometer_url
def getTenantControllerTenantMap(user, slice=None):
diff --git a/xos/services/onos/admin.py b/xos/services/onos/admin.py
index 3f9f96c..fb0f1d7 100644
--- a/xos/services/onos/admin.py
+++ b/xos/services/onos/admin.py
@@ -19,16 +19,28 @@
from django.contrib.admin.utils import quote
class ONOSServiceForm(forms.ModelForm):
- use_external_host = forms.CharField(required=False)
+ rest_hostname = forms.CharField(required=False)
+ rest_port = forms.CharField(required=False)
+ no_container = forms.BooleanField(required=False)
+# external_hostname = forms.CharField(required=False)
+# external_container = forms.CharField(required=False)
def __init__(self,*args,**kwargs):
super (ONOSServiceForm,self ).__init__(*args,**kwargs)
if self.instance:
# fields for the attributes
- self.fields['use_external_host'].initial = self.instance.use_external_host
+ self.fields['rest_hostname'].initial = self.instance.rest_hostname
+ self.fields['rest_port'].initial = self.instance.rest_port
+ self.fields['no_container'].initial = self.instance.no_container
+# self.fields['external_hostname'].initial = self.instance.external_hostname
+# self.fields['external_container'].initial = self.instance.external_hostname
def save(self, commit=True):
- self.instance.use_external_host = self.cleaned_data.get("use_external_host")
+ self.instance.rest_hostname = self.cleaned_data.get("rest_hostname")
+ self.instance.rest_port = self.cleaned_data.get("rest_port")
+ self.instance.no_container = self.cleaned_data.get("no_container")
+# self.instance.external_hostname = self.cleaned_data.get("external_hostname")
+# self.instance.external_container = self.cleaned_data.get("external_container")
return super(ONOSServiceForm, self).save(commit=commit)
class Meta:
@@ -40,7 +52,7 @@
verbose_name_plural = "ONOS Services"
list_display = ("backend_status_icon", "name", "enabled")
list_display_links = ('backend_status_icon', 'name', )
- fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url", "use_external_host" ], 'classes':['suit-tab suit-tab-general']})]
+ fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url", "rest_hostname", "rest_port", "no_container" ], 'classes':['suit-tab suit-tab-general']})]
readonly_fields = ('backend_status_text', )
inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
form = ONOSServiceForm
diff --git a/xos/services/onos/models.py b/xos/services/onos/models.py
index 1e869d1..4be2c1b 100644
--- a/xos/services/onos/models.py
+++ b/xos/services/onos/models.py
@@ -21,15 +21,34 @@
verbose_name = "ONOS Service"
proxy = True
- default_attributes = {"use_external_host": ""}
+ default_attributes = {"rest_hostname": "",
+ "rest_port": "8181",
+ "no_container": False}
@property
- def use_external_host(self):
- return self.get_attribute("use_external_host", self.default_attributes["use_external_host"])
+ def rest_hostname(self):
+ return self.get_attribute("rest_hostname", self.default_attributes["rest_hostname"])
- @use_external_host.setter
- def use_external_host(self, value):
- self.set_attribute("use_external_host", value)
+ @rest_hostname.setter
+ def rest_hostname(self, value):
+ self.set_attribute("rest_hostname", value)
+
+ @property
+ def rest_port(self):
+ return self.get_attribute("rest_port", self.default_attributes["rest_port"])
+
+ @rest_port.setter
+ def rest_port(self, value):
+ self.set_attribute("rest_port", value)
+
+ @property
+ def no_container(self):
+ return self.get_attribute("no_container", self.default_attributes["no_container"])
+
+ @no_container.setter
+ def no_container(self, value):
+ self.set_attribute("no_container", value)
+
class ONOSApp(Tenant): # aka 'ONOSTenant'
class Meta:
@@ -93,19 +112,6 @@
def install_dependencies(self, value):
self.set_attribute("install_dependencies", value)
- #@property
- #def instance(self):
- # instance_id = self.get_attribute("instance_id", self.default_attributes["instance_id"])
- # if instance_id:
- # instances = Instance.objects.filter(id=instance_id)
- # if instances:
- # return instances[0]
- # return None
-
- #@instance.setter
- #def instance(self, value):
- # self.set_attribute("instance_id", value.id)
-
def save(self, *args, **kwargs):
if not self.creator:
if not getattr(self, "caller", None):
diff --git a/xos/synchronizers/base/SyncInstanceUsingAnsible.py b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
index 335932f..4e5807e 100644
--- a/xos/synchronizers/base/SyncInstanceUsingAnsible.py
+++ b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
@@ -26,6 +26,12 @@
def __init__(self, **args):
SyncStep.__init__(self, **args)
+ def skip_ansible_fields(self, o):
+ # Return True if the instance processing and get_ansible_fields stuff
+ # should be skipped. This hook is primarily for the OnosApp
+ # sync step, so it can do its external REST API sync thing.
+ return False
+
def defer_sync(self, o, reason):
logger.info("defer object %s due to %s" % (str(o), reason))
raise Exception("defer object %s due to %s" % (str(o), reason))
@@ -44,6 +50,14 @@
return o.instance
+ def get_external_sync(self, o):
+ hostname = getattr(o, "external_hostname", None)
+ container = getattr(o, "external_container", None)
+ if hostname and container:
+ return (hostname, container)
+ else:
+ return None
+
def run_playbook(self, o, fields, template_name=None):
if not template_name:
template_name = self.template_name
@@ -80,6 +94,7 @@
"hostname": instance.node.name,
"instance_id": instance.instance_id,
"username": "ubuntu",
+ "ssh_ip": instance.get_ssh_ip(),
}
key_name = self.service_key_name
elif (instance.isolation == "container"):
@@ -91,6 +106,7 @@
"instance_name": "rootcontext",
"username": "root",
"container_name": "%s-%s" % (instance.slice.name, str(instance.id))
+ # ssh_ip is not used for container-on-metal
}
key_name = self.get_node_key(node)
else:
@@ -107,7 +123,7 @@
"instance_name": instance.parent.name,
"instance_id": instance.parent.instance_id,
"username": "ubuntu",
- "nat_ip": instance.parent.get_ssh_ip(),
+ "ssh_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
@@ -142,30 +158,43 @@
self.prepare_record(o)
- instance = self.get_instance(o)
-
- if isinstance(instance, basestring):
- # sync to some external host
-
- # XXX - this probably needs more work...
-
- fields = { "hostname": instance,
- "instance_id": "ubuntu", # this is the username to log into
- "private_key": service.key,
- }
+ if self.skip_ansible_fields(o):
+ fields = {}
else:
- # sync to an XOS instance
- if not instance:
- self.defer_sync(o, "waiting on instance")
- return
+ if self.get_external_sync(o):
+ # sync to some external host
- if not instance.instance_name:
- self.defer_sync(o, "waiting on instance.instance_name")
- return
+ # UNTESTED
- fields = self.get_ansible_fields(instance)
+ (hostname, container_name) = self.get_external_sync(o)
+ fields = { "hostname": hostname,
+ "baremetal_ssh": True,
+ "instance_name": "rootcontext",
+ "username": "root",
+ "container_name": container_name
+ }
+ key_name = self.get_node_key(node)
+ if not os.path.exists(key_name):
+ raise Exception("Node key %s does not exist" % key_name)
- fields["ansible_tag"] = o.__class__.__name__ + "_" + str(o.id)
+ key = file(key_name).read()
+
+ fields["private_key"] = key
+ # TO DO: Ceilometer stuff
+ else:
+ instance = self.get_instance(o)
+ # sync to an XOS instance
+ if not instance:
+ self.defer_sync(o, "waiting on instance")
+ return
+
+ if not instance.instance_name:
+ self.defer_sync(o, "waiting on instance.instance_name")
+ return
+
+ fields = self.get_ansible_fields(instance)
+
+ 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/synchronizers/base/ansible.py b/xos/synchronizers/base/ansible.py
index d2dca3b..d92835a 100644
--- a/xos/synchronizers/base/ansible.py
+++ b/xos/synchronizers/base/ansible.py
@@ -34,11 +34,20 @@
if (l.startswith(magic_str)):
w = len(magic_str)
str = l[w:]
+
+ # handle ok: [127.0.0.1] => (item=org.onosproject.driver) => {...
+ if str.startswith("(") and (" => {" in str):
+ str = str.split("=> ",1)[1]
+
d = json.loads(str)
results.append(d)
elif (l.startswith(magic_str2)):
w = len(magic_str2)
str = l[w:]
+
+ if str.startswith("(") and (" => {" in str):
+ str = str.split("=> ",1)[1]
+
d = json.loads(str)
results.append(d)
@@ -149,12 +158,12 @@
private_key = opts["private_key"]
baremetal_ssh = opts.get("baremetal_ssh",False)
if baremetal_ssh:
- # no instance_id or nat_ip for baremetal
+ # no instance_id or ssh_ip for baremetal
# we never proxy to baremetal
proxy_ssh = False
else:
instance_id = opts["instance_id"]
- nat_ip = opts["nat_ip"]
+ ssh_ip = opts["ssh_ip"]
try:
proxy_ssh = Config().observer_proxy_ssh
except:
@@ -172,7 +181,15 @@
f = open(config_pathname, "w")
f.write("[ssh_connection]\n")
if proxy_ssh:
- proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
+ proxy_ssh_key = getattr(Config(), "observer_proxy_ssh_key", None)
+ proxy_ssh_user = getattr(Config(), "observer_proxy_ssh_user", "root")
+ if proxy_ssh_key:
+ # If proxy_ssh_key is known, then we can proxy into the compute
+ # node without needing to have the OpenCloud sshd machinery in
+ # place.
+ proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s nc %s 22" % (proxy_ssh_key, proxy_ssh_user, hostname, ssh_ip)
+ else:
+ proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
f.write('ssh_args = -o "%s"\n' % proxy_command)
f.write('scp_if_ssh = True\n')
f.write('pipelining = True\n')
@@ -186,7 +203,7 @@
f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
else:
# acb: Login user is hardcoded, this is not great
- f.write("%s ansible_ssh_private_key_file=%s ansible_ssh_user=ubuntu\n" % (nat_ip, private_key_pathname))
+ f.write("%s ansible_ssh_private_key_file=%s ansible_ssh_user=ubuntu\n" % (ssh_ip, private_key_pathname))
f.close()
# SSH will complain if private key is world or group readable
diff --git a/xos/synchronizers/base/syncstep.py b/xos/synchronizers/base/syncstep.py
index bdab8f3..54c4b89 100644
--- a/xos/synchronizers/base/syncstep.py
+++ b/xos/synchronizers/base/syncstep.py
@@ -142,10 +142,10 @@
def sync_record(self, o):
try:
controller = o.get_controller()
- controller_register = json.loads(o.node.site_deployment.controller.backend_register)
+ controller_register = json.loads(controller.backend_register)
if (controller_register.get('disabled',False)):
- raise InnocuousException('Controller %s is disabled'%sliver.node.site_deployment.controller.name)
+ raise InnocuousException('Controller %s is disabled'%controller.name)
except AttributeError:
pass
@@ -249,7 +249,7 @@
except:
error = '%s'%str_e
- if isinstance(e, InnocuousException) and not force_error:
+ if isinstance(e, InnocuousException):
o.backend_status = '1 - %s'%error
else:
o.backend_status = '2 - %s'%error
diff --git a/xos/synchronizers/hpc/run.sh b/xos/synchronizers/hpc/run.sh
index f77d751..9d22047 100755
--- a/xos/synchronizers/hpc/run.sh
+++ b/xos/synchronizers/hpc/run.sh
@@ -1,6 +1,2 @@
-#if [[ ! -e ./hpc-backend.py ]]; then
-# ln -s ../xos-observer.py hpc-backend.py
-#fi
-
export XOS_DIR=/opt/xos
-python hpc-observer.py -C $XOS_DIR/observers/hpc/hpc_observer_config
+python hpc-synchronizer.py -C $XOS_DIR/synchronizers/hpc/hpc_synchronizer_config
diff --git a/xos/synchronizers/hpc/start.sh b/xos/synchronizers/hpc/start.sh
index 305c07f..3153a7d 100755
--- a/xos/synchronizers/hpc/start.sh
+++ b/xos/synchronizers/hpc/start.sh
@@ -1,6 +1,2 @@
-#if [[ ! -e ./hpc-backend.py ]]; then
-# ln -s ../xos-observer.py hpc-backend.py
-#fi
-
export XOS_DIR=/opt/xos
-nohup python hpc-observer.py -C $XOS_DIR/observers/hpc/hpc_observer_config > /dev/null 2>&1 &
+nohup python hpc-synchronizer.py -C $XOS_DIR/synchronizers/hpc/hpc_synchronizer_config > /dev/null 2>&1 &
diff --git a/xos/synchronizers/hpc/stop.sh b/xos/synchronizers/hpc/stop.sh
index a0b4a8e..780e25c 100755
--- a/xos/synchronizers/hpc/stop.sh
+++ b/xos/synchronizers/hpc/stop.sh
@@ -1 +1 @@
-pkill -9 -f hpc-observer.py
+pkill -9 -f hpc-synchronizer.py
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py b/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py
index 5e5cd83..154c5ab 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py
+++ b/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py
@@ -55,7 +55,6 @@
fields["instance_hostname"] = self.get_instance(o).instance_name.replace("_","-")
fields["sflow_port"] = o.sflow_port
fields["sflow_api_port"] = o.sflow_api_port
- fields["nat_ip"] = self.get_instance(o).get_ssh_ip()
fields["sflow_container"] = "sflowpubsub"
return fields
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py b/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py
index 6de0374..a15fa54 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py
+++ b/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py
@@ -64,7 +64,6 @@
instance = self.get_instance(o)
fields={}
- fields["nat_ip"] = instance.get_ssh_ip()
fields["sflow_api_base_url"] = self.get_sflow_service(o).sflow_api_url
fields["sflow_api_port"] = self.get_sflow_service(o).sflow_api_port
fields["listening_endpoint"] = o.listening_endpoint
diff --git a/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar b/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar
index 893c01a..3d44818 100644
--- a/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar
+++ b/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar
Binary files differ
diff --git a/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar b/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
index 7a32268..e9a4593 100644
--- a/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
+++ b/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
Binary files differ
diff --git a/xos/synchronizers/onos/steps/sync_onosapp.py b/xos/synchronizers/onos/steps/sync_onosapp.py
index 8942e59..281df10 100644
--- a/xos/synchronizers/onos/steps/sync_onosapp.py
+++ b/xos/synchronizers/onos/steps/sync_onosapp.py
@@ -9,6 +9,7 @@
import json
from django.db.models import F, Q
from xos.config import Config
+from synchronizers.base.ansible import run_template
from synchronizers.base.syncstep import SyncStep
from synchronizers.base.ansible import run_template_ssh
from synchronizers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
@@ -46,8 +47,8 @@
serv = self.get_onos_service(o)
- if serv.use_external_host:
- return serv.use_external_host
+ if serv.no_container:
+ raise Exception("get_instance() was called on a service that was marked no_container")
if serv.slices.exists():
slice = serv.slices.all()[0]
@@ -66,6 +67,12 @@
return onoses[0]
+ def is_no_container(self, o):
+ return self.get_onos_service(o).no_container
+
+ def skip_ansible_fields(self, o):
+ return self.is_no_container(o)
+
def get_files_dir(self, o):
if not hasattr(Config(), "observer_steps_dir"):
# make steps_dir mandatory; there's no valid reason for it to not
@@ -126,7 +133,7 @@
ordered_attrs = attrs.keys()
o.early_rest_configs=[]
- if ("cordvtn" in o.dependencies):
+ if ("cordvtn" in o.dependencies) and (not self.is_no_container(o)):
# For VTN, since it's running in a docker host container, we need
# to make sure it configures the cluster using the right ip addresses.
# NOTE: rest_onos/v1/cluster/configuration/ will reboot the cluster and
@@ -172,22 +179,36 @@
def prepare_record(self, o):
self.write_configs(o)
- def get_extra_attributes(self, o):
- instance = self.get_instance(o)
+ def get_extra_attributes_common(self, o):
+ fields = {}
- fields={}
+ # These are attributes that are not dependent on Instance. For example,
+ # REST API stuff.
+
+ onos = self.get_onos_service(o)
+
fields["files_dir"] = o.files_dir
fields["appname"] = o.name
- fields["nat_ip"] = instance.get_ssh_ip()
- fields["config_fns"] = o.config_fns
fields["rest_configs"] = o.rest_configs
- fields["early_rest_configs"] = o.early_rest_configs
- fields["component_configs"] = o.component_configs
+ fields["rest_hostname"] = onos.rest_hostname
+ fields["rest_port"] = onos.rest_port
+
if o.dependencies:
fields["dependencies"] = [x.strip() for x in o.dependencies.split(",")]
else:
fields["dependencies"] = []
+ return fields
+
+ def get_extra_attributes_full(self, o):
+ instance = self.get_instance(o)
+
+ fields = self.get_extra_attributes_common(o)
+
+ fields["config_fns"] = o.config_fns
+ fields["early_rest_configs"] = o.early_rest_configs
+ fields["component_configs"] = o.component_configs
+
if o.install_dependencies:
fields["install_dependencies"] = [x.strip() for x in o.install_dependencies.split(",")]
else:
@@ -199,12 +220,23 @@
fields["ONOS_container"] = "ONOS"
return fields
+ def get_extra_attributes(self, o):
+ if self.is_no_container(o):
+ return self.get_extra_attributes_common(o)
+ else:
+ return self.get_extra_attributes_full(o)
+
def sync_fields(self, o, fields):
# the super causes the playbook to be run
super(SyncONOSApp, self).sync_fields(o, fields)
def run_playbook(self, o, fields):
- super(SyncONOSApp, self).run_playbook(o, fields)
+ if self.is_no_container(o):
+ # There is no machine to SSH to, so use the synchronizer's
+ # run_template method directly.
+ run_template("sync_onosapp_nocontainer.yaml", fields)
+ else:
+ super(SyncONOSApp, self).run_playbook(o, fields)
def delete_record(self, m):
pass
diff --git a/xos/synchronizers/onos/steps/sync_onosapp_nocontainer.yaml b/xos/synchronizers/onos/steps/sync_onosapp_nocontainer.yaml
new file mode 100644
index 0000000..5aad569
--- /dev/null
+++ b/xos/synchronizers/onos/steps/sync_onosapp_nocontainer.yaml
@@ -0,0 +1,57 @@
+---
+- hosts: 127.0.0.1
+ connection: local
+ vars:
+ appname: {{ appname }}
+ dependencies: {{ dependencies }}
+{% if component_configs %}
+ component_configs:
+{% for component_config in component_configs %}
+ - component: {{ component_config.component }}
+ config_params: {{ component_config.config_params }}
+{% endfor %}
+{% endif %}
+{% if rest_configs %}
+ rest_configs:
+{% for rest_config in rest_configs %}
+ - endpoint: {{ rest_config.endpoint }}
+ body: "{{ '{{' }} lookup('file', '{{ files_dir }}/{{ rest_config.fn }}') {{ '}}' }}"
+{% endfor %}
+{% endif %}
+{% if early_rest_configs %}
+ early_rest_configs:
+{% for early_rest_config in early_rest_configs %}
+ - endpoint: {{ early_rest_config.endpoint }}
+ body: "{{ '{{' }} lookup('file', '{{ files_dir }}/{{ early_rest_config.fn }}') {{ '}}' }}"
+{% endfor %}
+{% endif %}
+ rest_hostname: {{ rest_hostname }}
+ rest_port: {{ rest_port }}
+
+ tasks:
+{% if dependencies %}
+ - name: Add dependencies to ONOS
+ uri:
+ url: http://{{ '{{' }} rest_hostname {{ '}}' }}:{{ '{{' }} rest_port {{ '}}' }}/onos/v1/applications/{{ '{{' }} item {{ '}}' }}/active
+ method: POST
+ user: karaf
+ password: karaf
+ with_items:
+ {% for dependency in dependencies %}
+ - {{ dependency }}
+ {% endfor %}
+{% endif %}
+
+{% if rest_configs %}
+# Do this after services have been activated, or it will cause an exception.
+# vOLT will re-read its net config; vbng may not.
+ - name: Add ONOS configuration values
+ uri:
+ url: http://{{ '{{' }} rest_hostname {{ '}}' }}:{{ '{{' }} rest_port {{ '}}' }}/{{ '{{' }} item.endpoint {{ '}}' }} #http://localhost:8181/onos/v1/network/configuration/
+ body: "{{ '{{' }} item.body {{ '}}' }}"
+ body_format: raw
+ method: POST
+ user: karaf
+ password: karaf
+ with_items: "rest_configs"
+{% endif %}
diff --git a/xos/synchronizers/onos/steps/sync_onosservice.py b/xos/synchronizers/onos/steps/sync_onosservice.py
index e70be0c..e1f62c7 100644
--- a/xos/synchronizers/onos/steps/sync_onosservice.py
+++ b/xos/synchronizers/onos/steps/sync_onosservice.py
@@ -43,9 +43,6 @@
serv = o
- if serv.use_external_host:
- return serv.use_external_host
-
if serv.slices.exists():
slice = serv.slices.all()[0]
if slice.instances.exists():
@@ -57,10 +54,17 @@
fields={}
fields["instance_hostname"] = self.get_instance(o).instance_name.replace("_","-")
fields["appname"] = o.name
- fields["nat_ip"] = self.get_instance(o).get_ssh_ip()
+ fields["ssh_ip"] = self.get_instance(o).get_ssh_ip()
fields["ONOS_container"] = "ONOS"
return fields
+ def sync_record(self, o):
+ if o.no_container:
+ logger.info("no work to do for onos service, because o.no_container is set")
+ o.save()
+ else:
+ super(SyncONOSService, self).sync_record(o)
+
def sync_fields(self, o, fields):
# the super causes the playbook to be run
super(SyncONOSService, self).sync_fields(o, fields)
diff --git a/xos/synchronizers/openstack/event_loop.py b/xos/synchronizers/openstack/event_loop.py
index 6cfc9f6..3a14511 100644
--- a/xos/synchronizers/openstack/event_loop.py
+++ b/xos/synchronizers/openstack/event_loop.py
@@ -23,8 +23,8 @@
#from timeout import timeout
from xos.config import Config, XOS_DIR
from synchronizers.base.steps import *
-from syncstep import SyncStep
-from toposort import toposort
+from synchronizers.base.syncstep import SyncStep
+from synchronizers.base.toposort import toposort
from synchronizers.base.error_mapper import *
from synchronizers.openstack.openstacksyncstep import OpenStackSyncStep
from synchronizers.base.steps.sync_object import SyncObject
diff --git a/xos/synchronizers/openstack/model_policies/model_policy_Network.py b/xos/synchronizers/openstack/model_policies/model_policy_Network.py
index 38d0377..9f9e5fd 100644
--- a/xos/synchronizers/openstack/model_policies/model_policy_Network.py
+++ b/xos/synchronizers/openstack/model_policies/model_policy_Network.py
@@ -22,4 +22,9 @@
if network not in network_deploy_lookup or \
expected_controller not in network_deploy_lookup[network]:
nd = ControllerNetwork(network=network, controller=expected_controller, lazy_blocked=True)
+ if network.subnet:
+ # XXX: Possibly unpredictable behavior if there is
+ # more than one ControllerNetwork and the subnet
+ # is specified.
+ nd.subnet = network.subnet
nd.save()
diff --git a/xos/synchronizers/openstack/openstacksyncstep.py b/xos/synchronizers/openstack/openstacksyncstep.py
index cc568f8..46056cf 100644
--- a/xos/synchronizers/openstack/openstacksyncstep.py
+++ b/xos/synchronizers/openstack/openstacksyncstep.py
@@ -1,6 +1,6 @@
import os
import base64
-from syncstep import SyncStep
+from synchronizers.base.syncstep import SyncStep
class OpenStackSyncStep(SyncStep):
""" XOS Sync step for copying data to OpenStack
diff --git a/xos/synchronizers/openstack/steps/sync_controller_networks.py b/xos/synchronizers/openstack/steps/sync_controller_networks.py
index 0b876fa..f8b2292 100644
--- a/xos/synchronizers/openstack/steps/sync_controller_networks.py
+++ b/xos/synchronizers/openstack/steps/sync_controller_networks.py
@@ -34,22 +34,21 @@
cidr = '%d.%d.%d.%d/24'%(a,b,c,d)
return cidr
- def alloc_gateway(self, uuid):
- # 16 bits only
- uuid_masked = uuid & 0xffff
- a = 10
- b = uuid_masked >> 8
- c = uuid_masked & 0xff
- d = 1
-
- gateway = '%d.%d.%d.%d'%(a,b,c,d)
- return gateway
-
+ def alloc_gateway(self, subnet):
+ parts = subnet.split(".")
+ if len(parts)!=4:
+ raise Exception("Invalid subnet %s" % subnet)
+ return ".".join(parts[:3]) + ".1"
def save_controller_network(self, controller_network):
network_name = controller_network.network.name
subnet_name = '%s-%d'%(network_name,controller_network.pk)
- cidr = self.alloc_subnet(controller_network.pk)
+ if controller_network.subnet and controller_network.subnet.strip():
+ # If a subnet is already specified (pass in by the creator), then
+ # use that rather than auto-generating one.
+ cidr = controller_network.subnet.strip()
+ else:
+ cidr = self.alloc_subnet(controller_network.pk)
self.cidr=cidr
slice = controller_network.network.owner
@@ -63,9 +62,9 @@
'subnet_name':subnet_name,
'ansible_tag':'%s-%s@%s'%(network_name,slice.slicename,controller_network.controller.name),
'cidr':cidr,
- 'gateway':self.alloc_gateway(controller_network.pk),
+ 'gateway':self.alloc_gateway(cidr),
'use_vtn':getattr(Config(), "networking_use_vtn", False),
- 'delete':False
+ 'delete':False
}
return network_fields
diff --git a/xos/synchronizers/openstack/syncstep.py b/xos/synchronizers/openstack/syncstep.py
index bdab8f3..d1639b4 100644
--- a/xos/synchronizers/openstack/syncstep.py
+++ b/xos/synchronizers/openstack/syncstep.py
@@ -142,10 +142,10 @@
def sync_record(self, o):
try:
controller = o.get_controller()
- controller_register = json.loads(o.node.site_deployment.controller.backend_register)
+ controller_register = json.loads(controller.backend_register)
if (controller_register.get('disabled',False)):
- raise InnocuousException('Controller %s is disabled'%sliver.node.site_deployment.controller.name)
+ raise InnocuousException('Controller %s is disabled'%controller.name)
except AttributeError:
pass
diff --git a/xos/tosca/custom_types/cdn.m4 b/xos/tosca/custom_types/cdn.m4
index 59d4ee6..0d33715 100644
--- a/xos/tosca/custom_types/cdn.m4
+++ b/xos/tosca/custom_types/cdn.m4
@@ -33,6 +33,26 @@
user:
type: tosca.capabilities.xos.CDNPrefix
+ tosca.nodes.HpcHealthCheck:
+ derived_from: tosca.nodes.Root
+
+ properties:
+ kind:
+ type: string
+ required: true
+ description: dns | http | nameserver
+ resource_name:
+ type: string
+ required: true
+ description: name of resource to query
+ result_contains:
+ type: string
+ required: false
+ description: soemthing to look for inside the result
+ capabilities:
+ healthcheck:
+ type: tosca.capabilities.xos.HpcHealthCheck
+
tosca.relationships.MemberOfServiceProvider:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.ServiceProvider ]
@@ -57,4 +77,7 @@
tosca.capabilities.xos.OriginServer:
derived_from: tosca.capabilities.Root
+ tosca.capabilities.xos.HpcHealthCheck:
+ derived_from: tosca.capabilities.Root
+
diff --git a/xos/tosca/custom_types/cdn.yaml b/xos/tosca/custom_types/cdn.yaml
index 59d4ee6..0d33715 100644
--- a/xos/tosca/custom_types/cdn.yaml
+++ b/xos/tosca/custom_types/cdn.yaml
@@ -33,6 +33,26 @@
user:
type: tosca.capabilities.xos.CDNPrefix
+ tosca.nodes.HpcHealthCheck:
+ derived_from: tosca.nodes.Root
+
+ properties:
+ kind:
+ type: string
+ required: true
+ description: dns | http | nameserver
+ resource_name:
+ type: string
+ required: true
+ description: name of resource to query
+ result_contains:
+ type: string
+ required: false
+ description: soemthing to look for inside the result
+ capabilities:
+ healthcheck:
+ type: tosca.capabilities.xos.HpcHealthCheck
+
tosca.relationships.MemberOfServiceProvider:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.ServiceProvider ]
@@ -57,4 +77,7 @@
tosca.capabilities.xos.OriginServer:
derived_from: tosca.capabilities.Root
+ tosca.capabilities.xos.HpcHealthCheck:
+ derived_from: tosca.capabilities.Root
+
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 20a537a..760e3c9 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -121,6 +121,15 @@
rest_onos/v1/network/configuration/:
type: string
required: false
+ rest_hostname:
+ type: string
+ required: false
+ rest_port:
+ type: string
+ required: false
+ no_container:
+ type: boolean
+ default: false
tosca.nodes.ONOSApp:
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 1b5db39..8135b7f 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -151,6 +151,15 @@
rest_onos/v1/network/configuration/:
type: string
required: false
+ rest_hostname:
+ type: string
+ required: false
+ rest_port:
+ type: string
+ required: false
+ no_container:
+ type: boolean
+ default: false
tosca.nodes.ONOSApp:
diff --git a/xos/tosca/resources/hpchealthcheck.py b/xos/tosca/resources/hpchealthcheck.py
new file mode 100644
index 0000000..93a0912
--- /dev/null
+++ b/xos/tosca/resources/hpchealthcheck.py
@@ -0,0 +1,39 @@
+import importlib
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from services.hpc.models import HpcHealthCheck, HpcService
+
+from xosresource import XOSResource
+
+class XOSHpcHealthCheck(XOSResource):
+ provides = "tosca.nodes.HpcHealthCheck"
+ xos_model = HpcHealthCheck
+ name_field = None
+ copyin_props = ("kind", "resource_name", "result_contains")
+
+ def get_xos_args(self, throw_exception=True):
+ args = super(XOSHpcHealthCheck, self).get_xos_args()
+
+ service_name = self.get_requirement("tosca.relationships.MemberOfService", throw_exception=throw_exception)
+ if service_name:
+ args["hpcService"] = self.get_xos_object(HpcService, throw_exception=throw_exception, name=service_name)
+
+ return args
+
+ def get_existing_objs(self):
+ args = self.get_xos_args(throw_exception=True)
+
+ return list( HpcHealthCheck.objects.filter(hpcService=args["hpcService"], kind=args["kind"], resource_name=args["resource_name"]) )
+
+ def postprocess(self, obj):
+ pass
+
+ def can_delete(self, obj):
+ return super(XOSTenant, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/network.py b/xos/tosca/resources/network.py
index f483b6c..7b513c3 100644
--- a/xos/tosca/resources/network.py
+++ b/xos/tosca/resources/network.py
@@ -39,6 +39,10 @@
# we have to manually fill in some defaults.
args["permit_all_slices"] = True
+ cidr = self.get_property_default("cidr", None)
+ if cidr:
+ args["subnet"] = cidr
+
return args
def postprocess(self, obj):
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
index 5947400..321600d 100644
--- a/xos/tosca/resources/onosapp.py
+++ b/xos/tosca/resources/onosapp.py
@@ -57,7 +57,7 @@
v = d.value
if k.startswith("config_"):
self.set_tenant_attr(obj, k, v)
- elif k.startswith("rest_"):
+ elif k.startswith("rest_") and (k!="rest_hostname") and (k!="rest_port"):
self.set_tenant_attr(obj, k, v)
elif k.startswith("component_config"):
self.set_tenant_attr(obj, k, v)
diff --git a/xos/tosca/resources/onosservice.py b/xos/tosca/resources/onosservice.py
index b742ebb..c836a6c 100644
--- a/xos/tosca/resources/onosservice.py
+++ b/xos/tosca/resources/onosservice.py
@@ -13,7 +13,7 @@
class XOSONOSService(XOSService):
provides = "tosca.nodes.ONOSService"
xos_model = ONOSService
- copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber"]
+ copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", "rest_hostname", "rest_port", "no_container"]
def set_service_attr(self, obj, prop_name, value):
value = self.try_intrinsic_function(value)
@@ -36,6 +36,6 @@
v = d.value
if k.startswith("config_"):
self.set_service_attr(obj, k, v)
- elif k.startswith("rest_"):
+ elif k.startswith("rest_") and (k!="rest_hostname") and (k!="rest_port"):
self.set_service_attr(obj, k, v)
diff --git a/xos/tosca/samples/two_slices_two_networks.yaml b/xos/tosca/samples/two_slices_two_networks.yaml
new file mode 100644
index 0000000..080a6f0
--- /dev/null
+++ b/xos/tosca/samples/two_slices_two_networks.yaml
@@ -0,0 +1,69 @@
+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
+
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ # this one lets XOS auto-allocate a subnet
+ producer_private_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - slice:
+ node: mysite_producer
+ relationship: tosca.relationships.MemberOfSlice
+ - slice:
+ node: mysite_producer
+ relationship: tosca.relationships.ConnectsToSlice
+
+ # this one specifies the subnet to use
+ producer_private_network2:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ cidr: 123.123.0.0/16
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - slice:
+ node: mysite_producer
+ relationship: tosca.relationships.MemberOfSlice
+ - slice:
+ node: mysite_producer
+ relationship: tosca.relationships.ConnectsToSlice
+
+ mysite_producer:
+ type: tosca.nodes.Slice
+ requirements:
+ - slice:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ mysite_consumer:
+ type: tosca.nodes.Slice
+ requirements:
+ - slice:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ - network:
+ node: producer_private_network
+ relationship: tosca.relationships.ConnectsToNetwork
+ - network2:
+ node: producer_private_network2
+ relationship: tosca.relationships.ConnectsToNetwork
+
+
diff --git a/xos/tosca/samples/vtn-external.yaml b/xos/tosca/samples/vtn-external.yaml
new file mode 100644
index 0000000..ee41ac8
--- /dev/null
+++ b/xos/tosca/samples/vtn-external.yaml
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ service_ONOS_VTN:
+ type: tosca.nodes.ONOSService
+ requirements:
+ properties:
+ kind: onos
+ view_url: /admin/onos/onosservice/$id$/
+ no_container: true
+ rest_hostname: ctl.smbaker-xos-neu.xos-pg0.clemson.cloudlab.us
+
+ VTN_ONOS_app:
+ type: tosca.nodes.ONOSVTNApp
+ requirements:
+ - onos_tenant:
+ node: service_ONOS_VTN
+ relationship: tosca.relationships.TenantOfService
+ properties:
+ dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.lldpprovider, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp, org.onosproject.openstackswitching, org.onosproject.cordvtn
+ rest_onos/v1/network/configuration/: { get_artifact: [ SELF, vtn_network_cfg_json, LOCAL_FILE ] }
+ artifacts:
+ vtn_network_cfg_json: /root/setup/vtn-network-cfg.json
+
+
diff --git a/xos/xos/exceptions.py b/xos/xos/exceptions.py
index 52badf8..9ab2605 100644
--- a/xos/xos/exceptions.py
+++ b/xos/xos/exceptions.py
@@ -62,3 +62,9 @@
"specific_error": why,
"fields": fields})
+class XOSServiceUnavailable(APIException):
+ status_code=503
+ def __init__(self, why="Service temporarily unavailable, try again later", fields={}):
+ APIException.__init__(self, {"error": "XOSServiceUnavailable",
+ "specific_error": why,
+ "fields": fields})
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index a6313cf..c8b0b07 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -224,8 +224,8 @@
}
}
-RESTAPI_HOSTNAME = getattr(config, "server_restapihostname", getattr(config, "server_hostname", socket.gethostname()))
-RESTAPI_PORT = int(getattr(config, "server_port", "8000"))
+RESTAPI_HOSTNAME = getattr(config, "server_restapi_hostname", getattr(config, "server_hostname", socket.gethostname()))
+RESTAPI_PORT = int(getattr(config, "server_restapi_port", getattr(config, "server_port", "8000")))
BIGQUERY_TABLE = getattr(config, "bigquery_table", "demoevents")