Merge branch 'master' into feature/ceilometerDashboard
diff --git a/xos/configurations/bash/Makefile b/xos/configurations/bash/Makefile
index f33ea56..a668a55 100644
--- a/xos/configurations/bash/Makefile
+++ b/xos/configurations/bash/Makefile
@@ -1,6 +1,8 @@
 MYIP:=$(shell hostname -i)
 
 test: common_cloudlab
+	rm ../../xos_configuration/*
+	cp ../common/xos_common_config ../../xos_configuration/
 	cat ../common/Dockerfile.common Dockerfile.bash > Dockerfile
 	cd ../../..; sudo docker build -t xos -f xos/configurations/bash/Dockerfile .
 	sudo docker run -i --tty --add-host="ctl:$(MYIP)" -p 9999:8000 xos
diff --git a/xos/configurations/common/xos_common_config b/xos/configurations/common/xos_common_config
new file mode 100644
index 0000000..df30b54
--- /dev/null
+++ b/xos/configurations/common/xos_common_config
@@ -0,0 +1,44 @@
+[plc]
+name=plc
+deployment=plc
+
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+host=localhost
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+logfile=/var/log/xos.log
+
+[nova]
+admin_user=admin@domain.com
+admin_password=admin
+admin_tenant=admin
+url=http://localhost:5000/v2.0/
+default_image=None
+default_flavor=m1.small
+default_security_group=default
+ca_ssl_cert=/etc/ssl/certs/ca-certificates.crt
+
+[observer]
+pretend=False
+backoff_disabled=False
+images_directory=/opt/xos/images
+dependency_graph=/opt/xos/model-deps
+logfile=/var/log/xos_backend.log
+
+[gui]
+disable_minidashboard=True
+branding_name=Open Cloud
+branding_icon=/static/logo.png
diff --git a/xos/configurations/cord/Makefile b/xos/configurations/cord/Makefile
index 701e92f..ef823cd 100644
--- a/xos/configurations/cord/Makefile
+++ b/xos/configurations/cord/Makefile
@@ -3,8 +3,11 @@
 LAST_CONTAINER=$(shell sudo docker ps -l -q)
 
 cord: common_cloudlab ceilometer_dashboard virtualbng_json
+	rm ../../xos_configuration/*
 	echo "# Autogenerated -- do not edit" > Dockerfile
 	cat ../common/Dockerfile.common Dockerfile.cord >> Dockerfile
+	cp ../common/xos_common_config ../../xos_configuration/
+	cp ./xos_cord_config ../../xos_configuration/
 	cd ../../..; sudo docker build -t xos -f xos/configurations/cord/Dockerfile .
 	sudo docker run -d --add-host="ctl:$(MYIP)" -p 9999:8000 xos
 	bash ../common/wait_for_xos.sh
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index a35454b..2a61cf9 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -203,6 +203,16 @@
               node: mysite_vbng
               relationship: tosca.relationships.ConnectsToSlice
 
+    Private-Direct:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          access: direct
+
+    Private-Indirect:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          access: indirect
+
     subscriber_network:
       type: tosca.nodes.network.Network
       properties:
diff --git a/xos/configurations/cord/xos_cord_config b/xos/configurations/cord/xos_cord_config
new file mode 100644
index 0000000..8dd0ae5
--- /dev/null
+++ b/xos/configurations/cord/xos_cord_config
@@ -0,0 +1,4 @@
+[gui]
+branding_name=CORD
+#branding_css=/static/cord.css
+branding_icon=/static/cord_logo_3.png
diff --git a/xos/configurations/devel/Makefile b/xos/configurations/devel/Makefile
index 24b7be7..216673d 100644
--- a/xos/configurations/devel/Makefile
+++ b/xos/configurations/devel/Makefile
@@ -7,6 +7,8 @@
 devstack: common_devstack xos
 
 xos:
+	rm ../../xos_configuration/*
+	cp ../common/xos_common_config ../../xos_configuration/
 	echo "# Autogenerated -- do not edit" > Dockerfile
 	cat ../common/Dockerfile.common Dockerfile.devel >> Dockerfile
 	cd ../../..; sudo docker build -t xosproject/xos-devel -f xos/configurations/devel/Dockerfile .
diff --git a/xos/configurations/frontend/Makefile b/xos/configurations/frontend/Makefile
index 0a1f04a..e30877a 100644
--- a/xos/configurations/frontend/Makefile
+++ b/xos/configurations/frontend/Makefile
@@ -6,15 +6,20 @@
 all: frontend
 
 frontend:
+	rm ../../xos_configuration/*
 	sudo apt-get -y install httpie
 	cat ../common/Dockerfile.common Dockerfile.frontend > Dockerfile
+	cp ../common/xos_common_config ../../xos_configuration/
 	cd ../../..; sudo docker build -t xos -f xos/configurations/frontend/Dockerfile .
 	sudo docker run -v $(XOS_FOLDER)/../../core/xoslib:/opt/xos/core/xoslib -p 9999:8000 --add-host="0.0.0.0:127.0.0.1" xos
 	bash ../common/wait_for_xos.sh
 	echo $(RUNNING_CONTAINER)
 
 interactive:
+	rm ../../xos_configuration/*
 	cat ../common/Dockerfile.common Dockerfile.frontend > Dockerfile
+	cp ../common/xos_common_config ../../xos_configuration/
+	#cp ../cord/xos_cord_config ../../xos_configuration/
 	cd ../../..; sudo docker build -t xos -f xos/configurations/frontend/Dockerfile .
 	echo "Inside the container run: /usr/bin/make -C /opt/xos/configurations/frontend -f Makefile.inside"
 	sudo docker run -it -v $(shell pwd)/../..:/opt/xos -p 9999:8000 --add-host="0.0.0.0:127.0.0.1" xos
diff --git a/xos/configurations/frontend/Makefile.inside b/xos/configurations/frontend/Makefile.inside
index f3fc898..48046b2 100644
--- a/xos/configurations/frontend/Makefile.inside
+++ b/xos/configurations/frontend/Makefile.inside
@@ -3,7 +3,7 @@
 setup_xos:
 	chmod +x /opt/xos/scripts/opencloud;
 	/opt/xos/scripts/opencloud genkeys;
-	/opt/xos/scripts/opencloud remigrate;
+#	/opt/xos/scripts/opencloud remigrate;
 	bash /opt/xos/tosca/install_tosca.sh;
 	bash /opt/xos/scripts/docker_setup_xos
 	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/frontend/sample.yaml
diff --git a/xos/core/admin.py b/xos/core/admin.py
index be9dcc0..e41ad1d 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1848,7 +1848,7 @@
     user_readonly_inlines = []
     inlines = [NetworkParameterInline,]
     fieldsets = [
-        (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
+        (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'access', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
                 'classes':['suit-tab suit-tab-general']}),]
     suit_form_tabs = (('general','Network Template Details'), ('netparams', 'Parameters') )
 
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index 6894f9f..a019091 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -100,15 +100,17 @@
     TRANSLATION_CHOICES = (('none', 'none'), ('NAT', 'NAT'))
     TOPOLOGY_CHOICES = (('bigswitch', 'BigSwitch'), ('physical', 'Physical'), ('custom', 'Custom'))
     CONTROLLER_CHOICES = ((None, 'None'), ('onos', 'ONOS'), ('custom', 'Custom'))
+    ACCESS_CHOICES = ((None, 'None'), ('indirect', 'Indirect'), ('direct', 'Direct'))
 
     name = models.CharField(max_length=32)
     description = models.CharField(max_length=1024, blank=True, null=True)
     guaranteed_bandwidth = models.IntegerField(default=0)
     visibility = models.CharField(max_length=30, choices=VISIBILITY_CHOICES, default="private")
     translation = models.CharField(max_length=30, choices=TRANSLATION_CHOICES, default="none")
+    access = models.CharField(max_length=30, null=True, blank=True, choices=ACCESS_CHOICES, help_text="Advertise this network as a means for other slices to contact this slice")
     shared_network_name = models.CharField(max_length=30, blank=True, null=True)
     shared_network_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum network")
-    topology_kind = models.CharField(null=False, blank=False, max_length=30, choices=TOPOLOGY_CHOICES, default="BigSwitch")
+    topology_kind = models.CharField(null=False, blank=False, max_length=30, choices=TOPOLOGY_CHOICES, default="bigswitch")
     controller_kind = models.CharField(null=True, blank=True, max_length=30, choices=CONTROLLER_CHOICES, default=None)
 
     def __init__(self, *args, **kwargs):
@@ -126,6 +128,10 @@
             print >> sys.stderr, "XXX warning: topology_kind invalid case"
             self.toplogy_kind="custom"
 
+    def save(self, *args, **kwargs):
+        self.enforce_choices(self.access, self.ACCESS_CHOICES)
+        super(NetworkTemplate, self).save(*args, **kwargs)
+
     def __unicode__(self):  return u'%s' % (self.name)
 
 class Network(PlCoreBase, ParameterMixin):
diff --git a/xos/core/models/plcorebase.py b/xos/core/models/plcorebase.py
index 1a2c37c..360792f 100644
--- a/xos/core/models/plcorebase.py
+++ b/xos/core/models/plcorebase.py
@@ -132,6 +132,16 @@
             else:
                 return ("error", html_escape(self.backend_status, quote=True))
 
+    def enforce_choices(self, field, choices):
+        choices = [x[0] for x in choices]
+        for choice in choices:
+            if field==choice:
+                return
+            if (choice==None) and (field==""):
+                # allow "" and None to be equivalent
+                return
+        raise Exception("Field value %s is not in %s" % (field, str(choices)))
+
 class PlCoreBase(models.Model, PlModelMixIn):
     objects = PlCoreBaseManager()
     deleted_objects = PlCoreBaseDeletionManager()
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 2b450e0..135dce9 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -168,12 +168,35 @@
 
                 # print "add instance", s
 
+    def get_vtn_src_nets(self):
+        nets=[]
+        for slice in self.slices.all():
+            for ns in slice.networkslices.all():
+                if not ns.network:
+                    continue
+                if ns.network.template.access in ["direct", "indirect"]:
+                    # skip access networks; we want to use the private network
+                    continue
+                if ns.network.name in ["wan_network", "lan_network"]:
+                    # we don't want to attach to the vCPE's lan or wan network
+                    # we only want to attach to its private network
+                    # TODO: fix hard-coding of network name
+                    continue
+                for cn in ns.network.controllernetworks.all():
+                    if cn.net_id:
+                        net = {"name": ns.network.name, "net_id": cn.net_id}
+                        nets.append(net)
+        return nets
+
     def get_vtn_nets(self):
         nets=[]
         for slice in self.slices.all():
             for ns in slice.networkslices.all():
                 if not ns.network:
                     continue
+                if ns.network.template.access not in ["direct", "indirect"]:
+                    # skip anything that's not an access network
+                    continue
                 for cn in ns.network.controllernetworks.all():
                     if cn.net_id:
                         net = {"name": ns.network.name, "net_id": cn.net_id}
@@ -195,11 +218,11 @@
     def get_vtn_dependencies_names(self):
         return [x["name"]+"_"+x["net_id"] for x in self.get_vtn_dependencies_nets()]
 
-    def get_vtn_ids(self):
-        return [x["net_id"] for x in self.get_vtn_nets()]
+    def get_vtn_src_ids(self):
+        return [x["net_id"] for x in self.get_vtn_src_nets()]
 
-    def get_vtn_names(self):
-        return [x["name"]+"_"+x["net_id"] for x in self.get_vtn_nets()]
+    def get_vtn_src_names(self):
+        return [x["name"]+"_"+x["net_id"] for x in self.get_vtn_src_nets()]
 
 
 class ServiceAttribute(PlCoreBase):
diff --git a/xos/core/static/xos.css b/xos/core/static/xos.css
index 60140ce..40cfcc3 100644
--- a/xos/core/static/xos.css
+++ b/xos/core/static/xos.css
@@ -1,4 +1,4 @@
-************************
+/************************
 colors:
     tab - active/focus color
     background-color: #105E9E !important;
@@ -12,8 +12,8 @@
 
 *************************/
 
-html, body {
-  /*height: 100%;*/
+html, body, body.login {
+  height: 100%;
   min-height: 100%;
   margin: 0;
 }
@@ -27,6 +27,15 @@
   min-height: 100%;
 }*/
 
+/* ************************* LOGIN PAGE ************************* */
+
+body.login img.logo{
+   width: 250px;
+   display: block;
+   margin: 20px auto;
+   padding-top: 20px;
+}
+
 /* ************************* SIDENAV TOGGLE ************************* */
 
 #wrapper {
@@ -1361,4 +1370,4 @@
     width: auto;
     padding: 10px;
     border-radius: 5px;
-}
\ No newline at end of file
+}
diff --git a/xos/core/xoslib/methods/vtn.py b/xos/core/xoslib/methods/vtn.py
index a912613..14dd458 100644
--- a/xos/core/xoslib/methods/vtn.py
+++ b/xos/core/xoslib/methods/vtn.py
@@ -38,7 +38,7 @@
     def get_services_names(self, request, pk=None):
         result = {}
         for service in Service.objects.all():
-           for id in service.get_vtn_names():
+           for id in service.get_vtn_src_names():
                dependencies = service.get_vtn_dependencies_names()
                if dependencies:
                    result[id] = dependencies
@@ -47,7 +47,7 @@
     def get_services(self, request, pk=None):
         result = {}
         for service in Service.objects.all():
-           for id in service.get_vtn_ids():
+           for id in service.get_vtn_src_ids():
                dependencies = service.get_vtn_dependencies_ids()
                if dependencies:
                    result[id] = dependencies
diff --git a/xos/openstack_observer/steps/sync_controller_networks.py b/xos/openstack_observer/steps/sync_controller_networks.py
index ef238ee..b646936 100644
--- a/xos/openstack_observer/steps/sync_controller_networks.py
+++ b/xos/openstack_observer/steps/sync_controller_networks.py
@@ -66,7 +66,8 @@
 
 
     def map_sync_inputs(self, controller_network):
-        if (controller_network.network.template.name!='Private'):
+        # XXX This check should really be made from booleans, rather than using hardcoded network names
+        if (controller_network.network.template.name not in ['Private', 'Private-Indirect', 'Private-Direct']):
             logger.info("skipping network controller %s because it is not private" % controller_network)
             # We only sync private networks
             return SyncStep.SYNC_WITHOUT_RUNNING
@@ -81,7 +82,8 @@
             raise Exception('Could not save network controller %s'%controller_network)
 
     def map_delete_inputs(self, controller_network):
-	if (controller_network.network.template.name!='Private'):
+        # XXX This check should really be made from booleans, rather than using hardcoded network names
+	if (controller_network.network.template.name not in ['Private', 'Private-Indirect', 'Private-Direct']):
             # We only sync private networks
             return
 	try:
diff --git a/xos/templates/admin/base.html b/xos/templates/admin/base.html
index cae98ac..f049da9 100644
--- a/xos/templates/admin/base.html
+++ b/xos/templates/admin/base.html
@@ -64,7 +64,7 @@
       {% if not is_popup %}
       <div id="sidebar-wrapper">
         <a href="{% url 'admin:index' %}" class="hidden-xs">
-          <img class="logo" src="{% static 'cord_logo_3.png' %}"/>
+          <img class="logo" src="{% static XOS_BRANDING_ICON %}"/>
         </a>
         {% include 'suit/menu.html' %}
         <button class="navbar-toggle collapsed visible-xs" type="button">
@@ -328,4 +328,4 @@
     });
   </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/xos/templates/admin/login.html b/xos/templates/admin/login.html
index 5b7ec4d..87bccd0 100644
--- a/xos/templates/admin/login.html
+++ b/xos/templates/admin/login.html
@@ -40,7 +40,7 @@
 {% endif %}
 <div id="wrap">
 <div id="content-main">
-<h1><i class="icon-lock icon-white"></i> OpenCloud</h1>
+          <img class="logo" src="{% static XOS_BRANDING_ICON %}"/>
 <form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
   <div class="form-row">
     {% if not form.this_is_the_login_form.errors %}{{ form.username.errors }}{% endif %}
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index a806327..907d54c 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -350,6 +350,10 @@
                 type: string
                 required: false
                 description: Indicates the type of controller that the network is connected to.
+            access:
+                type: string
+                required: false
+                description: The type of access semantics for this network
 
     tosca.nodes.network.Network.XOS:
           # Due to bug? in implementation, we have to copy everything from
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 3339fdf..60968a5 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -494,6 +494,10 @@
                 type: string
                 required: false
                 description: Indicates the type of controller that the network is connected to.
+            access:
+                type: string
+                required: false
+                description: The type of access semantics for this network
 
     tosca.nodes.network.Network.XOS:
           # Due to bug? in implementation, we have to copy everything from
diff --git a/xos/tosca/resources/networktemplate.py b/xos/tosca/resources/networktemplate.py
index 557964e..3a5ce59 100644
--- a/xos/tosca/resources/networktemplate.py
+++ b/xos/tosca/resources/networktemplate.py
@@ -12,7 +12,7 @@
 class XOSNetworkTemplate(XOSResource):
     provides = "tosca.nodes.NetworkTemplate"
     xos_model = NetworkTemplate
-    copyin_props = ["visibility", "translation", "shared_network_name", "shared_network_id", "toplogy_kind", "controller_kind"]
+    copyin_props = ["visibility", "translation", "shared_network_name", "shared_network_id", "toplogy_kind", "controller_kind", "access"]
 
     def get_xos_args(self):
         args = super(XOSNetworkTemplate, self).get_xos_args()
diff --git a/xos/tosca/samples/helloworld-chain.yaml b/xos/tosca/samples/helloworld-chain.yaml
new file mode 100644
index 0000000..decd3cf
--- /dev/null
+++ b/xos/tosca/samples/helloworld-chain.yaml
@@ -0,0 +1,76 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Two services "service_one" and "service_two" with a tenancy relationship.
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    Private-Indirect:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          access: indirect
+
+    mysite:
+      type: tosca.nodes.Site
+
+    service_vcpe:
+      type: tosca.nodes.Service
+      requirements:
+          - helloworld_tenant:
+              node: service_helloworld
+              relationship: tosca.relationships.TenantOfService
+
+    service_helloworld:
+      type: tosca.nodes.Service
+
+    mysite_helloworld:
+      type: tosca.nodes.Slice
+      requirements:
+          - service:
+              node: service_helloworld
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    helloworld_access:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
+      requirements:
+          - network_template:
+              node: Private-Indirect
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_helloworld
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_helloworld
+              relationship: tosca.relationships.ConnectsToSlice
+
+    # we need at least one instance to make the Networks instantiate
+    helloworld_instance:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: rhel
+            version: 6.5
+      requirements:
+          - slice:
+                node: mysite_helloworld
+                relationship: tosca.relationships.MemberOfSlice
+
diff --git a/xos/xos/config.py b/xos/xos/config.py
index 83d69cb..7a64de9 100644
--- a/xos/xos/config.py
+++ b/xos/xos/config.py
@@ -13,7 +13,7 @@
 """
 
 XOS_DIR = "/opt/xos"
-DEFAULT_CONFIG_FN = os.path.join(XOS_DIR, "xos_config")
+DEFAULT_CONFIG_FN = os.path.join(XOS_DIR, "xos_configuration/")
 
 # warning for now, remove once we're sure everyone has made the change
 if (os.path.exists("/opt/planetstack/plstackapi_config") and (not os.path.exists(DEFAULT_CONFIG_FN))):
@@ -40,7 +40,7 @@
 		self.config_path = os.path.dirname(config_file)
 		self.config = ConfigParser.ConfigParser()
 		self.filename = config_file
-		if not os.path.isfile(self.filename):
+		if not os.path.isfile(self.filename) and not os.path.isdir(self.filename):
 			self.create(self.filename)
 		self.load(self.filename)
 
@@ -80,7 +80,12 @@
 	def load(self, filename):
 		if filename:
 			try:
-				self.config.read(filename)
+				if os.path.isdir(filename):
+					config_list = list(reversed(os.listdir(filename)))
+					config_list = [os.path.join(filename, s) for s in config_list]
+					self.config.read(config_list)
+				else:
+					self.config.read(filename)
 			except ConfigParser.MissingSectionHeaderError:
 				if filename.endswith('.xml'):
 					self.load_xml(filename)
diff --git a/xos/xos_config b/xos/xos_configuration/xos_common_config
old mode 100644
new mode 100755
similarity index 100%
rename from xos/xos_config
rename to xos/xos_configuration/xos_common_config
diff --git a/xos/xos_configuration/xos_frontend_config b/xos/xos_configuration/xos_frontend_config
new file mode 100755
index 0000000..13fe53b
--- /dev/null
+++ b/xos/xos_configuration/xos_frontend_config
@@ -0,0 +1,4 @@
+[gui]
+branding_name=CORD
+branding_css=/static/cord.css
+branding_icon=/static/onos-logo.png