allow AccessDevice to be specified as a comma-separated list in VOLTDevice
diff --git a/xos/configurations/cord-pod/cord-volt-devices.yaml b/xos/configurations/cord-pod/cord-volt-devices.yaml
index 5068fa8..8b41623 100644
--- a/xos/configurations/cord-pod/cord-volt-devices.yaml
+++ b/xos/configurations/cord-pod/cord-volt-devices.yaml
@@ -19,20 +19,17 @@
       properties:
             driver: pmc-olt
             openflow_id: of:1000000000000001
+            access_devices: >
+              2 222,
+              3 223,
+              4 224
       requirements:
           - volt_service:
               node: service#volt
               relationship: tosca.relationships.MemberOfService
-
-    access-2-222:
-      type: tosca.nodes.AccessDevice
-      properties:
-          uplink: 2
-          vlan: 222
-      requirements:
-          - volt_device:
-              node: voltdev-1
-              relationship: tosca.relationships.MemberOfDevice
+          - access_agent:
+              node: agent-1
+              relationship: tosca.relationships.UsesAgent
 
     agent-1:
       type: tosca.nodes.AccessAgent
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index e72ea2f..346f0fd 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -349,21 +349,27 @@
                 type: string
                 required: false
                 description: driver name
+            access_devices:
+                type: string
+                required: false
+                description: list of access devices, in format "uplink vlan", multiple entries separated by commas
 
-    tosca.nodes.AccessDevice:
-        derived_from: tosca.nodes.Root
-        description: >
-            CORD: A vOLT Access Device.
-        properties:
-            xos_base_props
-            uplink:
-               type: integer
-               required: false
-               description: uplink
-            vlan:
-               type: integer
-               required: false
-               description: vlan
+# XXX - uncomment if we want access device to be specified as separate Tosca
+# objects, rather than encoding them into VOLTDevice.access_devices
+#    tosca.nodes.AccessDevice:
+#        derived_from: tosca.nodes.Root
+#        description: >
+#            CORD: A vOLT Access Device.
+#        properties:
+#            xos_base_props
+#            uplink:
+#               type: integer
+#               required: false
+#               description: uplink
+#            vlan:
+#               type: integer
+#               required: false
+#               description: vlan
 
     tosca.nodes.AccessAgent:
         derived_from: tosca.nodes.Root
@@ -378,7 +384,7 @@
             port_mappings:
                 type: string
                 required: false
-                description: list of port mappings, separated by commas
+                description: list of port mappings, in format "port mac", multiple entries separated by commas
 
 
     tosca.nodes.User:
@@ -1021,6 +1027,9 @@
     tosca.relationships.MemberOfDevice:
         derived_from: tosca.relationships.Root
 
+    tosca.relationships.UsesAgent:
+        derived_from: tosca.relationships.Root
+
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
         description: An XOS Service
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 40ab3a7..e802593 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -876,36 +876,25 @@
                 type: string
                 required: false
                 description: driver name
-
-    tosca.nodes.AccessDevice:
-        derived_from: tosca.nodes.Root
-        description: >
-            CORD: A vOLT Access Device.
-        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
-            replaces:
+            access_devices:
                 type: string
                 required: false
-                descrption: Replaces/renames this object
-            uplink:
-               type: integer
-               required: false
-               description: uplink
-            vlan:
-               type: integer
-               required: false
-               description: vlan
+                description: list of access devices, in format "uplink vlan", multiple entries separated by commas
+
+#    tosca.nodes.AccessDevice:
+#        derived_from: tosca.nodes.Root
+#        description: >
+#            CORD: A vOLT Access Device.
+#        properties:
+#            xos_base_props
+#            uplink:
+#               type: integer
+#               required: false
+#               description: uplink
+#            vlan:
+#               type: integer
+#               required: false
+#               description: vlan
 
     tosca.nodes.AccessAgent:
         derived_from: tosca.nodes.Root
@@ -935,7 +924,7 @@
             port_mappings:
                 type: string
                 required: false
-                description: list of port mappings, separated by commas
+                description: list of port mappings, in format "port mac", multiple entries separated by commas
 
 
     tosca.nodes.User:
diff --git a/xos/tosca/resources/accessagent.py b/xos/tosca/resources/accessagent.py
index 99d576b..368ce55 100644
--- a/xos/tosca/resources/accessagent.py
+++ b/xos/tosca/resources/accessagent.py
@@ -23,6 +23,13 @@
         return args
 
     def postprocess(self, obj):
+        # For convenient, allow the port mappings to be specified by a Tosca
+        # string with commas between lines.
+        #      <port> <mac>,
+        #      <port> <mac>,
+        #      ...
+        #      <port> <mac>
+
         port_mappings_str = self.get_property("port_mappings")
         port_mappings = []
         if port_mappings_str:
diff --git a/xos/tosca/resources/voltdevice.py b/xos/tosca/resources/voltdevice.py
index 20978cc..f1c6830 100644
--- a/xos/tosca/resources/voltdevice.py
+++ b/xos/tosca/resources/voltdevice.py
@@ -5,7 +5,7 @@
 sys.path.append("/opt/tosca")
 from translator.toscalib.tosca_template import ToscaTemplate
 
-from services.cord.models import VOLTDevice, VOLTService
+from services.cord.models import VOLTDevice, VOLTService, AccessDevice, AccessAgent
 from xosresource import XOSResource
 
 class XOSVOLTDevice(XOSResource):
@@ -20,4 +20,33 @@
         if volt_service_name:
             args["volt_service"] = self.get_xos_object(VOLTService, throw_exception=throw_exception, name=volt_service_name)
 
+        agent_name = self.get_requirement("tosca.relationships.UsesAgent", throw_exception=throw_exception)
+        if agent_name:
+            args["access_agent"] = self.get_xos_object(AccessAgent, throw_exception=throw_exception, name=agent_name)
+
         return args
+
+    def postprocess(self, obj):
+        access_devices_str = self.get_property("access_devices")
+        access_devices = []
+        if access_devices_str:
+            lines = [x.strip() for x in access_devices_str.split(",")]
+            for line in lines:
+                if not (" " in line):
+                    raise "Malformed access device `%s`", line
+                (uplink, vlan) = line.split(" ")
+                uplink=int(uplink.strip())
+                vlan=int(vlan.strip())
+                access_devices.append( (uplink, vlan) )
+
+            for ad in list(AccessDevice.objects.filter(volt_device=obj)):
+                if (ad.uplink, ad.vlan) not in access_devices:
+                    print "Deleting AccessDevice '%s'" % ad
+                    ad.delete()
+
+            for access_device in access_devices:
+                existing_objs = AccessDevice.objects.filter(volt_device=obj, uplink=access_device[0], vlan=access_device[1])
+                if not existing_objs:
+                    ad = AccessDevice(volt_device=obj, uplink=access_device[0], vlan=access_device[1])
+                    ad.save()
+                    print "Created AccessDevice '%s'" % ad