SEBA-497 delayering, Makefile, and tox for rcord-synchronizer
Change-Id: I1005db39de51e43bdfd9b17fe6290a37131e4605
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..b8f06bb
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+.tox
+venv-service
diff --git a/.gitignore b/.gitignore
index a04b416..0d57996 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,7 @@
-.idea/
-*.pyc
-
-.coverage
-htmlcov
+.idea
+.tox
.coverage
coverage.xml
-nose2-junit.xml
+nose2-results.xml
+venv-service
+*.pyc
diff --git a/Dockerfile.synchronizer b/Dockerfile.synchronizer
index c94b355..0d156c2 100644
--- a/Dockerfile.synchronizer
+++ b/Dockerfile.synchronizer
@@ -17,8 +17,14 @@
# xosproject/rcord-synchronizer
-FROM xosproject/xos-synchronizer-base:3.0.0
+FROM xosproject/alpine-grpc-base:0.9.0
+# Install pip packages
+COPY requirements.txt /tmp/requirements.txt
+RUN pip install -r /tmp/requirements.txt \
+ && pip freeze > /var/xos/pip_freeze_rcord_service_`date -u +%Y%m%dT%H%M%S`
+
+# Copy files
COPY xos/synchronizer /opt/xos/synchronizers/rcord
COPY VERSION /opt/xos/synchronizers/rcord/
@@ -55,4 +61,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 "python rcord-synchronizer.py"
+CMD ["/usr/bin/python", "/opt/xos/synchronizers/rcord/rcord-synchronizer.py"]
diff --git a/Makefile b/Makefile
index 94efe35..3d56a8a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,80 @@
-REGISTRY ?=
-REPOSITORY ?=
-DOCKER_BUILD_ARGS ?=
-SERVICE ?= rcord
-MAKEFILE_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
-TAG ?= $(shell cat ${MAKEFILE_DIR}/VERSION)
-IMAGENAME := ${REGISTRY}${REPOSITORY}${SERVICE}-synchronizer:${TAG}
-SHELL := /bin/bash
+# Copyright 2019-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.
-all: build push
+# Configure shell
+SHELL = bash -e -o pipefail
-build:
- docker build $(DOCKER_BUILD_ARGS) -t ${IMAGENAME} -f Dockerfile.synchronizer .
+# Variables
+VERSION ?= $(shell cat ./VERSION)
+SERVICE_NAME ?= $(notdir $(abspath .))
+SYNCHRONIZER_NAME ?= rcord-synchronizer
-push:
- docker push ${IMAGENAME}
+## Docker related
+DOCKER_REGISTRY ?=
+DOCKER_REPOSITORY ?=
+DOCKER_BUILD_ARGS ?=
+DOCKER_TAG ?= ${VERSION}
+DOCKER_IMAGENAME := ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}${SYNCHRONIZER_NAME}:${DOCKER_TAG}
-test:
- source ../../xos/venv-xos/bin/activate && cd xos && nose2 --verbose --coverage-report term || echo "Please install the XOS virtual environment"
+## Docker labels. Only set ref and commit date if committed
+DOCKER_LABEL_VCS_URL ?= $(shell git remote get-url $(shell git remote))
+DOCKER_LABEL_VCS_REF ?= $(shell git diff-index --quiet HEAD -- && git rev-parse HEAD || echo "unknown")
+DOCKER_LABEL_COMMIT_DATE ?= $(shell git diff-index --quiet HEAD -- && git show -s --format=%cd --date=iso-strict HEAD || echo "unknown" )
+DOCKER_LABEL_BUILD_DATE ?= $(shell date -u "+%Y-%m-%dT%H:%M:%SZ")
-migrate:
- source ../../xos/venv-xos/bin/activate && cd xos && xos-migrate -s $(SERVICE)
\ No newline at end of file
+## Migration related - paths are relative to the xos subdirectory within this repo
+XOS_DIR ?= "../../../xos"
+SERVICES_DIR ?= "../.."
+
+all: test
+
+docker-build:
+ docker build $(DOCKER_BUILD_ARGS) \
+ -t ${DOCKER_IMAGENAME} \
+ --build-arg org_label_schema_version="${VERSION}" \
+ --build-arg org_label_schema_vcs_url="${DOCKER_LABEL_VCS_URL}" \
+ --build-arg org_label_schema_vcs_ref="${DOCKER_LABEL_VCS_REF}" \
+ --build-arg org_label_schema_build_date="${DOCKER_LABEL_BUILD_DATE}" \
+ --build-arg org_opencord_vcs_commit_date="${DOCKER_LABEL_COMMIT_DATE}" \
+ -f Dockerfile.synchronizer .
+
+docker-push:
+ docker push ${DOCKER_IMAGENAME}
+
+test: test-unit test-migration
+
+test-unit:
+ tox
+
+venv-service:
+ virtualenv $@;\
+ source ./$@/bin/activate ; set -u ;\
+ pip install -r requirements.txt xosmigrate~=3.0.1
+
+create-migration: venv-service
+ source ./venv-service/bin/activate; set -u;\
+ cd xos; xos-migrate --xos-dir ${XOS_DIR} --services-dir ${SERVICES_DIR} -s ${SERVICE_NAME}
+
+test-migration: venv-service
+ source ./venv-service/bin/activate; set -u;\
+ cd xos; xos-migrate --xos-dir ${XOS_DIR} --services-dir ${SERVICES_DIR} -s ${SERVICE_NAME} --check
+
+clean:
+ find . -name '*.pyc' | xargs rm -f
+ rm -rf \
+ .tox \
+ venv-service \
+ xos/.coverage \
+ xos/coverage.xml \
+ xos/nose2-results.xml
diff --git a/VERSION b/VERSION
index 0c64b5c..f0bb29e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.0-dev
+1.3.0
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e039eea
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+xossynchronizer~=3.0.1
+xosapi~=3.0.1
+xoskafka~=3.0.1
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..9064a11
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,46 @@
+; Copyright 2019-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.
+
+[tox]
+envlist = py27
+; future; envlist = py27,py35,py36,py37
+skip_missing_interpreters = true
+skipsdist = True
+
+[testenv]
+deps =
+ -r requirements.txt
+ requests_mock
+ nose2
+; flake8
+
+changedir = xos
+commands =
+ nose2 -c ../tox.ini --verbose --junit-xml
+; future: flake8
+
+[flake8]
+max-line-length = 119
+
+[unittest]
+plugins = nose2.plugins.junitxml
+
+[junit-xml]
+path = nose2-results.xml
+
+[coverage]
+always-on = True
+coverage-report =
+ term
+ xml
diff --git a/xos/synchronizer/__init__.py b/xos/synchronizer/__init__.py
new file mode 100644
index 0000000..19d1424
--- /dev/null
+++ b/xos/synchronizer/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019-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.
diff --git a/xos/synchronizer/model_policies/__init__.py b/xos/synchronizer/model_policies/__init__.py
new file mode 100644
index 0000000..19d1424
--- /dev/null
+++ b/xos/synchronizer/model_policies/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019-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.
diff --git a/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py b/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py
index cb260ed..dbf2b71 100644
--- a/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py
+++ b/xos/synchronizer/model_policies/model_policy_rcordsubscriber.py
@@ -16,6 +16,7 @@
from xossynchronizer.modelaccessor import ServiceInstanceLink, model_accessor
from xossynchronizer.model_policies.policy import Policy
+
class RCORDSubscriberPolicy(Policy):
model_name = "RCORDSubscriber"
@@ -25,7 +26,9 @@
def handle_update(self, si):
if si.status == "pre-provisioned":
- self.logger.debug("MODEL_POLICY: Skipping chain creation as RCORDSubscriber %s is in 'pre-provisioned' state" % si.id)
+ self.logger.debug(
+ "MODEL_POLICY: Skipping chain creation as RCORDSubscriber %s is in 'pre-provisioned' state" %
+ si.id)
return
chain = si.subscribed_links.all()
@@ -37,7 +40,9 @@
# delete chain
self.logger.debug("MODEL_POLICY: deleting RCORDSubscriber chain from %s" % si.id, status=si.status)
for link in chain:
- self.logger.debug("Removing link %s" % link.id, provider_service=link.provider_service_instance.leaf_model, subscriber_service=link.subscriber_service_instance.leaf_model)
+ self.logger.debug("Removing link %s" % link.id,
+ provider_service=link.provider_service_instance.leaf_model,
+ subscriber_service=link.subscriber_service_instance.leaf_model)
link.delete()
link.provider_service_instance.leaf_model.delete()
diff --git a/xos/synchronizer/model_policies/test_model_policy_rcordsubscriber.py b/xos/synchronizer/model_policies/test_model_policy_rcordsubscriber.py
index 05df866..3405c6a 100644
--- a/xos/synchronizer/model_policies/test_model_policy_rcordsubscriber.py
+++ b/xos/synchronizer/model_policies/test_model_policy_rcordsubscriber.py
@@ -17,9 +17,11 @@
from mock import patch, Mock
-import os, sys
+import os
+import sys
-test_path=os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+
class TestModelPolicyRCORDSubscriber(unittest.TestCase):
def setUp(self):
@@ -38,7 +40,7 @@
import xossynchronizer.modelaccessor
import mock_modelaccessor
- reload(mock_modelaccessor) # in case nose2 loaded it in a previous test
+ reload(mock_modelaccessor) # in case nose2 loaded it in a previous test
reload(xossynchronizer.modelaccessor) # in case nose2 loaded it in a previous test
from xossynchronizer.modelaccessor import model_accessor
@@ -68,7 +70,7 @@
self.policy.handle_create(si)
with patch.object(VOLTServiceInstance, "save", autospec=True) as save_volt, \
- patch.object(ServiceInstanceLink, "save", autospec=True) as save_link:
+ patch.object(ServiceInstanceLink, "save", autospec=True) as save_link:
self.policy.handle_create(si)
self.assertEqual(save_link.call_count, 0)
@@ -78,9 +80,9 @@
si = self.si
si.is_new = False
si.subscribed_links.all.return_value = ["already", "have", "a", "chain"]
-
+
with patch.object(VOLTServiceInstance, "save", autospec=True) as save_volt, \
- patch.object(ServiceInstanceLink, "save", autospec=True) as save_link:
+ patch.object(ServiceInstanceLink, "save", autospec=True) as save_link:
self.policy.handle_create(si)
self.assertEqual(save_link.call_count, 0)
@@ -100,7 +102,7 @@
si.owner.subscribed_dependencies.all.return_value = [service_dependency]
with patch.object(VOLTServiceInstance, "save", autospec=True) as save_volt, \
- patch.object(ServiceInstanceLink, "save", autospec=True) as save_link:
+ patch.object(ServiceInstanceLink, "save", autospec=True) as save_link:
self.policy.handle_create(si)
self.assertEqual(save_link.call_count, 1)
@@ -111,18 +113,17 @@
volt.name = "volt"
link = ServiceInstanceLink()
- link.subscriber_service_instance= self.si
+ link.subscriber_service_instance = self.si
link.provider_service_instance = volt
link.provider_service_instance.leaf_model = volt
-
si = self.si
si.is_new = False
si.status = "awaiting-auth"
si.subscribed_links.all.return_value = [link]
with patch.object(VOLTServiceInstance, "delete", autospec=True) as delete_volt, \
- patch.object(ServiceInstanceLink, "delete", autospec=True) as delete_link:
+ patch.object(ServiceInstanceLink, "delete", autospec=True) as delete_link:
self.policy.handle_create(si)
self.assertEqual(delete_link.call_count, 1)
diff --git a/xos/synchronizer/models/__init__.py b/xos/synchronizer/models/__init__.py
new file mode 100644
index 0000000..19d1424
--- /dev/null
+++ b/xos/synchronizer/models/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019-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.
diff --git a/xos/synchronizer/models/convenience/rcordsubscriber.py b/xos/synchronizer/models/convenience/rcordsubscriber.py
index 47c29ed..e6e7bf0 100644
--- a/xos/synchronizer/models/convenience/rcordsubscriber.py
+++ b/xos/synchronizer/models/convenience/rcordsubscriber.py
@@ -14,9 +14,9 @@
# limitations under the License.
-import json
from xosapi.orm import ORMWrapper, register_convenience_wrapper
+
class ORMWrapperRCORDSubscriber(ORMWrapper):
@property
def volt(self):
@@ -24,7 +24,7 @@
for link in links:
# FIXME: hardcoded service dependency
# cast from ServiceInstance to VOLTServiceInstance
- volts = self.stub.VOLTServiceInstance.objects.filter(id = link.provider_service_instance.id)
+ volts = self.stub.VOLTServiceInstance.objects.filter(id=link.provider_service_instance.id)
if volts:
return volts[0]
return None
@@ -38,4 +38,5 @@
"downlink_speed",
"status")
+
register_convenience_wrapper("RCORDSubscriber", ORMWrapperRCORDSubscriber)
diff --git a/xos/synchronizer/models/models.py b/xos/synchronizer/models/models.py
index 7227ab9..f5ac079 100644
--- a/xos/synchronizer/models/models.py
+++ b/xos/synchronizer/models/models.py
@@ -16,17 +16,20 @@
import socket
import random
-from xos.exceptions import XOSValidationError, XOSProgrammingError, XOSPermissionDenied
+from xos.exceptions import XOSValidationError, XOSProgrammingError
from models_decl import RCORDService_decl, RCORDSubscriber_decl, RCORDIpAddress_decl, BandwidthProfile_decl
+
class BandwidthProfile(BandwidthProfile_decl):
class Meta:
proxy = True
+
class RCORDService(RCORDService_decl):
class Meta:
proxy = True
+
class RCORDIpAddress(RCORDIpAddress_decl):
class Meta:
proxy = True
@@ -44,6 +47,7 @@
super(RCORDIpAddress, self).save(*args, **kwargs)
return
+
class RCORDSubscriber(RCORDSubscriber_decl):
class Meta:
@@ -149,7 +153,9 @@
is_update_with_same_tag = True
if self.c_tag in self.get_used_c_tags() and not is_update_with_same_tag:
- raise XOSValidationError("The c_tag you specified (%s) has already been used on device %s" % (self.c_tag, self.onu_device))
+ raise XOSValidationError(
+ "The c_tag you specified (%s) has already been used on device %s" %
+ (self.c_tag, self.onu_device))
# validate s_tag and c_tag combination
if self.c_tag and self.s_tag:
@@ -173,10 +179,14 @@
self.set_owner()
- if self.status != "pre-provisioned" and hasattr(self.owner.leaf_model, "access") and self.owner.leaf_model.access == "voltha" and not self.deleted:
+ if self.status != "pre-provisioned" and \
+ hasattr(self.owner.leaf_model, "access") and \
+ self.owner.leaf_model.access == "voltha" and \
+ not self.deleted:
# if the access network is managed by voltha, validate that onu_device actually exist
- volt_service = self.owner.provider_services[0].leaf_model # we assume RCORDService is connected only to the vOLTService
+ # we assume RCORDService is connected only to the vOLTService
+ volt_service = self.owner.provider_services[0].leaf_model
if not volt_service.has_access_device(self.onu_device):
raise XOSValidationError("The onu_device you specified (%s) does not exists" % self.onu_device)
diff --git a/xos/synchronizer/models/test_models.py b/xos/synchronizer/models/test_models.py
index 072b7f5..4fe767f 100644
--- a/xos/synchronizer/models/test_models.py
+++ b/xos/synchronizer/models/test_models.py
@@ -13,25 +13,30 @@
# limitations under the License.
import unittest
-import os, sys
+import os
+import sys
from mock import patch, Mock, MagicMock
-test_path=os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
-service_dir=os.path.join(test_path, "../../../..")
-xos_dir=os.path.join(test_path, "../../..")
+test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+service_dir = os.path.join(test_path, "../../../..")
+xos_dir = os.path.join(test_path, "../../..")
if not os.path.exists(os.path.join(test_path, "new_base")):
- xos_dir=os.path.join(test_path, "../../../../../../orchestration/xos/xos")
- services_dir=os.path.join(xos_dir, "../../xos_services")
+ xos_dir = os.path.join(test_path, "../../../../../../orchestration/xos/xos")
+ services_dir = os.path.join(xos_dir, "../../xos_services")
# mocking XOS exception, as they're based in Django
+
+
class Exceptions:
XOSValidationError = Exception
XOSProgrammingError = Exception
XOSPermissionDenied = Exception
+
class XOS:
exceptions = Exceptions
+
class TestRCORDModels(unittest.TestCase):
def setUp(self):
@@ -51,7 +56,6 @@
self.models_decl.RCORDIpAddress_decl.objects = Mock()
self.models_decl.RCORDIpAddress_decl.objects.filter.return_value = []
-
modules = {
'xos.exceptions': self.xos.exceptions,
'models_decl': self.models_decl
@@ -68,7 +72,7 @@
self.rcord_subscriber = RCORDSubscriber()
self.rcord_subscriber.deleted = False
- self.rcord_subscriber.id = None # this is a new model
+ self.rcord_subscriber.id = None # this is a new model
self.rcord_subscriber.is_new = True
self.rcord_subscriber.onu_device = "BRCM1234"
self.rcord_subscriber.c_tag = 111
@@ -80,7 +84,7 @@
self.rcord_subscriber.owner.provider_services = [self.volt]
self.rcord_ip = RCORDIpAddress()
- self.rcord_ip.subscriber = 1;
+ self.rcord_ip.subscriber = 1
def tearDown(self):
sys.path = self.sys_path_save
@@ -184,7 +188,8 @@
with self.assertRaises(Exception) as e:
self.rcord_subscriber.save()
- self.assertEqual(e.exception.message, "The c_tag(111) and s_tag(222) pair you specified,has already been used by Subscriber with Id (123)")
+ self.assertEqual(e.exception.message,
+ "The c_tag(111) and s_tag(222) pair you specified,has already been used by Subscriber with Id (123)")
self.models_decl.RCORDSubscriber_decl.save.assert_not_called()
def test_validate_c_tag_on_update(self):
diff --git a/xos/synchronizer/rcord-synchronizer.py b/xos/synchronizer/rcord-synchronizer.py
index 205bd4a..5f82ca5 100644
--- a/xos/synchronizer/rcord-synchronizer.py
+++ b/xos/synchronizer/rcord-synchronizer.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
# Copyright 2017-present Open Networking Foundation
#
@@ -13,9 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
-#!/usr/bin/env python
-
# This imports and runs ../../xos-observer.py
import os