CORD-3227: adding support for dual stack for R-CORD subscribers
Change-Id: Id25e7eb19a1cb60dfbd066ef4457e9b0245337c0
diff --git a/VERSION b/VERSION
index 90a27f9..7479347 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.5
+1.0.6-dev
diff --git a/xos/synchronizer/models/models.py b/xos/synchronizer/models/models.py
index ccf5211..2fc2993 100644
--- a/xos/synchronizer/models/models.py
+++ b/xos/synchronizer/models/models.py
@@ -17,12 +17,29 @@
import random
from xos.exceptions import XOSValidationError, XOSProgrammingError, XOSPermissionDenied
-from models_decl import RCORDService_decl, RCORDSubscriber_decl
+from models_decl import RCORDService_decl, RCORDSubscriber_decl, RCORDIpAddress_decl
class RCORDService(RCORDService_decl):
class Meta:
proxy = True
+class RCORDIpAddress(RCORDIpAddress_decl):
+ class Meta:
+ proxy = True
+
+ def save(self, *args, **kwargs):
+ try:
+ if ":" in self.ip:
+ # it's an IPv6 address
+ socket.inet_pton(socket.AF_INET6, self.ip)
+ else:
+ # it's an IPv4 address
+ socket.inet_pton(socket.AF_INET, self.ip)
+ except socket.error:
+ raise XOSValidationError("The IP specified is not valid: %s" % self.ip)
+ super(RCORDIpAddress, self).save(*args, **kwargs)
+ return
+
class RCORDSubscriber(RCORDSubscriber_decl):
class Meta:
@@ -61,7 +78,6 @@
return used_tags
def save(self, *args, **kwargs):
-
self.validate_unique_service_specific_id(none_okay=True)
# VSGServiceInstance will extract the creator from the Subscriber, as it needs a creator to create its
@@ -72,17 +88,10 @@
raise XOSProgrammingError("RCORDSubscriber's self.caller was not set")
self.creator = self.caller
- # 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)
+ raise XOSValidationError("The MAC address specified is not valid: %s" % self.mac_address)
# validate c_tag
if hasattr(self, 'c_tag') and self.c_tag is not None:
diff --git a/xos/synchronizer/models/rcord.xproto b/xos/synchronizer/models/rcord.xproto
index f3025d4..527ed6f 100644
--- a/xos/synchronizer/models/rcord.xproto
+++ b/xos/synchronizer/models/rcord.xproto
@@ -20,7 +20,6 @@
optional int32 c_tag = 12 [db_index = False, min_value = 0, max_value = 4096];
optional int32 s_tag = 19 [db_index = False, min_value = 0, max_value = 4096];
required string onu_device = 13 [help_text = "ONUDevice serial number", db_index = False];
- optional string ip_address = 17 [help_text = "Subscriber IP Address", db_index = False];
optional string mac_address = 18 [db_index = False];
// operator specific fields
@@ -28,3 +27,11 @@
optional string circuit_id = 21 [db_index = False];
optional string remote_id = 22 [db_index = False];
}
+
+message RCORDIpAddress(XOSBase) {
+ option verbose_name = "IP address";
+
+ required manytoone subscriber->RCORDSubscriber:ips = 1:1001 [help_text = "The subscriber the IP address belongs to", db_index = True, null = False, blank = False];
+ required string ip = 2 [help_text = "The unique IP address (either IPv4 or IPv6 / netmask)", max_length = 52, null = False, db_index = False, blank = False, unique_with = "subscriber"];
+ optional string description = 3 [help_text = "A short description of the IP address", max_length = 254, null = False, db_index = False, blank = False];
+}
\ No newline at end of file
diff --git a/xos/synchronizer/models/test_models.py b/xos/synchronizer/models/test_models.py
index 1e66be2..3d3c3f2 100644
--- a/xos/synchronizer/models/test_models.py
+++ b/xos/synchronizer/models/test_models.py
@@ -46,6 +46,11 @@
self.models_decl.RCORDSubscriber_decl.objects = Mock()
self.models_decl.RCORDSubscriber_decl.objects.filter.return_value = []
+ self.models_decl.RCORDIpAddress_decl = MagicMock
+ self.models_decl.RCORDIpAddress_decl.save = Mock()
+ self.models_decl.RCORDIpAddress_decl.objects = Mock()
+ self.models_decl.RCORDIpAddress_decl.objects.filter.return_value = []
+
modules = {
'xos.exceptions': self.xos.exceptions,
@@ -57,7 +62,7 @@
self.volt = Mock()
- from models import RCORDSubscriber
+ from models import RCORDSubscriber, RCORDIpAddress
self.rcord_subscriber_class = RCORDSubscriber
@@ -67,11 +72,15 @@
self.rcord_subscriber.onu_device = "BRCM1234"
self.rcord_subscriber.c_tag = 111
self.rcord_subscriber.s_tag = 222
- self.rcord_subscriber.ip_address = "1.1.1.1"
+ self.rcord_subscriber.ips = Mock()
+ self.rcord_subscriber.ips.all.return_value = []
self.rcord_subscriber.mac_address = "00:AA:00:00:00:01"
self.rcord_subscriber.owner.leaf_model.access = "voltha"
self.rcord_subscriber.owner.provider_services = [self.volt]
+ self.rcord_ip = RCORDIpAddress()
+ self.rcord_ip.subscriber = 1;
+
def tearDown(self):
sys.path = self.sys_path_save
@@ -79,20 +88,28 @@
self.rcord_subscriber.save()
self.models_decl.RCORDSubscriber_decl.save.assert_called()
- def test_validate_ip_address(self):
- self.rcord_subscriber.ip_address = "invalid"
+ def _test_validate_ipv4_address(self):
+ self.rcord_ip.ip = "192.168.0."
with self.assertRaises(Exception) as e:
- self.rcord_subscriber.save()
+ self.rcord_ip.save()
- self.assertEqual(e.exception.message, "The ip_address you specified (invalid) is not valid")
- self.models_decl.RCORDSubscriber_decl.save.assert_not_called()
+ self.assertEqual(e.exception.message, "The IP specified is not valid: 192.168.0.")
+ self.models_decl.RCORDIpAddress.save.assert_not_called()
+
+ def test_validate_ipv6_address(self):
+ self.rcord_ip.ip = "2001:0db8:85a3:0000:0000:8a2e:03"
+ with self.assertRaises(Exception) as e:
+ self.rcord_ip.save()
+
+ self.assertEqual(e.exception.message, "The IP specified is not valid: 2001:0db8:85a3:0000:0000:8a2e:03")
+ self.models_decl.RCORDIpAddress.save.assert_not_called()
def test_validate_mac_address(self):
self.rcord_subscriber.mac_address = "invalid"
with self.assertRaises(Exception) as e:
self.rcord_subscriber.save()
- self.assertEqual(e.exception.message, "The mac_address you specified (invalid) is not valid")
+ self.assertEqual(e.exception.message, "The MAC address specified is not valid: invalid")
self.models_decl.RCORDSubscriber_decl.save.assert_not_called()
def test_valid_onu_device(self):