Merge "[CORD-2796][CORD-3007] Adding RCORDService and validating OLTDevice name"
diff --git a/Dockerfile.synchronizer b/Dockerfile.synchronizer
index 6e6c25e..f63121e 100644
--- a/Dockerfile.synchronizer
+++ b/Dockerfile.synchronizer
@@ -54,4 +54,4 @@
       org.opencord.component.xos.vcs-url=$org_opencord_component_xos_vcs_url \
       org.opencord.component.xos.vcs-ref=$org_opencord_component_xos_vcs_ref
 
-CMD bash -c "cd /opt/xos/synchronizers/rcord; ./run-from-api.sh"
+CMD bash -c "python rcord-synchronizer.py"
diff --git a/templates/cord-services.yaml.j2 b/templates/cord-services.yaml.j2
index b4d7529..46db7f0 100644
--- a/templates/cord-services.yaml.j2
+++ b/templates/cord-services.yaml.j2
@@ -29,6 +29,7 @@
   - custom_types/nodelabel.yaml
   - custom_types/onosapp.yaml
   - custom_types/onosservice.yaml
+  - custom_types/rcordservice.yaml
   - custom_types/site.yaml
   - custom_types/service.yaml
   - custom_types/servicedependency.yaml
@@ -70,7 +71,7 @@
 {% endif %}
 
     service#rcord:
-      type: tosca.nodes.Service
+      type: tosca.nodes.RCORDService
       properties:
         name: rcord
 
diff --git a/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py b/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py
index d74fda0..c64ff1d 100644
--- a/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py
+++ b/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py
@@ -17,7 +17,7 @@
 from synchronizers.new_base.policy import Policy
 
 class RCORDSubscriberPolicy(Policy):
-    model_name = "CordSubscriberRoot"
+    model_name = "RCORDSubscriber"
 
     def handle_create(self, si):
         return self.handle_update(si)
@@ -28,7 +28,7 @@
 
         # Already has a chain
         if len(chain) > 0 and not si.is_new:
-            self.logger.debug("MODEL_POLICY: Subscriber %s is already part of a chain" % si.id)
+            self.logger.debug("MODEL_POLICY: RCORDSubscriber %s is already part of a chain" % si.id)
             return
 
         # if it does not have a chain,
@@ -40,7 +40,7 @@
         for link in links:
             ps = link.provider_service.leaf_model
             si_class = link.provider_service.get_service_instance_class_name()
-            self.logger.info("MODEL_POLICY: RCORDSubscriberRoot %s creating %s" % (si, si_class))
+            self.logger.info("MODEL_POLICY: RCORDSubscriber %s creating %s" % (si, si_class))
 
             eastbound_si_class = model_accessor.get_model_class(si_class)
             eastbound_si = eastbound_si_class()
diff --git a/xos/synchronizer/models/convenience/cordsubscriberroot.py b/xos/synchronizer/models/convenience/cordsubscriberroot.py
index 5af889c..57e10a4 100644
--- a/xos/synchronizer/models/convenience/cordsubscriberroot.py
+++ b/xos/synchronizer/models/convenience/cordsubscriberroot.py
@@ -17,12 +17,12 @@
 import json
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
-class ORMWrapperCordSubscriberRoot(ORMWrapper):
+class ORMWrapperRCORDSubscriber(ORMWrapper):
     @property
     def volt(self):
         links = self.subscribed_links.all()
         for link in links:
-            # TODO: hardcoded service dependency
+            # FIXME: hardcoded service dependency
             # cast from ServiceInstance to VOLTServiceInstance
             volts = self.stub.VOLTServiceInstance.objects.filter(id = link.provider_service_instance.id)
             if volts:
@@ -52,4 +52,4 @@
     def devices(self):
         return self.get_attribute("devices", [])
 
-register_convenience_wrapper("CordSubscriberRoot", ORMWrapperCordSubscriberRoot)
+register_convenience_wrapper("RCORDSubscriber", ORMWrapperRCORDSubscriber)
diff --git a/xos/synchronizer/models/models.py b/xos/synchronizer/models/models.py
index 28244fa..271cb68 100644
--- a/xos/synchronizer/models/models.py
+++ b/xos/synchronizer/models/models.py
@@ -12,12 +12,17 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+import re
+import socket
 
-from xos.exceptions import *
-from models_decl import *
-from core.models import Service
+from xos.exceptions import XOSValidationError, XOSProgrammingError, XOSPermissionDenied
+from models_decl import RCORDService_decl, RCORDSubscriber_decl
 
-class CordSubscriberRoot(CordSubscriberRoot_decl):
+class RCORDService(RCORDService_decl):
+    class Meta:
+        proxy = True
+
+class RCORDSubscriber(RCORDSubscriber_decl):
     class Meta:
         proxy = True
 
@@ -37,26 +42,46 @@
     def save(self, *args, **kwargs):
         self.validate_unique_service_specific_id(none_okay=True)
 
-        # NOTE setting owner_id
-        try:
-            rcord_service = Service.objects.filter(name="rcord")[0]
-            self.owner_id = rcord_service.id
-        except IndexError:
-            raise XOSValidationError("Service RCORD cannot be found, please make sure that the model exists.")
-
         # VSGServiceInstance will extract the creator from the Subscriber, as it needs a creator to create its
         # Instance.
         if not self.creator:
             # If we weren't passed an explicit creator, then we will assume the caller is the creator.
             if not getattr(self, "caller", None):
-                raise XOSProgrammingError("CordSubscriberRoot's self.caller was not set")
+                raise XOSProgrammingError("RCORDSubscriber's self.caller was not set")
             self.creator = self.caller
 
-        # TODO: What is this for?
         if (not hasattr(self, 'caller') or not self.caller.is_admin):
             if (self.has_field_changed("service_specific_id")):
                 raise XOSPermissionDenied("You do not have permission to change service_specific_id")
 
-        super(CordSubscriberRoot, self).save(*args, **kwargs)
+        # validate IP Address
+        if hasattr(self, 'ip_address') and self.ip_address is not None:
+            try:
+                socket.inet_aton(self.ip_address)
+            except socket.error:
+                raise XOSValidationError("The ip_address you specified (%s) is not valid" % self.ip_address)
+
+        # validate MAC Address
+        if hasattr(self, 'mac_address') and self.mac_address is not None:
+            if not re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", self.mac_address.lower()):
+                raise XOSValidationError("The mac_address you specified (%s) is not valid" % self.mac_address)
+
+        if self.owner.leaf_model.access == "voltha":
+            # if the access network is managed by voltha, validate that olt_device and olt_port actually exists
+            volt_service = self.owner.provider_services[0].leaf_model # we assume RCORDService is connected only to the vOLTService
+
+            try:
+                olt_device = [d for d in volt_service.volt_devices.all() if d.name == self.olt_device][0]
+            except IndexError, e:
+                raise XOSValidationError("The olt_device you specified (%s) does not exists" % self.olt_device)
+
+            try:
+                olt_port = [p for p in olt_device.ports.all() if p.name == self.olt_port][0]
+            except IndexError, e:
+                raise XOSValidationError("The olt_port you specified (%s) does not exists on OLTDevice %s" % (self.olt_port, olt_device.name))
+
+
+
+        super(RCORDSubscriber, self).save(*args, **kwargs)
         self.invalidate_related_objects()
         return
\ No newline at end of file
diff --git a/xos/synchronizer/models/rcord.xproto b/xos/synchronizer/models/rcord.xproto
index 7a41dfa..8fe2f67 100644
--- a/xos/synchronizer/models/rcord.xproto
+++ b/xos/synchronizer/models/rcord.xproto
@@ -1,10 +1,16 @@
 option name = "rcord";
 option app_label = "rcord";
-option verbose_name = "RCORD Subscriber";
 option legacy="True";
 
-message CordSubscriberRoot (ServiceInstance) {
-    option kind = "CordSubscriberRoot";
+message RCORDService (Service) {
+    option verbose_name = "RCORD Service";
+    required string access = 11 [help_text = "Who is managing the Access Network", default = "voltha", choices = "(('voltha', 'VOLTHA'),)", max_length = 30, blank = False, null = False, db_index = False];
+}
+
+message RCORDSubscriber (ServiceInstance) {
+    option kind = "RCORDSubscriber";
+    option verbose_name = "RCORD Subscriber";
+    option owner_class_name = "RCORDService";
 
     required bool firewall_enable = 1 [default = False, null = False, db_index = False, blank = True];
     optional string firewall_rules = 2 [default = "accept all anywhere anywhere", null = True, db_index = False, blank = True];
@@ -23,6 +29,6 @@
     optional manytoone creator->User:created_rcord_subscribers = 15 [db_index = True, null = True, blank = True];
 
     optional int32 uni_port_id = 16 [help_text = "UNI PORT ID in VOLTHA", null = True, db_index = False, blank = False];
-    optional string ip_address = 17 [help_text = "Subscriber IP Address", null = False, db_index = False, blank = False];
-    optional string mac_address = 18 [null = False, db_index = False, blank = False];
+    optional string ip_address = 17 [help_text = "Subscriber IP Address", null = True, db_index = False, blank = False];
+    optional string mac_address = 18 [null = True, db_index = False, blank = False];
 }
diff --git a/xos/synchronizer/rcord_config.yaml b/xos/synchronizer/rcord_config.yaml
index 0527ca2..3775616 100644
--- a/xos/synchronizer/rcord_config.yaml
+++ b/xos/synchronizer/rcord_config.yaml
@@ -18,6 +18,9 @@
 accessor:
   username: xosadmin@opencord.org
   password: "@/opt/xos/services/rcord/credentials/xosadmin@opencord.org"
+required_models:
+  - RCORDService
+  - RCORDSubscriber
 dependency_graph: "/opt/xos/synchronizers/rcord/model-deps"
 sys_dir: "/opt/xos/synchronizers/rcord/sys"
 models_dir: "/opt/xos/synchronizers/rcord/models"
diff --git a/xos/synchronizer/run-from-api.sh b/xos/synchronizer/run-from-api.sh
deleted file mode 100755
index 1d936f7..0000000
--- a/xos/synchronizer/run-from-api.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-python rcord-synchronizer.py