Merge branch 'master' of github.com:open-cloud/xos
diff --git a/xos/configurations/cord-pod/Makefile b/xos/configurations/cord-pod/Makefile
index 49a1c98..9296f10 100644
--- a/xos/configurations/cord-pod/Makefile
+++ b/xos/configurations/cord-pod/Makefile
@@ -6,7 +6,7 @@
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
-vtn: vtn_network_cfg_json
+vtn:
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/vtn-external.yaml
cord: virtualbng_json
@@ -15,7 +15,7 @@
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-vtn-vsg.yaml
exampleservice:
- sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/pod-exampleservice.yaml
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/pod-exampleservice.yaml
cord-ceilometer: ceilometer_custom_images cord
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/ceilometer.yaml
diff --git a/xos/configurations/cord-pod/README.md b/xos/configurations/cord-pod/README.md
index 2c74c15..3d7a60f 100644
--- a/xos/configurations/cord-pod/README.md
+++ b/xos/configurations/cord-pod/README.md
@@ -77,18 +77,33 @@
They will have been put there for you by the cluster installation scripts.
-If your setup uses the CORD fabric, you need to edit `make-vtn-networkconfig-json.sh`
-and `cord-vtn-vsg.yml` as appropriate. Specifically, in
-`make-vtn-networkconfig-json.sh` you need to set these parameters for VTN:
- * gatewayIp
- * gatewayMac
- * PHYPORT
+**If your setup uses the CORD fabric**, you need to modify the autogenerated VTN
+configuration and node tags, and edit `cord-vtn-vsg.yml` as follows.
-And in `cord-vtn-vsg.yml`:
- * public_addresses -> properties -> addresses
- * service_vsg -> properties -> wan_container_gateway_ip
- * service_vsg -> properties -> wan_container_gateway_mac
- * service_vsg -> properties -> wan_container_netbits
+ 1. The VTN app configuration is autogenerated by XOS. For more information
+about the configuration, see [this page on the ONOS Wiki](https://wiki.onosproject.org/display/ONOS/CORD+VTN),
+under the **ONOS Settings** heading. To see the generated
+configuration, go to http://xos/admin/onos/onosapp/, click on
+*VTN_ONOS_app*, then the *Attributes* tab, and look for the
+`rest_onos/v1/network/configuration/` attribute. You can edit this
+configuration after deleting the `autogenerate` attribute (otherwise XOS will
+overwrite your changes), or you can change the other
+attributes and delete `rest_onos/v1/network/configuration/` in order
+to get XOS to regenerate it.
+
+ 2. The process of autoconfiguring VTN also assigns some default values to per-node parameters. Go to
+ http://xos/admin/core/node/, select a node, then select the *Tags* tab. Configure the following:
+ * `bridgeId` (the ID to set on the node's br-int)
+ * `dataPlaneIntf` (the data plane interface for the fabric on the node)
+ * `dataPlaneIp` (the IP address for the node on the fabric)
+
+ 3. Modify `cord-vtn-vsg.yml` and set these parameters to the
+appropriate values for the fabric:
+ * `public_addresses:properties:addresses` (IP address block of fabric)
+ * `service_vsg:properties:wan_container_gateway_ip` (same as `publicGateway:gatewayIp` from VTN configuration)
+ * `service_vsg:properties:wan_container_gateway_mac` (same as `publicGateway:gatewayMac` from VTN configuration)
+ * `service_vsg:properties:wan_container_netbits` (bits in fabric IP address block netmask)
+
If you're not using the fabric then the default values should be OK.
@@ -104,20 +119,20 @@
### Inspecting the vSG
-The above series of `make` commands will spin up a vSG for a sample subscriber. The
-vSG is implemented as a Docker container (using the
-[andybavier/docker-vcpe](https://hub.docker.com/r/andybavier/docker-vcpe/) image
+The above series of `make` commands will spin up a vSG for a sample subscriber. The
+vSG is implemented as a Docker container (using the
+[andybavier/docker-vcpe](https://hub.docker.com/r/andybavier/docker-vcpe/) image
hosted on Docker Hub) running inside an Ubuntu VM. Once the VM is created, you
can login as the `ubuntu` user at the management network IP (172.27.0.x) on the compute node
hosting the VM, using the private key generated on the head node by the install process.
-For example, in the single-node development POD configuration, you can login to the VM
+For example, in the single-node development POD configuration, you can login to the VM
with management IP 172.27.0.2 using a ProxyCommand as follows:
```
ubuntu@pod:~$ ssh -o ProxyCommand="ssh -W %h:%p ubuntu@nova-compute" ubuntu@172.27.0.2
```
-Alternatively, you could copy the generated private key to the compute node
+Alternatively, you could copy the generated private key to the compute node
and login from there:
```
@@ -126,7 +141,7 @@
ubuntu@nova-compute:~$ ssh ubuntu@172.27.0.2
```
-Once logged in to the VM, you can run `sudo docker ps` to see the running
+Once logged in to the VM, you can run `sudo docker ps` to see the running
vSG containers:
```
@@ -134,4 +149,3 @@
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2b0bfb3662c7 andybavier/docker-vcpe "/sbin/my_init" 5 days ago Up 5 days vcpe-222-111
```
-
diff --git a/xos/configurations/cord-pod/cord-vtn-vsg.yaml b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
index f29e257..c8c6ceb 100644
--- a/xos/configurations/cord-pod/cord-vtn-vsg.yaml
+++ b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
@@ -24,16 +24,25 @@
view_url: /admin/cord/voltservice/$id$/
kind: vOLT
- public_addresses:
+ addresses_vsg:
type: tosca.nodes.AddressPool
properties:
addresses: 10.168.0.0/24
+ gateway_ip: 10.168.0.1
+ gateway_mac: 02:42:0a:a8:00:01
+
+ addresses_exampleservice-public:
+ type: tosca.nodes.AddressPool
+ properties:
+ addresses: 10.168.1.0/24
+ gateway_ip: 10.168.1.1
+ gateway_mac: 02:42:0a:a8:00:01
service_vsg:
type: tosca.nodes.VSGService
requirements:
- - vbng_tenant:
- node: service_vbng
+ - vrouter_tenant:
+ node: service_vrouter
relationship: tosca.relationships.TenantOfService
properties:
view_url: /admin/cord/vsgservice/$id$/
@@ -47,13 +56,17 @@
artifacts:
pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
- service_vbng:
- type: tosca.nodes.VBNGService
+ service_vrouter:
+ type: tosca.nodes.VRouterService
properties:
- view_url: /admin/cord/vbngservice/$id$/
-# if unspecified, vbng observer will look for an ONOSApp Tenant and
-# generate a URL from its IP address
-# vbng_url: http://10.11.10.24:8181/onos/virtualbng/
+ view_url: /admin/vrouter/vrouterservice/$id$/
+ requirements:
+ - addresses_vsg:
+ node: addresses_vsg
+ relationship: tosca.relationships.ProvidesAddresses
+ - addresses_service1:
+ node: addresses_exampleservice-public
+ relationship: tosca.relationships.ProvidesAddresses
Private:
type: tosca.nodes.NetworkTemplate
diff --git a/xos/configurations/cord-pod/images/.gitignore b/xos/configurations/cord-pod/images/.gitignore
new file mode 100644
index 0000000..6949d1f
--- /dev/null
+++ b/xos/configurations/cord-pod/images/.gitignore
@@ -0,0 +1,3 @@
+*.img
+*.qcow2
+*.qcow
diff --git a/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh b/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
deleted file mode 100755
index a1e5137..0000000
--- a/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
+++ /dev/null
@@ -1,83 +0,0 @@
-FN=$SETUPDIR/vtn-network-cfg.json
-
-echo "Writing to $FN"
-
-rm -f $FN
-
-cat >> $FN <<EOF
-{
- "apps" : {
- "org.onosproject.cordvtn" : {
- "cordvtn" : {
- "privateGatewayMac" : "00:00:00:00:00:01",
- "localManagementIp": "172.27.0.1/24",
- "ovsdbPort": "6641",
- "sshPort": "22",
- "sshUser": "root",
- "sshKeyFile": "/root/node_key",
- "publicGateways": [
- {
- "gatewayIp": "10.168.0.1",
- "gatewayMac": "02:42:0a:a8:00:01"
- }
- ],
- "nodes" : [
-EOF
-
-NODES=$( sudo bash -c "source $SETUPDIR/admin-openrc.sh ; nova hypervisor-list" |grep -v ID|grep -v +|awk '{print $4}' )
-
-# XXX disabled - we don't need or want the nm node at this time
-# 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=veth1
- # How to set LOCALIP?
- LOCALIPNET="192.168.199"
-
- ((I++))
- cat >> $FN <<EOF
- {
- "hostname": "$NODE",
- "hostManagementIp": "$NODEIP/24",
- "bridgeId": "of:000000000000000$I",
- "dataPlaneIntf": "$PHYPORT",
- "dataPlaneIp": "$LOCALIPNET.$I/24"
-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.openstackinterface" : {
- "openstackinterface" : {
- "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/pod-exampleservice.yaml b/xos/configurations/cord-pod/pod-exampleservice.yaml
index 59a9c8f..4e4835c 100644
--- a/xos/configurations/cord-pod/pod-exampleservice.yaml
+++ b/xos/configurations/cord-pod/pod-exampleservice.yaml
@@ -18,11 +18,17 @@
no-delete: true
no-update: true
+ service_vrouter:
+ type: tosca.nodes.Service
+ properties:
+ no-create: true
+ no-delete: true
+ no-update: true
+
exampleservice-public:
type: tosca.nodes.network.Network
properties:
ip_version: 4
- cidr: 10.168.1.0/24
requirements:
- network_template:
node: Private
@@ -33,6 +39,9 @@
- connection:
node: mysite_exampleservice
relationship: tosca.relationships.ConnectsToSlice
+ - vrouter_tenant:
+ node: service_vrouter
+ relationship: tosca.relationships.TenantOfService
mysite:
type: tosca.nodes.Site
diff --git a/xos/configurations/cord-pod/vtn-external.yaml b/xos/configurations/cord-pod/vtn-external.yaml
index 0aaee67..74e7ef7 100644
--- a/xos/configurations/cord-pod/vtn-external.yaml
+++ b/xos/configurations/cord-pod/vtn-external.yaml
@@ -25,6 +25,4 @@
relationship: tosca.relationships.TenantOfService
properties:
dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp, org.onosproject.cordvtn, org.onosproject.olt, org.onosproject.igmp, org.onosproject.cordmcast
- 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
+ autogenerate: vtn-network-cfg
diff --git a/xos/configurations/cord/README.md b/xos/configurations/cord/README.md
index 606f12a..64075d9 100644
--- a/xos/configurations/cord/README.md
+++ b/xos/configurations/cord/README.md
@@ -7,8 +7,10 @@
* Brings up ONOS apps for controlling the dataplane: virtualbng, olt
* Configures XOS with the CORD services: vCPE, vBNG, vOLT
-**NOTE:** This configuration is under **active development** and is not yet finished! Some features are not
-fully working yet.
+**NOTE: This configuration is stale and likely not working at present. If you are looking to evaluate
+and/or contribute to [CORD](http://opencord.org/),
+you should look instead at the [cord-pod](../cord-pod) configuration. Almost
+all CORD developers have transitioned to [cord-pod](../cord-pod).**
## End-to-end dataplane
diff --git a/xos/configurations/frontend/Makefile b/xos/configurations/frontend/Makefile
index 2b46441..ee2739c 100644
--- a/xos/configurations/frontend/Makefile
+++ b/xos/configurations/frontend/Makefile
@@ -32,3 +32,11 @@
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/frontend/mocks/cord.yaml
sudo docker exec frontend_xos_1 cp /opt/xos/configurations/cord/xos_cord_config /opt/xos/xos_configuration/
sudo docker exec frontend_xos_1 touch /opt/xos/xos/settings.py
+
+mock-cord-pod:
+ echo "make sure to add '../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro' to volumes section of docker-compose.yml"
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/fixtures.yaml
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord-pod/mgmt-net.yaml
+ sudo docker-compose run xos bash -c "echo somekey > /opt/xos/synchronizers/vcpe/vcpe_public_key; python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord-pod/cord-vtn-vsg.yaml"
+ sudo docker exec frontend_xos_1 cp /opt/xos/configurations/cord/xos_cord_config /opt/xos/xos_configuration/
+ sudo docker exec frontend_xos_1 touch /opt/xos/xos/settings.py
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 7a88d0e..ee6c6bf 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -2109,6 +2109,39 @@
return tabs
+class AddressPoolForm(forms.ModelForm):
+ class Meta:
+ model = Program
+ widgets = {
+ 'addresses': UploadTextareaWidget(attrs={'rows': 20, 'cols': 80, 'class': "input-xxlarge"}),
+ }
+
+class AddressPoolAdmin(XOSBaseAdmin):
+ list_display = ("name", "cidr")
+ list_display_links = ('name',)
+
+ form=AddressPoolForm
+
+ fieldsets = [
+ (None, {'fields': ['name', 'cidr', 'gateway_ip', 'gateway_mac', 'addresses', 'inuse', 'service'],
+ 'classes':['suit-tab suit-tab-general']}),
+ ]
+
+ readonly_fields = ("status",)
+
+ @property
+ def suit_form_tabs(self):
+ tabs=[('general','Program Details'),
+ ('contents','Program Source'),
+ ('messages','Messages'),
+ ]
+
+# request=getattr(_thread_locals, "request", None)
+# if request and request.user.is_admin:
+# tabs.append( ('admin-only', 'Admin-Only') )
+
+ return tabs
+
# Now register the new UserAdmin...
admin.site.register(User, UserAdmin)
# ... and, since we're not using Django's builtin permissions,
@@ -2151,5 +2184,5 @@
admin.site.register(TenantRoot, TenantRootAdmin)
admin.site.register(TenantRootRole, TenantRootRoleAdmin)
admin.site.register(TenantAttribute, TenantAttributeAdmin)
-# admin.site.register(Container, ContainerAdmin)
+ admin.site.register(AddressPool, AddressPoolAdmin)
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index 6af72bf..8373814 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -2,7 +2,7 @@
import socket
import sys
from django.db import models, transaction
-from core.models import PlCoreBase, Site, Slice, Instance, Controller
+from core.models import PlCoreBase, Site, Slice, Instance, Controller, Service
from core.models import ControllerLinkManager,ControllerLinkDeletionManager
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
@@ -350,7 +350,11 @@
class AddressPool(PlCoreBase):
name = models.CharField(max_length=32)
addresses = models.TextField(blank=True, null=True)
+ gateway_ip = models.CharField(max_length=32, null=True)
+ gateway_mac = models.CharField(max_length=32, null=True)
+ cidr = models.CharField(max_length=32, null=True)
inuse = models.TextField(blank=True, null=True)
+ service = models.ForeignKey(Service, related_name="addresspools", null=True, blank=True)
def __unicode__(self): return u'%s' % (self.name)
diff --git a/xos/core/models/plcorebase.py b/xos/core/models/plcorebase.py
index 4170697..c8c25a7 100644
--- a/xos/core/models/plcorebase.py
+++ b/xos/core/models/plcorebase.py
@@ -211,7 +211,8 @@
deleted = models.BooleanField(default=False)
write_protect = models.BooleanField(default=False)
lazy_blocked = models.BooleanField(default=False)
- no_sync = models.BooleanField(default=False)
+ no_sync = models.BooleanField(default=False) # prevent object sync
+ no_policy = models.BooleanField(default=False) # prevent model_policy run
class Meta:
# Changing abstract to False would require the managers of subclasses of
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 96bff36..920bc3b 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -368,10 +368,12 @@
# The next four things are the various type of objects that can be subscribers of this Tenancy
# relationship. One and only one can be used at a time.
+ # XXX these should really be changed to GenericForeignKey
subscriber_service = models.ForeignKey(Service, related_name='subscribed_tenants', blank=True, null=True)
subscriber_tenant = models.ForeignKey("Tenant", related_name='subscribed_tenants', blank=True, null=True)
subscriber_user = models.ForeignKey("User", related_name='subscribed_tenants', blank=True, null=True)
subscriber_root = models.ForeignKey("TenantRoot", related_name="subscribed_tenants", blank=True, null=True)
+ subscriber_network = models.ForeignKey("Network", related_name="subscribed_tenants", blank=True, null=True)
# Service_specific_attribute and service_specific_id are opaque to XOS
service_specific_id = StrippedCharField(max_length=30, blank=True, null=True)
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index 0b8e3af..4acad99 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -127,6 +127,9 @@
default="Provisioning in progress")
deleted = models.BooleanField(default=False)
write_protect = models.BooleanField(default=False)
+ lazy_blocked = models.BooleanField(default=False)
+ no_sync = models.BooleanField(default=False) # prevent object sync
+ no_policy = models.BooleanField(default=False) # prevent model_policy run
timezone = TimeZoneField()
diff --git a/xos/services/cord/models.py b/xos/services/cord/models.py
index 07c169d..0da4809 100644
--- a/xos/services/cord/models.py
+++ b/xos/services/cord/models.py
@@ -8,6 +8,7 @@
from operator import itemgetter, attrgetter, methodcaller
from core.models import Tag
from core.models.service import LeastLoadedNodeScheduler
+from services.vrouter.models import VRouterService, VRouterTenant
import traceback
from xos.exceptions import *
from xos.config import Config
@@ -448,6 +449,7 @@
def __init__(self, *args, **kwargs):
super(VSGTenant, self).__init__(*args, **kwargs)
self.cached_vbng=None
+ self.cached_vrouter=None
@property
def vbng(self):
@@ -468,6 +470,24 @@
raise XOSConfigurationError("vCPE.vBNG cannot be set this way -- create a new vBNG object and set it's subscriber_tenant instead")
@property
+ def vrouter(self):
+ vrouter = self.get_newest_subscribed_tenant(VRouterTenant)
+ if not vrouter:
+ return None
+
+ # always return the same object when possible
+ if (self.cached_vrouter) and (self.cached_vrouter.id == vrouter.id):
+ return self.cached_vrouter
+
+ vrouter.caller = self.creator
+ self.cached_vrouter = vrouter
+ return vrouter
+
+ @vrouter.setter
+ def vrouter(self, value):
+ raise XOSConfigurationError("vCPE.vRouter cannot be set this way -- create a new vRuter object and set its subscriber_tenant instead")
+
+ @property
def volt(self):
if not self.subscriber_tenant:
return None
@@ -559,21 +579,20 @@
@property
def wan_container_ip(self):
- if CORD_USE_VTN:
- # When using VTN, wan_container_ip is stored and maintained inside
- # of the vSG object.
- return self.get_attribute("wan_container_ip", self.default_attributes["wan_container_ip"])
+ if self.vrouter:
+ return self.vrouter.public_ip
else:
- # When not using VTN, wan_container_ip is the same as wan_ip.
- # XXX Is this broken for multiple-containers-per-VM?
- return self.wan_ip
+ if (CORD_USE_VTN):
+ # Should this be an error?
+ return None
+ else:
+ # When not using VTN, wan_container_ip is the same as wan_ip.
+ # XXX Is this broken for multiple-containers-per-VM?
+ return self.wan_ip
@wan_container_ip.setter
def wan_container_ip(self, value):
- if CORD_USE_VTN:
- self.set_attribute("wan_container_ip", value)
- else:
- raise Exception("wan_container_ip.setter called on non-VTN CORD")
+ raise Exception("wan_container_ip is not settable")
def ip_to_mac(self, ip):
(a, b, c, d) = ip.split('.')
@@ -582,9 +601,38 @@
# Generate the MAC for the container interface connected to WAN
@property
def wan_container_mac(self):
- if not self.wan_container_ip:
- return None
- return self.ip_to_mac(self.wan_container_ip)
+ if self.vrouter:
+ return self.vrouter.public_mac
+ else:
+ if (CORD_USE_VTN):
+ # Should this be an error?
+ return None
+ else:
+ return self.ip_to_mac(self.wan_container_ip)
+
+ @property
+ def wan_vm_ip(self):
+ tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
+ if tags:
+ tenant = VRouterTenant.objects.get(id=tags[0].value)
+ return tenant.public_ip
+ else:
+ if CORD_USE_VTN:
+ raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
+ else:
+ return ""
+
+ @property
+ def wan_vm_mac(self):
+ tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
+ if tags:
+ tenant = VRouterTenant.objects.get(id=tags[0].value)
+ return tenant.public_mac
+ else:
+ if CORD_USE_VTN:
+ raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
+ else:
+ return ""
@property
def private_ip(self):
@@ -631,6 +679,28 @@
# print "XXX cleanup vnbg", self.vbng
self.vbng.delete()
+ def get_vrouter_service(self):
+ vrouterServices = VRouterService.get_service_objects().all()
+ if not vrouterServices:
+ raise XOSConfigurationError("No VROUTER Services available")
+ return vrouterServices[0]
+
+ def manage_vrouter(self):
+ # Each vCPE object owns exactly one vRouterTenant object
+
+ if self.deleted:
+ return
+
+ if self.vrouter is None:
+ vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_tenant = self)
+ vrouter.caller = self.creator
+ vrouter.save()
+
+ def cleanup_vrouter(self):
+ if self.vrouter:
+ # print "XXX cleanup vrouter", self.vrouter
+ self.vrouter.delete()
+
def cleanup_orphans(self):
# ensure vCPE only has one vBNG
cur_vbng = self.vbng
@@ -639,6 +709,13 @@
# print "XXX clean up orphaned vbng", vbng
vbng.delete()
+ # ensure vCPE only has one vRouter
+ cur_vrouter = self.vrouter
+ for vrouter in list(self.get_subscribed_tenants(VRouterTenant)):
+ if (not cur_vrouter) or (vrouter.id != cur_vrouter.id):
+ # print "XXX clean up orphaned vrouter", vrouter
+ vrouter.delete()
+
if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
instances=Instance.objects.filter(id=self.orig_instance_id)
if instances:
@@ -741,33 +818,6 @@
self.bbs_account = None
super(VSGTenant, self).save()
- def get_wan_address_from_pool(self):
- ap = AddressPool.objects.filter(name="public_addresses")
- if not ap:
- raise Exception("AddressPool 'public_addresses' does not exist. Please configure it.")
- ap = ap[0]
-
- addr = ap.get_address()
- if not addr:
- raise Exception("AddressPool 'public_addresses' has run out of addresses.")
- return addr
-
- def put_wan_address_to_pool(self, addr):
- AddressPool.objects.filter(name="public_addresses")[0].put_address(addr)
-
- def manage_wan_container_ip(self):
- if CORD_USE_VTN:
- if not self.wan_container_ip:
- addr = self.get_wan_address_from_pool()
-
- self.wan_container_ip = addr
- super(TenantWithContainer, self).save()
-
- def cleanup_wan_container_ip(self):
- if CORD_USE_VTN and self.wan_container_ip:
- self.put_wan_address_to_pool(self.wan_container_ip)
- self.wan_container_ip = None
-
def find_or_make_port(self, instance, network, **kwargs):
port = Port.objects.filter(instance=instance, network=network)
if port:
@@ -829,10 +879,12 @@
# VTN-CORD needs a WAN address for the VM, so that the VM can
# be configured.
if CORD_USE_VTN:
- tags = Tag.select_by_content_object(instance).filter(name="vm_wan_addr")
+ tags = Tag.select_by_content_object(instance).filter(name="vm_vrouter_tenant")
if not tags:
- address = self.get_wan_address_from_pool()
- tag = Tag(service=self.provider_service, content_object=instance, name="vm_wan_addr", value="%s,%s,%s" % ("public_addresses", address, self.ip_to_mac(address)))
+ vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_service = self.provider_service)
+ vrouter.set_attribute("tenant_for_instance_id", instance.id)
+ vrouter.save()
+ tag = Tag(service=self.provider_service, content_object=instance, name="vm_vrouter_tenant", value="%d" % vrouter.id)
tag.save()
def save(self, *args, **kwargs):
@@ -849,8 +901,8 @@
def delete(self, *args, **kwargs):
self.cleanup_vbng()
+ self.cleanup_vrouter()
self.cleanup_container()
- self.cleanup_wan_container_ip()
super(VSGTenant, self).delete(*args, **kwargs)
def model_policy_vcpe(pk):
@@ -860,9 +912,9 @@
if not vcpe:
return
vcpe = vcpe[0]
- vcpe.manage_wan_container_ip()
vcpe.manage_container()
- vcpe.manage_vbng()
+ vcpe.manage_vrouter()
+ #vcpe.manage_vbng()
vcpe.manage_bbs_account()
vcpe.cleanup_orphans()
diff --git a/xos/services/vrouter/__init__.py b/xos/services/vrouter/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/services/vrouter/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/services/vrouter/admin.py b/xos/services/vrouter/admin.py
new file mode 100644
index 0000000..318b3dc
--- /dev/null
+++ b/xos/services/vrouter/admin.py
@@ -0,0 +1,113 @@
+from django.contrib import admin
+
+from services.vrouter.models import *
+from django import forms
+from django.utils.safestring import mark_safe
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.signals import user_logged_in
+from django.utils import timezone
+from django.contrib.contenttypes import generic
+from suit.widgets import LinkedSelect
+from core.models import AddressPool
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, ServicePrivilegeInline, TenantRootTenantInline, TenantRootPrivilegeInline
+from core.middleware import get_request
+
+from functools import update_wrapper
+from django.contrib.admin.views.main import ChangeList
+from django.core.urlresolvers import reverse
+from django.contrib.admin.utils import quote
+
+class VRouterServiceForm(forms.ModelForm):
+ def __init__(self,*args,**kwargs):
+ super (VRouterServiceForm,self ).__init__(*args,**kwargs)
+
+ def save(self, commit=True):
+ return super(VRouterServiceForm, self).save(commit=commit)
+
+ class Meta:
+ model = VRouterService
+
+class VRouterServiceAdmin(ReadOnlyAwareAdmin):
+ model = VRouterService
+ verbose_name = "vRouter Service"
+ verbose_name_plural = "vRouter Service"
+ list_display = ("backend_status_icon", "name", "enabled")
+ list_display_links = ('backend_status_icon', 'name', )
+ fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description', "view_url", "icon_url", ],
+ 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', )
+ inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+ form = VRouterServiceForm
+
+ extracontext_registered_admins = True
+
+ user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
+
+ suit_form_tabs =(('general', 'vRouter Service Details'),
+ ('administration', 'Administration'),
+ #('tools', 'Tools'),
+ ('slices','Slices'),
+ ('serviceattrs','Additional Attributes'),
+ ('serviceprivileges','Privileges'),
+ )
+
+ suit_form_includes = (('vrouteradmin.html', 'top', 'administration'),
+ ) #('hpctools.html', 'top', 'tools') )
+
+ def queryset(self, request):
+ return VRouterService.get_service_objects_by_user(request.user)
+
+class VRouterTenantForm(forms.ModelForm):
+ public_ip = forms.CharField(required=True)
+ public_mac = forms.CharField(required=True)
+ gateway_ip = forms.CharField(required=False)
+ gateway_mac = forms.CharField(required=False)
+ cidr = forms.CharField(required=False)
+ address_pool = forms.ModelChoiceField(queryset=AddressPool.objects.all(),required=False)
+
+ def __init__(self,*args,**kwargs):
+ super (VRouterTenantForm,self ).__init__(*args,**kwargs)
+ self.fields['kind'].widget.attrs['readonly'] = True
+ self.fields['provider_service'].queryset = VRouterService.get_service_objects().all()
+ if self.instance:
+ # fields for the attributes
+ self.fields['address_pool'].initial = self.instance.address_pool
+ self.fields['public_ip'].initial = self.instance.public_ip
+ self.fields['public_mac'].initial = self.instance.public_mac
+ self.fields['gateway_ip'].initial = self.instance.gateway_ip
+ self.fields['gateway_mac'].initial = self.instance.gateway_mac
+ self.fields['cidr'].initial = self.instance.cidr
+ if (not self.instance) or (not self.instance.pk):
+ # default fields for an 'add' form
+ self.fields['kind'].initial = VROUTER_KIND
+ if VRouterService.get_service_objects().exists():
+ self.fields["provider_service"].initial = VRouterService.get_service_objects().all()[0]
+
+ def save(self, commit=True):
+ self.instance.public_ip = self.cleaned_data.get("public_ip")
+ self.instance.public_mac = self.cleaned_data.get("public_mac")
+ self.instance.address_pool = self.cleaned_data.get("address_pool")
+ return super(VRouterTenantForm, self).save(commit=commit)
+
+ class Meta:
+ model = VRouterTenant
+
+class VRouterTenantAdmin(ReadOnlyAwareAdmin):
+ list_display = ('backend_status_icon', 'id', 'subscriber_tenant', 'public_ip' )
+ list_display_links = ('backend_status_icon', 'id')
+ fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', 'subscriber_tenant', 'subscriber_service',
+ 'address_pool', 'public_ip', 'public_mac', 'gateway_ip', 'gateway_mac', 'cidr'],
+ 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', 'service_specific_attribute', 'gateway_ip', 'gateway_mac', 'cidr')
+ form = VRouterTenantForm
+
+ suit_form_tabs = (('general','Details'),)
+
+ def queryset(self, request):
+ return VRouterTenant.get_tenant_objects_by_user(request.user)
+
+admin.site.register(VRouterService, VRouterServiceAdmin)
+admin.site.register(VRouterTenant, VRouterTenantAdmin)
+
diff --git a/xos/services/vrouter/models.py b/xos/services/vrouter/models.py
new file mode 100644
index 0000000..5dba838
--- /dev/null
+++ b/xos/services/vrouter/models.py
@@ -0,0 +1,130 @@
+from django.db import models
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port, AddressPool
+from core.models.plcorebase import StrippedCharField
+import os
+from django.db import models, transaction
+from django.forms.models import model_to_dict
+from django.db.models import Q
+from operator import itemgetter, attrgetter, methodcaller
+from core.models import Tag
+from core.models.service import LeastLoadedNodeScheduler
+import traceback
+from xos.exceptions import *
+from xos.config import Config
+
+class ConfigurationError(Exception):
+ pass
+
+VROUTER_KIND = "vROUTER"
+
+CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
+
+class VRouterService(Service):
+ KIND = VROUTER_KIND
+
+ class Meta:
+ app_label = "vrouter"
+ verbose_name = "vRouter Service"
+ proxy = True
+
+ def ip_to_mac(self, ip):
+ (a, b, c, d) = ip.split('.')
+ return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
+
+ def get_gateways(self):
+ gateways=[]
+
+ aps = self.addresspools.all()
+ for ap in aps:
+ gateways.append( {"gateway_ip": ap.gateway_ip, "gateway_mac": ap.gateway_mac} )
+
+ return gateways
+
+ def get_address_pool(self, name):
+ ap = AddressPool.objects.filter(name=name, service=self)
+ if not ap:
+ raise Exception("vRouter unable to find addresspool %s" % name)
+ return ap[0]
+
+ def get_tenant(self, **kwargs):
+ address_pool_name = kwargs.pop("address_pool_name")
+
+ ap = self.get_address_pool(address_pool_name)
+
+ ip = ap.get_address()
+ if not ip:
+ raise Exception("AddressPool '%s' has run out of addresses." % ap.name)
+
+ t = VRouterTenant(provider_service=self, **kwargs)
+ t.public_ip = ip
+ t.public_mac = self.ip_to_mac(ip)
+ t.address_pool_id = ap.id
+ t.save()
+
+ return t
+
+#VRouterService.setup_simple_attributes()
+
+class VRouterTenant(Tenant):
+ class Meta:
+ proxy = True
+
+ KIND = VROUTER_KIND
+
+ simple_attributes = ( ("public_ip", None),
+ ("public_mac", None),
+ ("address_pool_id", None),
+ )
+
+ @property
+ def gateway_ip(self):
+ if not self.address_pool:
+ return None
+ return self.address_pool.gateway_ip
+
+ @property
+ def gateway_mac(self):
+ if not self.address_pool:
+ return None
+ return self.address_pool.gateway_mac
+
+ @property
+ def cidr(self):
+ if not self.address_pool:
+ return None
+ return self.address_pool.cidr
+
+ @property
+ def address_pool(self):
+ if getattr(self, "cached_address_pool", None):
+ return self.cached_address_pool
+ if not self.address_pool_id:
+ return None
+ aps=AddressPool.objects.filter(id=self.address_pool_id)
+ if not aps:
+ return None
+ ap=aps[0]
+ self.cached_address_pool = ap
+ return ap
+
+ @address_pool.setter
+ def address_pool(self, value):
+ if value:
+ value = value.id
+ if (value != self.get_attribute("address_pool_id", None)):
+ self.cached_address_pool=None
+ self.set_attribute("address_pool_id", value)
+
+ def cleanup_addresspool(self):
+ if self.address_pool_id:
+ ap = AddressPool.objects.filter(id=self.address_pool_id)
+ if ap:
+ ap[0].put_address(self.public_ip)
+ self.public_ip = None
+
+ def delete(self, *args, **kwargs):
+ self.cleanup_addresspool()
+ super(VRouterTenant, self).delete(*args, **kwargs)
+
+VRouterTenant.setup_simple_attributes()
+
diff --git a/xos/synchronizers/model_policy.py b/xos/synchronizers/model_policy.py
index e2121ec..8faae81 100644
--- a/xos/synchronizers/model_policy.py
+++ b/xos/synchronizers/model_policy.py
@@ -110,7 +110,7 @@
deleted_objects = []
for m in models:
- res = m.objects.filter(Q(policed__lt=F('updated')) | Q(policed=None))
+ res = m.objects.filter((Q(policed__lt=F('updated')) | Q(policed=None)) & Q(no_policy=False))
objects.extend(res)
res = m.deleted_objects.filter(Q(policed__lt=F('updated')) | Q(policed=None))
deleted_objects.extend(res)
diff --git a/xos/synchronizers/onos/steps/sync_onosapp.py b/xos/synchronizers/onos/steps/sync_onosapp.py
index 2dfdfbd..1fc6579 100644
--- a/xos/synchronizers/onos/steps/sync_onosapp.py
+++ b/xos/synchronizers/onos/steps/sync_onosapp.py
@@ -1,7 +1,6 @@
import hashlib
import os
import socket
-import socket
import sys
import base64
import time
@@ -14,7 +13,7 @@
from synchronizers.base.syncstep import SyncStep
from synchronizers.base.ansible import run_template_ssh
from synchronizers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
-from core.models import Service, Slice, ControllerSlice, ControllerUser
+from core.models import Service, Slice, Controller, ControllerSlice, ControllerUser, Node, TenantAttribute, Tag
from services.onos.models import ONOSService, ONOSApp
from xos.logger import Logger, logging
@@ -117,6 +116,127 @@
raise Exception("Controller user object for %s does not exist" % instance.creator)
return cuser.kuser_id
+
+ def node_tag_default(self, o, node, tagname, default):
+ tags = Tag.select_by_content_object(node).filter(name=tagname)
+ if tags:
+ value = tags[0].value
+ else:
+ value = default
+ logger.info("node %s: saving default value %s for tag %s" % (node.name, value, tagname))
+ service = self.get_onos_service(o)
+ tag = Tag(service=service, content_object=node, name=tagname, value=value)
+ tag.save()
+ return value
+
+ # Scan attrs for attribute name
+ # If it's not present, save it as a TenantAttribute
+ def attribute_default(self, tenant, attrs, name, default):
+ if name in attrs:
+ value = attrs[name]
+ else:
+ value = default
+ logger.info("saving default value %s for attribute %s" % (value, name))
+ ta = TenantAttribute(tenant=tenant, name=name, value=value)
+ ta.save()
+ return value
+
+ # This function currently assumes a single Deployment and Site
+ def get_vtn_config(self, o, attrs):
+
+ # The "attrs" argument contains a list of all service and tenant attributes
+ # If an attribute is present, use it in the configuration
+ # Otherwise save the attriute with a reasonable (for a CORD devel pod) default value
+ # The admin will see all possible configuration values and the assigned defaults
+ privateGatewayMac = self.attribute_default(o, attrs, "privateGatewayMac", "00:00:00:00:00:01")
+ localManagementIp = self.attribute_default(o, attrs, "localManagementIp", "172.27.0.1/24")
+ ovsdbPort = self.attribute_default(o, attrs, "ovsdbPort", "6641")
+ sshPort = self.attribute_default(o, attrs, "sshPort", "22")
+ sshUser = self.attribute_default(o, attrs, "sshUser", "root")
+ sshKeyFile = self.attribute_default(o, attrs, "sshKeyFile", "/root/node_key")
+
+ # OpenStack endpoints and credentials
+ keystone_server = "http://keystone:5000/v2.0/"
+ user_name = "admin"
+ password = "ADMIN_PASS"
+ controllers = Controller.objects.all()
+ if controllers:
+ controller = controllers[0]
+ keystone_server = controller.auth_url
+ user_name = controller.admin_user
+ password = controller.admin_password
+
+ # Put this in the controller object? Or fetch it from Keystone?
+ # Seems like VTN should be pulling it from Keystone
+ # For now let it be specified by a service / tenant attribute
+ neutron_server = self.attribute_default(o, attrs, "neutron_server", "http://neutron-api:9696/v2.0/")
+
+ data = {
+ "apps" : {
+ "org.onosproject.cordvtn" : {
+ "cordvtn" : {
+ "privateGatewayMac" : privateGatewayMac,
+ "localManagementIp": localManagementIp,
+ "ovsdbPort": ovsdbPort,
+ "sshPort": sshPort,
+ "sshUser": sshUser,
+ "sshKeyFile": sshKeyFile,
+ "publicGateways": [],
+ "nodes" : []
+ }
+ },
+ "org.onosproject.openstackinterface" : {
+ "openstackinterface" : {
+ "do_not_push_flows" : "true",
+ "neutron_server" : neutron_server,
+ "keystone_server" : keystone_server,
+ "user_name" : user_name,
+ "password" : password
+ }
+ }
+ }
+ }
+
+ # Generate apps->org.onosproject.cordvtn->cordvtn->nodes
+
+ # We need to generate a CIDR address for the physical node's
+ # address on the management network
+ mgmtSubnetBits = self.attribute_default(o, attrs, "mgmtSubnetBits", "24")
+
+ nodes = Node.objects.all()
+ for node in nodes:
+ nodeip = socket.gethostbyname(node.name)
+
+ try:
+ bridgeId = self.node_tag_default(o, node, "bridgeId", "of:0000000000000001")
+ dataPlaneIntf = self.node_tag_default(o, node, "dataPlaneIntf", "veth1")
+ # This should be generated from the AddressPool if not present
+ dataPlaneIp = self.node_tag_default(o, node, "dataPlaneIp", "192.168.199.1/24")
+ except:
+ logger.error("not adding node %s to the VTN configuration" % node.name)
+ continue
+
+ node_dict = {
+ "hostname": node.name,
+ "hostManagementIp": "%s/%s" % (nodeip, mgmtSubnetBits),
+ "bridgeId": bridgeId,
+ "dataPlaneIntf": dataPlaneIntf,
+ "dataPlaneIp": dataPlaneIp
+ }
+ data["apps"]["org.onosproject.cordvtn"]["cordvtn"]["nodes"].append(node_dict)
+
+ # Generate apps->org.onosproject.cordvtn->cordvtn->publicGateways
+ # This should come from the vRouter service, but stick it in an attribute for now
+ gatewayIp = self.attribute_default(o, attrs, "gatewayIp", "10.168.0.1")
+ gatewayMac = self.attribute_default(o, attrs, "gatewayMac", "02:42:0a:a8:00:01")
+ gateway_dict = {
+ "gatewayIp": gatewayIp,
+ "gatewayMac": gatewayMac
+ }
+ data["apps"]["org.onosproject.cordvtn"]["cordvtn"]["publicGateways"].append(gateway_dict)
+
+ return json.dumps(data, indent=4, sort_keys=True)
+
def write_configs(self, o):
o.config_fns = []
o.rest_configs = []
@@ -153,6 +273,33 @@
file(os.path.join(o.files_dir, fn),"w").write(" " +value)
o.early_rest_configs.append( {"endpoint": endpoint, "fn": fn} )
+ # Generate config files and save them to the appropriate tenant attributes
+ autogen = []
+ for key, value in attrs.iteritems():
+ if key == "autogenerate" and value:
+ autogen.append(value)
+ for label in autogen:
+ config = None
+ value = None
+ if label == "vtn-network-cfg":
+ # Generate the VTN config file... where should this live?
+ config = "rest_onos/v1/network/configuration/"
+ value = self.get_vtn_config(o, attrs)
+ if config:
+ tas = TenantAttribute.objects.filter(tenant=o, name=config)
+ if tas:
+ ta = tas[0]
+ if ta.value != value:
+ logger.info("updating %s with autogenerated config" % config)
+ ta.value = value
+ ta.save()
+ attrs[config] = value
+ else:
+ logger.info("saving autogenerated config %s" % config)
+ ta = TenantAttribute(tenant=o, name=config, value=value)
+ ta.save()
+ attrs[config] = value
+
for name in attrs.keys():
value = attrs[name]
if name.startswith("config_"):
diff --git a/xos/synchronizers/openstack/steps/sync_images.py b/xos/synchronizers/openstack/steps/sync_images.py
index 8049ac1..1638fd0 100644
--- a/xos/synchronizers/openstack/steps/sync_images.py
+++ b/xos/synchronizers/openstack/steps/sync_images.py
@@ -27,7 +27,7 @@
if os.path.exists(images_path):
for f in os.listdir(images_path):
filename = os.path.join(images_path, f)
- if os.path.isfile(filename):
+ if os.path.isfile(filename) and filename.endswith(".img"):
available_images[f] = filename
logger.info("SyncImages: available_images = %s" % str(available_images))
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant.py b/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
index d52f075..c28b3c1 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
@@ -153,18 +153,8 @@
if mac:
safe_macs.append(mac)
- wan_vm_ip=""
- wan_vm_mac=""
- tags = Tag.select_by_content_object(o.instance).filter(name="vm_wan_addr")
- if tags:
- parts=tags[0].value.split(",")
- if len(parts)!=3:
- raise Exception("vm_wan_addr tag is malformed: %s" % value)
- wan_vm_ip = parts[1]
- wan_vm_mac = parts[2]
- else:
- if CORD_USE_VTN:
- raise Exception("no vm_wan_addr tag for instance %s" % o.instance)
+ wan_vm_ip=o.wan_vm_ip
+ wan_vm_mac=o.wan_vm_mac
fields = {"vlan_ids": vlan_ids, # XXX remove this
"s_tags": s_tags,
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
index 04521dc..618e9de 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
@@ -294,7 +294,7 @@
service: name={{ container_name }} state=started
- name: reload ufw
- shell: docker exec {{ container_name }} bash -c "/sbin/iptables -t nat -F PREROUTING; /usr/sbin/ufw reload"
+ shell: docker exec {{ container_name }} bash -c "/sbin/iptables -t nat -F PREROUTING; /sbin/iptables -t nat -F POSTROUTING; /usr/sbin/ufw reload"
- name: rerun /etc/rc.local
shell: docker exec {{ container_name }} bash -c "/etc/rc.local"
diff --git a/xos/synchronizers/vtn/steps/sync_port_addresses.py b/xos/synchronizers/vtn/steps/sync_port_addresses.py
index 6b48911..bbc6c99 100644
--- a/xos/synchronizers/vtn/steps/sync_port_addresses.py
+++ b/xos/synchronizers/vtn/steps/sync_port_addresses.py
@@ -70,14 +70,11 @@
# now do the VM_WAN_IP from the instance
if vsg.instance:
- tags=Tag.select_by_content_object(vsg.instance).filter(name="vm_wan_addr")
- if tags:
- parts=tags[0].value.split(",")
- if len(parts)!=3:
- raise Exception("vm_wan_addr tag is malformed: %s" % value)
- entry = {"mac_address": parts[2], "ip_address": parts[1]}
- if not entry in addr_pairs:
- addr_pairs.append(entry)
+ wan_vm_ip = vsg.wan_vm_ip
+ wan_vm_mac = vsg.wan_vm_mac
+ entry = {"mac_address": wan_vm_mac, "ip_address": wan_vm_ip}
+ if not entry in addr_pairs:
+ addr_pairs.append(entry)
# Get all ports in all controllers
ports_by_id = {}
diff --git a/xos/tools/xos-manage b/xos/tools/xos-manage
index 5410f37..23cda72 100755
--- a/xos/tools/xos-manage
+++ b/xos/tools/xos-manage
@@ -147,6 +147,7 @@
python ./manage.py makemigrations helloworldservice_complete
python ./manage.py makemigrations onos
python ./manage.py makemigrations vtr
+ python ./manage.py makemigrations vrouter
#python ./manage.py makemigrations servcomp
}
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index d903190..fd4f8ec 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -204,6 +204,9 @@
rest_onos/v1/network/configuration/:
type: string
required: false
+ autogenerate:
+ type: string
+ required: false
tosca.nodes.VSGService:
description: >
@@ -248,6 +251,16 @@
required: false
description: URL of REST API endpoint for vBNG Service.
+ tosca.nodes.VRouterService:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: The vRouter Service.
+ capabilities:
+ xos_base_service_caps
+ properties:
+ xos_base_props
+ xos_base_service_props
+
tosca.nodes.CDNService:
derived_from: tosca.nodes.Root
description: >
@@ -553,12 +566,23 @@
derived_from: tosca.nodes.Root
description: >
A pool of addresses
+ capabilities:
+ addresspool:
+ type: tosca.capabilities.xos.AddressPool
properties:
xos_base_props
addresses:
type: string
required: false
description: space-separated list of addresses
+ gateway_ip:
+ type: string
+ required: false
+ description: gateway ip address
+ gateway_mac:
+ type: string
+ required: false
+ description: gateway mac address
tosca.nodes.Image:
derived_from: tosca.nodes.Root
@@ -877,6 +901,10 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.NodeLabel ]
+ tosca.relationships.ProvidesAddresses:
+ derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.AddressPool ]
+
tosca.relationships.DependsOn:
derived_from: tosca.relationships.Root
@@ -939,3 +967,7 @@
tosca.capabilities.xos.NetworkParameterType:
derived_from: tosca.capabilities.Root
description: An XOS NetworkParameterType
+
+ tosca.capabilities.xos.AddressPool:
+ derived_from: tosca.capabilities.Root
+ description: An XOS AddressPool
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index adc1bf1..9bdcfc3 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -262,6 +262,9 @@
rest_onos/v1/network/configuration/:
type: string
required: false
+ autogenerate:
+ type: string
+ required: false
tosca.nodes.VSGService:
description: >
@@ -394,6 +397,60 @@
required: false
description: URL of REST API endpoint for vBNG Service.
+ tosca.nodes.VRouterService:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: The vRouter Service.
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ service:
+ type: tosca.capabilities.xos.Service
+ properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
+ kind:
+ type: string
+ default: generic
+ description: Type of service.
+ view_url:
+ type: string
+ required: false
+ description: URL to follow when icon is clicked in the Service Directory.
+ icon_url:
+ type: string
+ required: false
+ description: ICON to display in the Service Directory.
+ enabled:
+ type: boolean
+ default: true
+ published:
+ type: boolean
+ default: true
+ description: If True then display this Service in the Service Directory.
+ public_key:
+ 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
+ description: Version number of Service.
+
tosca.nodes.CDNService:
derived_from: tosca.nodes.Root
description: >
@@ -832,6 +889,9 @@
derived_from: tosca.nodes.Root
description: >
A pool of addresses
+ capabilities:
+ addresspool:
+ type: tosca.capabilities.xos.AddressPool
properties:
no-delete:
type: boolean
@@ -849,6 +909,14 @@
type: string
required: false
description: space-separated list of addresses
+ gateway_ip:
+ type: string
+ required: false
+ description: gateway ip address
+ gateway_mac:
+ type: string
+ required: false
+ description: gateway mac address
tosca.nodes.Image:
derived_from: tosca.nodes.Root
@@ -1233,6 +1301,10 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.NodeLabel ]
+ tosca.relationships.ProvidesAddresses:
+ derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.AddressPool ]
+
tosca.relationships.DependsOn:
derived_from: tosca.relationships.Root
@@ -1295,3 +1367,7 @@
tosca.capabilities.xos.NetworkParameterType:
derived_from: tosca.capabilities.Root
description: An XOS NetworkParameterType
+
+ tosca.capabilities.xos.AddressPool:
+ derived_from: tosca.capabilities.Root
+ description: An XOS AddressPool
diff --git a/xos/tosca/resources/addresspool.py b/xos/tosca/resources/addresspool.py
index e8577a2..8cd3e83 100644
--- a/xos/tosca/resources/addresspool.py
+++ b/xos/tosca/resources/addresspool.py
@@ -14,7 +14,7 @@
class XOSAddressPool(XOSResource):
provides = "tosca.nodes.AddressPool"
xos_model = AddressPool
- copyin_props = ["addresses"]
+ copyin_props = ["addresses", "gateway_ip", "gateway_mac"]
def expand_cidr(self, cidr):
(network, bits) = cidr.split("/")
@@ -31,20 +31,31 @@
ip = ip & netmask | i
dest.append( socket.inet_ntoa(struct.pack("!L", ip)) )
- return dest
+ return (dest, bits)
def get_xos_args(self):
args = super(XOSAddressPool, self).get_xos_args()
if "addresses" in args:
- dest = []
- for addr in args["addresses"].split():
- addr=addr.strip()
- if "/" in addr:
- dest.extend(self.expand_cidr(addr))
- else:
- dest.append(addr)
- args["addresses"] = " ".join(dest)
+ addr = args["addresses"]
+ if "," in addr:
+ raise Exception("Only one cidr per AddressPool")
+ if not "/" in addr:
+ raise Exception("AddressPool addresses must be a cidr")
+ (cidr_addrs, cidr_netbits) = self.expand_cidr(addr)
+ args["addresses"] = " ".join(cidr_addrs)
+ args["cidr"] = addr
+
+# if "addresses" in args:
+# dest = []
+# for addr in args["addresses"].split():
+# addr=addr.strip()
+# if "/" in addr:
+# (cidr_addrs, cidr_netbits) = self.expand_cidr(addr)
+# dest.extend(cidr_addrs)
+# else:
+# dest.append(addr)
+# args["addresses"] = " ".join(dest)
return args
diff --git a/xos/tosca/resources/network.py b/xos/tosca/resources/network.py
index 7b513c3..32e3fc1 100644
--- a/xos/tosca/resources/network.py
+++ b/xos/tosca/resources/network.py
@@ -6,7 +6,7 @@
from translator.toscalib.tosca_template import ToscaTemplate
import pdb
-from core.models import Slice,User,Network,NetworkTemplate,NetworkSlice
+from core.models import Slice,User,Network,NetworkTemplate,NetworkSlice,Service,Tenant
from xosresource import XOSResource
@@ -35,7 +35,7 @@
if v:
args[prop] = v
else:
- # tosca.nodes.network.Netwrok is not as rich as an XOS network. So
+ # tosca.nodes.network.Network is not as rich as an XOS network. So
# we have to manually fill in some defaults.
args["permit_all_slices"] = True
@@ -54,6 +54,24 @@
ns = NetworkSlice(network = obj, slice=slice)
ns.save()
+ # this is really for vRouter
+ for provider_service_name in self.get_requirements("tosca.relationships.TenantOfService"):
+ provider_service = self.get_xos_object(Service, name=provider_service_name)
+
+ existing_tenancy = Tenant.objects.filter(provider_service = provider_service, subscriber_network = obj)
+ if existing_tenancy:
+ self.info("Tenancy relationship from %s to %s already exists" % (str(obj), str(provider_service)))
+ else:
+ from services.vrouter.models import VROUTER_KIND, VRouterService
+ if provider_service.kind == VROUTER_KIND:
+ tenancy = VRouterService.objects.get(id=provider_service.id).get_tenant(address_pool_name="addresses_"+obj.name, subscriber_network=obj)
+ tenancy.save()
+ obj.subnet = tenancy.cidr
+ else:
+ raise Exception("The only network tenancy relationships that are allowed are to vRouter services")
+
+ self.info("Created Tenancy relationship from %s to %s" % (str(obj), str(provider_service)))
+
# v = self.get_property("permitted_slices")
# if v:
# for slicename in v.split(","):
@@ -72,12 +90,21 @@
if not xos_args.get("template", None):
raise Exception("Must specify network template when creating network")
+ # XXX TODO: investigate using transaction.atomic instead of setting
+ # no_sync and no_policy
+
network = Network(**xos_args)
network.caller = self.user
+ network.no_sync = True # postprocess might set the cidr
+ network.no_policy = True
network.save()
self.postprocess(network)
+ network.no_sync = False
+ network.no_policy = False
+ network.save()
+
self.info("Created Network '%s' owned by Slice '%s'" % (str(network), str(network.owner)))
def delete(self, obj):
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
index 321600d..72511b3 100644
--- a/xos/tosca/resources/onosapp.py
+++ b/xos/tosca/resources/onosapp.py
@@ -61,7 +61,8 @@
self.set_tenant_attr(obj, k, v)
elif k.startswith("component_config"):
self.set_tenant_attr(obj, k, v)
+ elif k == "autogenerate":
+ self.set_tenant_attr(obj, k, v)
def can_delete(self, obj):
return super(XOSONOSApp, self).can_delete(obj)
-
diff --git a/xos/tosca/resources/service.py b/xos/tosca/resources/service.py
index 247be08..5a57418 100644
--- a/xos/tosca/resources/service.py
+++ b/xos/tosca/resources/service.py
@@ -6,7 +6,7 @@
from translator.toscalib.tosca_template import ToscaTemplate
import pdb
-from core.models import Service,User,CoarseTenant
+from core.models import Service,User,CoarseTenant,AddressPool
from xosresource import XOSResource
@@ -29,6 +29,11 @@
self.info("Created Tenancy relationship from %s to %s" % (str(obj), str(provider_service)))
+ for ap_name in self.get_requirements("tosca.relationships.ProvidesAddresses"):
+ ap = self.get_xos_object(AddressPool, name=ap_name)
+ ap.service = obj
+ ap.save()
+
def can_delete(self, obj):
if obj.slices.exists():
self.info("Service %s has active slices; skipping delete" % obj.name)
diff --git a/xos/tosca/resources/vrouterservice.py b/xos/tosca/resources/vrouterservice.py
new file mode 100644
index 0000000..14dabcc
--- /dev/null
+++ b/xos/tosca/resources/vrouterservice.py
@@ -0,0 +1,16 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from services.vrouter.models import VRouterService
+
+from service import XOSService
+
+class XOSVRouterService(XOSService):
+ provides = "tosca.nodes.VRouterService"
+ xos_model = VRouterService
+ copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber"]
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index aa50e7d..f457476 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -181,6 +181,7 @@
'services.requestrouter',
'services.syndicate_storage',
'services.vtr',
+ 'services.vrouter',
'geoposition',
'rest_framework_swagger',
)