SEBA-557 implement xos test service
Change-Id: I21ab4c9139a3be7e68cfff8ef506100f8fe3a2c3
diff --git a/testservice/Dockerfile.synchronizer b/testservice/Dockerfile.synchronizer
new file mode 100644
index 0000000..f3a2d69
--- /dev/null
+++ b/testservice/Dockerfile.synchronizer
@@ -0,0 +1,58 @@
+# Copyright 2018-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.
+
+# docker build -t smbaker/testservice-synchronizer:test -f Dockerfile.synchronizer .
+
+# xosproject/testservice
+
+FROM xosproject/xos-synchronizer-base:candidate
+
+COPY xos/synchronizer /opt/xos/synchronizers/testservice
+COPY VERSION /opt/xos/synchronizers/testservice/
+
+ENTRYPOINT []
+
+WORKDIR "/opt/xos/synchronizers/testservice"
+
+# Label image
+ARG org_label_schema_schema_version=1.0
+ARG org_label_schema_name=testservice
+ARG org_label_schema_version=unknown
+ARG org_label_schema_vcs_url=unknown
+ARG org_label_schema_vcs_ref=unknown
+ARG org_label_schema_build_date=unknown
+ARG org_opencord_vcs_commit_date=unknown
+ARG org_opencord_component_chameleon_version=unknown
+ARG org_opencord_component_chameleon_vcs_url=unknown
+ARG org_opencord_component_chameleon_vcs_ref=unknown
+ARG org_opencord_component_xos_version=unknown
+ARG org_opencord_component_xos_vcs_url=unknown
+ARG org_opencord_component_xos_vcs_ref=unknown
+
+LABEL org.label-schema.schema-version=$org_label_schema_schema_version \
+ org.label-schema.name=$org_label_schema_name \
+ org.label-schema.version=$org_label_schema_version \
+ org.label-schema.vcs-url=$org_label_schema_vcs_url \
+ org.label-schema.vcs-ref=$org_label_schema_vcs_ref \
+ org.label-schema.build-date=$org_label_schema_build_date \
+ org.opencord.vcs-commit-date=$org_opencord_vcs_commit_date \
+ org.opencord.component.chameleon.version=$org_opencord_component_chameleon_version \
+ org.opencord.component.chameleon.vcs-url=$org_opencord_component_chameleon_vcs_url \
+ org.opencord.component.chameleon.vcs-ref=$org_opencord_component_chameleon_vcs_ref \
+ org.opencord.component.xos.version=$org_opencord_component_xos_version \
+ 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 ["/usr/bin/python", "/opt/xos/synchronizers/testservice/testservice-synchronizer.py"]
+CMD ["/bin/bash", "-c", "echo test && python /opt/xos/synchronizers/testservice/testservice-synchronizer.py"]
diff --git a/testservice/README.md b/testservice/README.md
new file mode 100644
index 0000000..c74e09a
--- /dev/null
+++ b/testservice/README.md
@@ -0,0 +1 @@
+# Testservice Service
diff --git a/testservice/VERSION b/testservice/VERSION
new file mode 100644
index 0000000..8acdd82
--- /dev/null
+++ b/testservice/VERSION
@@ -0,0 +1 @@
+0.0.1
diff --git a/testservice/helm-charts/testservice-devel.yaml b/testservice/helm-charts/testservice-devel.yaml
new file mode 100644
index 0000000..3f487a9
--- /dev/null
+++ b/testservice/helm-charts/testservice-devel.yaml
@@ -0,0 +1,16 @@
+# 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.
+
+imagePullPolicy: 'IfNotPresent'
+testservice_synchronizerImage: 'xosproject/testservice-synchronizer:candidate'
diff --git a/testservice/helm-charts/testservice/Chart.yaml b/testservice/helm-charts/testservice/Chart.yaml
new file mode 100644
index 0000000..3076353
--- /dev/null
+++ b/testservice/helm-charts/testservice/Chart.yaml
@@ -0,0 +1,17 @@
+---
+# Copyright 2018-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.
+
+name: testservice
+version: 0.0.1
diff --git a/testservice/helm-charts/testservice/templates/_helpers.tpl b/testservice/helm-charts/testservice/templates/_helpers.tpl
new file mode 100644
index 0000000..3e260de
--- /dev/null
+++ b/testservice/helm-charts/testservice/templates/_helpers.tpl
@@ -0,0 +1,71 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Copyright 2018-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.
+*/}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "testservice.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "testservice.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "testservice.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{- define "testservice.serviceConfig" -}}
+name: testservice
+accessor:
+ username: {{ .Values.xosAdminUser | quote }}
+ password: {{ .Values.xosAdminPassword | quote }}
+ endpoint: xos-core:50051
+logging:
+ version: 1
+ handlers:
+ console:
+ class: logging.StreamHandler
+ file:
+ class: logging.handlers.RotatingFileHandler
+ filename: /var/log/xos.log
+ maxBytes: 10485760
+ backupCount: 5
+ loggers:
+ 'multistructlog':
+ handlers:
+ - console
+ - file
+ level: DEBUG
+{{- end -}}
diff --git a/testservice/helm-charts/testservice/templates/_tosca.tpl b/testservice/helm-charts/testservice/templates/_tosca.tpl
new file mode 100644
index 0000000..a33348b
--- /dev/null
+++ b/testservice/helm-charts/testservice/templates/_tosca.tpl
@@ -0,0 +1,30 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Copyright 2018-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.
+*/}}
+{{- define "testservice.serviceTosca" -}}
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: Set up testservice service
+imports:
+ - custom_types/testserviceservice.yaml
+
+topology_template:
+ node_templates:
+ service#testservice:
+ type: tosca.nodes.TestserviceService
+ properties:
+ name: testservice
+ kind: Testservice
+{{- end -}}
diff --git a/testservice/helm-charts/testservice/templates/configmap.yaml b/testservice/helm-charts/testservice/templates/configmap.yaml
new file mode 100644
index 0000000..26cca16
--- /dev/null
+++ b/testservice/helm-charts/testservice/templates/configmap.yaml
@@ -0,0 +1,22 @@
+---
+# Copyright 2018-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.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: testservice
+data:
+ serviceConfig: |
+{{ include "testservice.serviceConfig" . | indent 4 }}
diff --git a/testservice/helm-charts/testservice/templates/deployment.yaml b/testservice/helm-charts/testservice/templates/deployment.yaml
new file mode 100644
index 0000000..ad82915
--- /dev/null
+++ b/testservice/helm-charts/testservice/templates/deployment.yaml
@@ -0,0 +1,76 @@
+---
+# Copyright 2018-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.
+
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: {{ template "testservice.fullname" . }}
+ labels:
+ app: {{ template "testservice.name" . }}
+ chart: {{ template "testservice.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: {{ .Values.replicaCount }}
+ selector:
+ matchLabels:
+ app: {{ template "testservice.name" . }}
+ release: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "testservice.name" . }}
+ release: {{ .Release.Name }}
+ annotations:
+ checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
+ spec:
+ containers:
+ - name: {{ .Chart.Name }}
+ image: {{ tpl .Values.testservice_synchronizerImage . | quote }}
+ imagePullPolicy: {{ .Values.imagePullPolicy }}
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ volumeMounts:
+ - name: testservice-config
+ mountPath: /opt/xos/synchronizers/testservice/mounted_config.yaml
+ subPath: mounted_config.yaml
+ - name: certchain-volume
+ mountPath: /usr/local/share/ca-certificates/local_certs.crt
+ subPath: config/ca_cert_chain.pem
+ volumes:
+ - name: testservice-config
+ configMap:
+ name: testservice
+ items:
+ - key: serviceConfig
+ path: mounted_config.yaml
+ - name: certchain-volume
+ configMap:
+ name: ca-certificates
+ items:
+ - key: chain
+ path: config/ca_cert_chain.pem
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
diff --git a/testservice/helm-charts/testservice/values.yaml b/testservice/helm-charts/testservice/values.yaml
new file mode 100644
index 0000000..567f213
--- /dev/null
+++ b/testservice/helm-charts/testservice/values.yaml
@@ -0,0 +1,30 @@
+---
+# Copyright 2018-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.
+
+nameOverride: ""
+fullnameOverride: ""
+
+testservice_synchronizerImage: "xosproject/testservice-synchronizer:{{ .Chart.Version }}"
+
+imagePullPolicy: 'IfNotPresent'
+
+xosAdminUser: "admin@opencord.org"
+xosAdminPassword: "letmein"
+
+affinity: {}
+nodeSelector: {}
+replicaCount: 1
+resources: {}
+tolerations: []
diff --git a/testservice/scripts/cleanup.sh b/testservice/scripts/cleanup.sh
new file mode 100755
index 0000000..ad39431
--- /dev/null
+++ b/testservice/scripts/cleanup.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# 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.
+
+TOSCA_URL=http://`hostname`:30007
+RECIPE=../tosca/test_no_constraints.yaml
+curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X POST --data-binary @$RECIPE $TOSCA_URL/delete
diff --git a/testservice/scripts/cycle.sh b/testservice/scripts/cycle.sh
new file mode 100755
index 0000000..7426b42
--- /dev/null
+++ b/testservice/scripts/cycle.sh
@@ -0,0 +1,21 @@
+#! /bin/bash
+
+# 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.
+
+pushd ~/cord/orchestration/xos/testservice/scripts
+helm del --purge testservice
+./rebuild.sh
+./deploy.sh
+popd
diff --git a/testservice/scripts/deploy.sh b/testservice/scripts/deploy.sh
new file mode 100755
index 0000000..7909a3c
--- /dev/null
+++ b/testservice/scripts/deploy.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+
+# 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.
+
+pushd ~/cord/orchestration/xos/testservice/helm-charts
+#helm dep update testservice
+helm install testservice -n testservice -f testservice-devel.yaml
+popd
diff --git a/testservice/scripts/rebuild.sh b/testservice/scripts/rebuild.sh
new file mode 100755
index 0000000..6f6d38b
--- /dev/null
+++ b/testservice/scripts/rebuild.sh
@@ -0,0 +1,19 @@
+#! /bin/bash
+
+# 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.
+
+pushd ~/cord/orchestration/xos/testservice
+sudo docker build -t xosproject/testservice-synchronizer:candidate -f Dockerfile.synchronizer .
+popd
diff --git a/testservice/scripts/test_no_constraints.sh b/testservice/scripts/test_no_constraints.sh
new file mode 100755
index 0000000..dd58a85
--- /dev/null
+++ b/testservice/scripts/test_no_constraints.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# 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.
+
+set -e
+
+CHAMELEON_URL=http://`hostname`:30006/xosapi/v1/testservice/testserviceserviceinstances
+TOSCA_URL=http://`hostname`:30007
+TEST_POD=`kubectl get pods | grep -i testservice | cut -f 1 -d " "`
+RECIPE=../tosca/test_no_constraints.yaml
+
+curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X post --data-binary @$RECIPE $TOSCA_URL/run
+
+echo "done tosca"
+
+cat <<EOF > wait_for.txt
+TEST:SYNC_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-no-constraints' some_integer=0 some_other_integer=0
+TEST:POLICY_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-no-constraints' some_integer=0 some_other_integer=0
+EOF
+kubectl logs -f --since=30s $TEST_POD | ansi2txt | python wait_for_lines.py wait_for.txt
+
+python ./verify_model.py $CHAMELEON_URL name=test-no-constraints "updated>=0" "enacted>=@updated" "policed>=@updated"
diff --git a/testservice/scripts/test_policy_after_sync.sh b/testservice/scripts/test_policy_after_sync.sh
new file mode 100755
index 0000000..090b373
--- /dev/null
+++ b/testservice/scripts/test_policy_after_sync.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# 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.
+
+set -e
+
+CHAMELEON_URL=http://`hostname`:30006/xosapi/v1/testservice/testserviceserviceinstances
+TOSCA_URL=http://`hostname`:30007
+TEST_POD=`kubectl get pods | grep -i testservice | cut -f 1 -d " "`
+RECIPE=../tosca/test_policy_after_sync.yaml
+
+curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X post --data-binary @$RECIPE $TOSCA_URL/run
+
+echo "done tosca"
+
+cat <<EOF > wait_for.txt
+TEST:SYNC_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-policy-after-sync' some_integer=0 some_other_integer=0
+TEST:POLICY_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-policy-after-sync' some_integer=0 some_other_integer=0
+EOF
+kubectl logs -f --since=30s $TEST_POD | ansi2txt | python wait_for_lines.py wait_for.txt
+
+python ./verify_model.py $CHAMELEON_URL name=test-policy-after-sync "updated>=0" "enacted>=@updated" "policed>=@updated"
diff --git a/testservice/scripts/test_policy_after_sync_update.sh b/testservice/scripts/test_policy_after_sync_update.sh
new file mode 100755
index 0000000..b3cbbfa
--- /dev/null
+++ b/testservice/scripts/test_policy_after_sync_update.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# 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.
+
+set -e
+
+CHAMELEON_URL=http://`hostname`:30006/xosapi/v1/testservice/testserviceserviceinstances
+TOSCA_URL=http://`hostname`:30007
+TEST_POD=`kubectl get pods | grep -i testservice | cut -f 1 -d " "`
+RECIPE=../tosca/test_policy_after_sync_update.yaml
+
+curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X post --data-binary @$RECIPE $TOSCA_URL/run
+
+echo "done tosca"
+
+cat <<EOF > wait_for.txt
+TEST:SYNC_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-policy-after-sync-update' some_integer=0 some_other_integer=0
+TEST:SYNC_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-policy-after-sync-update' some_integer=0 some_other_integer=1
+TEST:POLICY_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-policy-after-sync-update' some_integer=0 some_other_integer=1
+EOF
+kubectl logs -f --since=30s $TEST_POD | ansi2txt | python wait_for_lines.py wait_for.txt
+
+python ./verify_model.py $CHAMELEON_URL name=test-policy-after-sync-update "updated>=0" "policed>=@updated" "enacted>=@policed" "some_other_integer=1"
diff --git a/testservice/scripts/test_sync_after_policy.sh b/testservice/scripts/test_sync_after_policy.sh
new file mode 100755
index 0000000..2daf7ef
--- /dev/null
+++ b/testservice/scripts/test_sync_after_policy.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# 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.
+
+set -e
+
+CHAMELEON_URL=http://`hostname`:30006/xosapi/v1/testservice/testserviceserviceinstances
+TOSCA_URL=http://`hostname`:30007
+TEST_POD=`kubectl get pods | grep -i testservice | cut -f 1 -d " "`
+RECIPE=../tosca/test_sync_after_policy.yaml
+
+curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X post --data-binary @$RECIPE $TOSCA_URL/run
+
+echo "done tosca"
+
+cat <<EOF > wait_for.txt
+TEST:SYNC_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-sync-after-policy' some_integer=0 some_other_integer=0
+TEST:POLICY_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-sync-after-policy' some_integer=0 some_other_integer=0
+EOF
+kubectl logs -f --since=30s $TEST_POD | ansi2txt | python wait_for_lines.py wait_for.txt
+
+python ./verify_model.py $CHAMELEON_URL name=test-sync-after-policy "updated>=0" "enacted>=@updated" "policed>=@updated"
diff --git a/testservice/scripts/test_sync_after_policy_update.sh b/testservice/scripts/test_sync_after_policy_update.sh
new file mode 100755
index 0000000..e7f2112
--- /dev/null
+++ b/testservice/scripts/test_sync_after_policy_update.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# 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.
+
+set -e
+
+CHAMELEON_URL=http://`hostname`:30006/xosapi/v1/testservice/testserviceserviceinstances
+TOSCA_URL=http://`hostname`:30007
+TEST_POD=`kubectl get pods | grep -i testservice | cut -f 1 -d " "`
+RECIPE=../tosca/test_sync_after_policy_update.yaml
+
+curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X post --data-binary @$RECIPE $TOSCA_URL/run
+
+echo "done tosca"
+
+cat <<EOF > wait_for.txt
+TEST:SYNC_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-sync-after-policy-update' some_integer=1 some_other_integer=0
+TEST:POLICY_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-sync-after-policy-update' some_integer=0 some_other_integer=0
+TEST:POLICY_DONE id=[0-9]+ model_class=TestserviceServiceInstance model_name=u'test-sync-after-policy-update' some_integer=1 some_other_integer=0
+EOF
+kubectl logs -f --since=30s $TEST_POD | ansi2txt | python wait_for_lines.py wait_for.txt
+
+python ./verify_model.py $CHAMELEON_URL name=test-sync-after-policy-update "updated>=0" "enacted>=@updated" "policed>=@enacted" "some_integer=1"
diff --git a/testservice/scripts/verify_model.py b/testservice/scripts/verify_model.py
new file mode 100755
index 0000000..a895940
--- /dev/null
+++ b/testservice/scripts/verify_model.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+# 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.
+
+from __future__ import absolute_import, print_function
+
+import requests
+import sys
+
+
+def matches(test, item):
+ if ">=" in test:
+ delim = ">="
+ elif ">" in test:
+ delim = ">"
+ elif "=" in test:
+ delim = "="
+ (name, value) = test.split(delim, 1)
+ if value.startswith("@"):
+ value = item[value[1:]]
+
+ if delim == ">":
+ if float(item[name]) > float(value):
+ return True
+
+ if delim == ">=":
+ if float(item[name]) >= float(value):
+ return True
+
+ if delim == "=":
+ if str(item[name]) == str(value):
+ return True
+ return False
+
+
+def main():
+ url = sys.argv[1]
+ tests = sys.argv[2:]
+
+ r = requests.get(url, auth=("admin@opencord.org", "letmein"))
+ for item in r.json()["items"]:
+ okay = True
+ for test in tests:
+ if not matches(test, item):
+ okay = False
+ if okay:
+ print("matched")
+ sys.exit(0)
+
+ print("Not Matched")
+ sys.exit(-1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/testservice/scripts/wait_for_lines.py b/testservice/scripts/wait_for_lines.py
new file mode 100755
index 0000000..089978f
--- /dev/null
+++ b/testservice/scripts/wait_for_lines.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# 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.
+
+from __future__ import absolute_import, print_function
+
+import re
+import signal
+import sys
+
+
+def handler(signum, frame):
+ print "Timed Out"
+ sys.exit(-1)
+
+
+signal.signal(signal.SIGALRM, handler)
+signal.alarm(60) # timeout in 60 seconds
+
+waitlines = open(sys.argv[1]).readlines()
+waitlines = [x.strip() for x in waitlines]
+
+while True:
+ if not waitlines:
+ print("all lines matched")
+ break
+ line = sys.stdin.readline()
+ line = line.strip()
+ for pattern in waitlines[:]:
+ if re.match(pattern, line):
+ print("matched", pattern)
+ waitlines.remove(pattern)
diff --git a/testservice/tosca/test_create_dup.yaml b/testservice/tosca/test_create_dup.yaml
new file mode 100644
index 0000000..7b3c7dd
--- /dev/null
+++ b/testservice/tosca/test_create_dup.yaml
@@ -0,0 +1,35 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: Set up testservice service
+imports:
+ - custom_types/testserviceservice.yaml
+ - custom_types/testserviceserviceinstance.yaml
+
+topology_template:
+ node_templates:
+ service#testservice:
+ type: tosca.nodes.TestserviceService
+ properties:
+ name: testservice
+ kind: generic
+
+ serviceinstance:
+ type: tosca.nodes.TestserviceServiceInstance
+ properties:
+ name: test-create-dup
+ create_duplicate: true
+ some_integer: 1234
+ some_other_integer: 5678
diff --git a/testservice/tosca/test_no_constraints.yaml b/testservice/tosca/test_no_constraints.yaml
new file mode 100644
index 0000000..abaf163
--- /dev/null
+++ b/testservice/tosca/test_no_constraints.yaml
@@ -0,0 +1,32 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: Set up testservice service
+imports:
+ - custom_types/testserviceservice.yaml
+ - custom_types/testserviceserviceinstance.yaml
+
+topology_template:
+ node_templates:
+ service#testservice:
+ type: tosca.nodes.TestserviceService
+ properties:
+ name: testservice
+ kind: generic
+
+ serviceinstance:
+ type: tosca.nodes.TestserviceServiceInstance
+ properties:
+ name: test-no-constraints
diff --git a/testservice/tosca/test_policy_after_sync.yaml b/testservice/tosca/test_policy_after_sync.yaml
new file mode 100644
index 0000000..f697190
--- /dev/null
+++ b/testservice/tosca/test_policy_after_sync.yaml
@@ -0,0 +1,33 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: Set up testservice service
+imports:
+ - custom_types/testserviceservice.yaml
+ - custom_types/testserviceserviceinstance.yaml
+
+topology_template:
+ node_templates:
+ service#testservice:
+ type: tosca.nodes.TestserviceService
+ properties:
+ name: testservice
+ kind: generic
+
+ serviceinstance:
+ type: tosca.nodes.TestserviceServiceInstance
+ properties:
+ name: test-policy-after-sync
+ policy_after_sync: true
diff --git a/testservice/tosca/test_policy_after_sync_update.yaml b/testservice/tosca/test_policy_after_sync_update.yaml
new file mode 100644
index 0000000..3101d69
--- /dev/null
+++ b/testservice/tosca/test_policy_after_sync_update.yaml
@@ -0,0 +1,34 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: Set up testservice service
+imports:
+ - custom_types/testserviceservice.yaml
+ - custom_types/testserviceserviceinstance.yaml
+
+topology_template:
+ node_templates:
+ service#testservice:
+ type: tosca.nodes.TestserviceService
+ properties:
+ name: testservice
+ kind: generic
+
+ serviceinstance:
+ type: tosca.nodes.TestserviceServiceInstance
+ properties:
+ name: test-policy-after-sync-update
+ policy_after_sync: true
+ update_during_policy: true
diff --git a/testservice/tosca/test_sync_after_policy.yaml b/testservice/tosca/test_sync_after_policy.yaml
new file mode 100644
index 0000000..4a219c0
--- /dev/null
+++ b/testservice/tosca/test_sync_after_policy.yaml
@@ -0,0 +1,33 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: Set up testservice service
+imports:
+ - custom_types/testserviceservice.yaml
+ - custom_types/testserviceserviceinstance.yaml
+
+topology_template:
+ node_templates:
+ service#testservice:
+ type: tosca.nodes.TestserviceService
+ properties:
+ name: testservice
+ kind: generic
+
+ serviceinstance:
+ type: tosca.nodes.TestserviceServiceInstance
+ properties:
+ name: test-sync-after-policy
+ sync_after_policy: true
diff --git a/testservice/tosca/test_sync_after_policy_update.yaml b/testservice/tosca/test_sync_after_policy_update.yaml
new file mode 100644
index 0000000..c2b3f54
--- /dev/null
+++ b/testservice/tosca/test_sync_after_policy_update.yaml
@@ -0,0 +1,34 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: Set up testservice service
+imports:
+ - custom_types/testserviceservice.yaml
+ - custom_types/testserviceserviceinstance.yaml
+
+topology_template:
+ node_templates:
+ service#testservice:
+ type: tosca.nodes.TestserviceService
+ properties:
+ name: testservice
+ kind: generic
+
+ serviceinstance:
+ type: tosca.nodes.TestserviceServiceInstance
+ properties:
+ name: test-sync-after-policy-update
+ sync_after_policy: true
+ update_during_sync: true
diff --git a/testservice/xos/synchronizer/config.yaml b/testservice/xos/synchronizer/config.yaml
new file mode 100644
index 0000000..a1399b5
--- /dev/null
+++ b/testservice/xos/synchronizer/config.yaml
@@ -0,0 +1,23 @@
+# Copyright 2018-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.
+
+name: testservice
+core_version: ">=2.2.1"
+required_models:
+ - TestserviceService
+ - TestserviceServiceInstance
+dependency_graph: "/opt/xos/synchronizers/testservice/model-deps"
+model_policies_dir: "/opt/xos/synchronizers/testservice/model_policies"
+models_dir: "/opt/xos/synchronizers/testservice/models"
+steps_dir: "/opt/xos/synchronizers/testservice/steps"
diff --git a/testservice/xos/synchronizer/helpers.py b/testservice/xos/synchronizer/helpers.py
new file mode 100644
index 0000000..20f4ca8
--- /dev/null
+++ b/testservice/xos/synchronizer/helpers.py
@@ -0,0 +1,37 @@
+# Copyright 2018-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.
+
+from __future__ import absolute_import
+
+import os
+import time
+
+
+def get_signal_fn(model, name):
+ return os.path.join("/tmp", "signal_%s_%s_%s" % (model.model_name, model.id, name))
+
+
+def put_signal(log, model, name):
+ open(get_signal_fn(model, name), "a").write(str(time.time()) + "\n")
+
+
+def wait_for_signal(log, model, name, timeout=30):
+ fn = get_signal_fn(model, name)
+ count = 0
+ while not os.path.exists(fn):
+ count += 1
+ if (count > 30):
+ log.error("TEST:FAIL - Timeout waiting for %s" % fn, model=model)
+ raise Exception("timeout")
+ time.sleep(1)
diff --git a/testservice/xos/synchronizer/migrations/0001_initial.py b/testservice/xos/synchronizer/migrations/0001_initial.py
new file mode 100644
index 0000000..e4a87ae
--- /dev/null
+++ b/testservice/xos/synchronizer/migrations/0001_initial.py
@@ -0,0 +1,140 @@
+# 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.
+
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-03-20 23:53
+from __future__ import unicode_literals
+
+import core.models.xosbase_header
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('core', '0009_auto_20190313_1442'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TestserviceDuplicateServiceInstance',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created', models.DateTimeField(auto_now_add=True, help_text=b'Time this model was created')),
+ ('updated', models.DateTimeField(default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer')),
+ ('enacted', models.DateTimeField(blank=True, default=None, help_text=b'When synced, set to the timestamp of the data that was synced', null=True)),
+ ('policed', models.DateTimeField(blank=True, default=None, help_text=b'When policed, set to the timestamp of the data that was policed', null=True)),
+ ('backend_register', models.CharField(blank=True, default=b'{}', max_length=1024, null=True)),
+ ('backend_need_delete', models.BooleanField(default=False)),
+ ('backend_need_reap', models.BooleanField(default=False)),
+ ('backend_status', models.CharField(default=b'Provisioning in progress', max_length=1024)),
+ ('backend_code', models.IntegerField(default=0)),
+ ('deleted', models.BooleanField(default=False)),
+ ('write_protect', models.BooleanField(default=False)),
+ ('lazy_blocked', models.BooleanField(default=False)),
+ ('no_sync', models.BooleanField(default=False)),
+ ('no_policy', models.BooleanField(default=False)),
+ ('policy_status', models.CharField(blank=True, default=b'Policy in process', max_length=1024, null=True)),
+ ('policy_code', models.IntegerField(blank=True, default=0, null=True)),
+ ('leaf_model_name', models.CharField(help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024)),
+ ('backend_need_delete_policy', models.BooleanField(default=False, help_text=b'True if delete model_policy must be run before object can be reaped')),
+ ('xos_managed', models.BooleanField(default=True, help_text=b'True if xos is responsible for creating/deleting this object')),
+ ('backend_handle', models.CharField(blank=True, help_text=b'Handle used by the backend to track this object', max_length=1024, null=True)),
+ ('changed_by_step', models.DateTimeField(blank=True, default=None, help_text=b'Time this model was changed by a sync step', null=True)),
+ ('changed_by_policy', models.DateTimeField(blank=True, default=None, help_text=b'Time this model was changed by a model policy', null=True)),
+ ('name', models.CharField(help_text=b'Named copied From serviceInstance', max_length=256)),
+ ('some_integer', models.IntegerField(default=0)),
+ ('some_other_integer', models.IntegerField(default=0)),
+ ('optional_string', models.TextField(blank=True, null=True)),
+ ('optional_string_with_default', models.TextField(blank=True, default=b'some_default', null=True)),
+ ('optional_string_with_choices', models.TextField(blank=True, choices=[(b'one', b'one'), (b'two', b'two')], null=True)),
+ ('optional_string_max_length', models.CharField(blank=True, max_length=32, null=True)),
+ ('optional_string_stripped', core.models.xosbase_header.StrippedCharField(blank=True, max_length=32, null=True)),
+ ('optional_string_date', models.DateTimeField(blank=True, null=True)),
+ ('optional_string_ip', models.GenericIPAddressField(blank=True, null=True)),
+ ('optional_string_indexed', models.TextField(blank=True, db_index=True, null=True)),
+ ('required_string', models.TextField(default=b'some_default')),
+ ('required_bool_default_false', models.BooleanField(default=False)),
+ ('required_bool_default_true', models.BooleanField(default=True)),
+ ('optional_int', models.IntegerField(blank=True, null=True)),
+ ('optional_int_with_default', models.IntegerField(blank=True, default=123, null=True)),
+ ('optional_int_with_min', models.IntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(100)])),
+ ('optional_int_with_max', models.IntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(199)])),
+ ('required_int_with_default', models.IntegerField(default=456)),
+ ('optional_float', models.FloatField(blank=True, null=True)),
+ ('optional_float_with_default', models.FloatField(blank=True, default=3.3, null=True)),
+ ],
+ options={
+ 'verbose_name': 'Testservice Duplicate Service Instance',
+ },
+ bases=(models.Model, core.models.xosbase_header.PlModelMixIn),
+ ),
+ migrations.CreateModel(
+ name='TestserviceService',
+ fields=[
+ ('service_decl_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.Service_decl')),
+ ],
+ options={
+ 'verbose_name': 'Testservice Service',
+ },
+ bases=('core.service',),
+ ),
+ migrations.CreateModel(
+ name='TestserviceServiceInstance',
+ fields=[
+ ('serviceinstance_decl_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.ServiceInstance_decl')),
+ ('sync_after_policy', models.BooleanField(default=False)),
+ ('sync_during_policy', models.BooleanField(default=False)),
+ ('policy_after_sync', models.BooleanField(default=False)),
+ ('policy_during_sync', models.BooleanField(default=False)),
+ ('update_during_sync', models.BooleanField(default=False)),
+ ('update_during_policy', models.BooleanField(default=False)),
+ ('create_duplicate', models.BooleanField(default=False)),
+ ('some_integer', models.IntegerField(default=0)),
+ ('some_other_integer', models.IntegerField(default=0)),
+ ('optional_string', models.TextField(blank=True, null=True)),
+ ('optional_string_with_default', models.TextField(blank=True, default=b'some_default', null=True)),
+ ('optional_string_with_choices', models.TextField(blank=True, choices=[(b'one', b'one'), (b'two', b'two')], null=True)),
+ ('optional_string_max_length', models.CharField(blank=True, max_length=32, null=True)),
+ ('optional_string_stripped', core.models.xosbase_header.StrippedCharField(blank=True, max_length=32, null=True)),
+ ('optional_string_date', models.DateTimeField(blank=True, null=True)),
+ ('optional_string_ip', models.GenericIPAddressField(blank=True, null=True)),
+ ('optional_string_indexed', models.TextField(blank=True, db_index=True, null=True)),
+ ('required_string', models.TextField(default=b'some_default')),
+ ('required_bool_default_false', models.BooleanField(default=False)),
+ ('required_bool_default_true', models.BooleanField(default=True)),
+ ('optional_int', models.IntegerField(blank=True, null=True)),
+ ('optional_int_with_default', models.IntegerField(blank=True, default=123, null=True)),
+ ('optional_int_with_min', models.IntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(100)])),
+ ('optional_int_with_max', models.IntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(199)])),
+ ('required_int_with_default', models.IntegerField(default=456)),
+ ('optional_float', models.FloatField(blank=True, null=True)),
+ ('optional_float_with_default', models.FloatField(blank=True, default=3.3, null=True)),
+ ],
+ options={
+ 'verbose_name': 'Testservice Service Instance',
+ },
+ bases=('core.serviceinstance',),
+ ),
+ migrations.AddField(
+ model_name='testserviceduplicateserviceinstance',
+ name='serviceinstance',
+ field=models.ForeignKey(help_text=b'Link to the ServiceInstance this was duplicated from', on_delete=django.db.models.deletion.CASCADE, related_name='duplicates', to='testservice.TestserviceServiceInstance'),
+ ),
+ ]
diff --git a/testservice/xos/synchronizer/migrations/__init__.py b/testservice/xos/synchronizer/migrations/__init__.py
new file mode 100644
index 0000000..72d9768
--- /dev/null
+++ b/testservice/xos/synchronizer/migrations/__init__.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+
+# 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.
diff --git a/testservice/xos/synchronizer/model-deps b/testservice/xos/synchronizer/model-deps
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/testservice/xos/synchronizer/model-deps
@@ -0,0 +1 @@
+{}
diff --git a/testservice/xos/synchronizer/model_policies/model_policy_testservice_serviceinstance.py b/testservice/xos/synchronizer/model_policies/model_policy_testservice_serviceinstance.py
new file mode 100644
index 0000000..71dfab7
--- /dev/null
+++ b/testservice/xos/synchronizer/model_policies/model_policy_testservice_serviceinstance.py
@@ -0,0 +1,107 @@
+# Copyright 2018-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.
+
+from __future__ import absolute_import
+
+import os
+import sys
+
+from xossynchronizer.model_policies.policy import Policy
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from helpers import put_signal, wait_for_signal
+
+DUP_FIELD_NAMES = ["name",
+ "some_integer",
+ "some_other_integer",
+ "optional_string",
+ "optional_string_with_default",
+ "optional_string_with_choices",
+ "optional_string_max_length",
+ "optional_string_stripped",
+ "optional_string_date",
+ "optional_string_ip",
+ "optional_string_indexed",
+ "required_string",
+ "required_bool_default_false",
+ "required_bool_default_true",
+ "optional_int",
+ "optional_int_with_default",
+ "optional_int_with_min",
+ "optional_int_with_max",
+ "required_int_with_default",
+ "optional_float",
+ "optional_float_with_default"]
+
+
+class TestserviceServiceInstancePolicy(Policy):
+ """
+ TestserviceServiceInstancePolicy
+ Implements model policy for TestserviceInstance
+ """
+
+ model_name = "TestserviceServiceInstance"
+
+ def handle_create(self, model):
+ self.logger.debug(
+ "MODEL_POLICY: enter handle_create for TestserviceServiceInstance %s" %
+ model.id)
+ self.handle_update(model)
+ # TODO: Implement creation policy, if it differs from update policy
+
+ def handle_update(self, model):
+ self.logger.info("TEST:POLICY_UPDATE_START", model_class=model.model_name, model_name=model.name, id=model.id,
+ some_integer=model.some_integer, some_other_integer=model.some_other_integer)
+
+ put_signal(self.logger, model, "policy_start")
+
+ if model.policy_after_sync or model.sync_during_policy:
+ wait_for_signal(self.logger, model, "sync_done")
+
+ if model.policy_during_sync:
+ wait_for_signal(self.logger, model, "sync_start")
+
+ if model.update_during_policy:
+ model.some_other_integer = model.some_other_integer + 1
+ model.save(update_fields=["some_other_integer"])
+
+ if model.create_duplicate:
+ dups = self.model_accessor.TestserviceDuplicateServiceInstance.objects.filter(serviceinstance_id=model.id)
+ if dups:
+ dup = dups[0]
+ else:
+ dup = self.model_accessor.TestserviceDuplicateServiceInstance(serviceinstance=model)
+
+ for fieldname in DUP_FIELD_NAMES:
+ value = getattr(model, fieldname)
+ if value is not None:
+ setattr(dup, fieldname, value)
+
+ dup.save()
+ else:
+ # See if one was previously created when create_duplicate was previously true. If so, delete it
+ dups = self.model_accessor.TestserviceDuplicateServiceInstance.objects.filter(serviceinstance_id=model.id)
+ for dup in dups:
+ dup.delete()
+
+ def after_policy_save(self, model):
+ put_signal(self.logger, model, "policy_done")
+ self.logger.info("TEST:POLICY_DONE", model_class=model.model_name, model_name=model.name, id=model.id,
+ some_integer=model.some_integer, some_other_integer=model.some_other_integer)
+
+ def handle_delete(self, model):
+ self.logger.debug(
+ "MODEL_POLICY: enter handle_delete for TestserviceServiceInstance %s" %
+ model.id)
+ # TODO: Implement delete policy
diff --git a/testservice/xos/synchronizer/model_policies/test_model_policy_testservice_serviceinstance.py b/testservice/xos/synchronizer/model_policies/test_model_policy_testservice_serviceinstance.py
new file mode 100644
index 0000000..e2a7005
--- /dev/null
+++ b/testservice/xos/synchronizer/model_policies/test_model_policy_testservice_serviceinstance.py
@@ -0,0 +1,98 @@
+# Copyright 2018-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.
+
+from __future__ import absolute_import
+
+import imp
+import os
+import sys
+import unittest
+from mock import Mock
+
+test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+
+
+class TestModelPolicyTestserviceServiceInstance(unittest.TestCase):
+
+ def setUp(self):
+
+ self.sys_path_save = sys.path
+
+ # Setting up the config module
+ from xosconfig import Config
+ config = os.path.join(test_path, "../test_config.yaml")
+ Config.clear()
+ Config.init(config, "synchronizer-config-schema.yaml")
+ # END Setting up the config module
+
+ from xossynchronizer.mock_modelaccessor_build import build_mock_modelaccessor
+
+ # Can't use mock_modelaccessor_config because we're not in the xos-services directory, so do it
+ # the long way...
+ xos_dir = os.path.join(test_path, "../../../../xos")
+ services_dir = os.path.join(test_path, "../../../..")
+ service_xprotos = [os.path.join(test_path, "../models/testservice.xproto")]
+ build_mock_modelaccessor(None, xos_dir, services_dir, service_xprotos)
+# mock_modelaccessor_config(test_path, [("testservice", "testservice.xproto")])
+
+ import xossynchronizer.modelaccessor
+ import mock_modelaccessor
+ imp.reload(mock_modelaccessor) # in case nose2 loaded it in a previous test
+ imp.reload(xossynchronizer.modelaccessor) # in case nose2 loaded it in a previous test
+
+ from model_policy_testservice_serviceinstance import TestserviceServiceInstancePolicy
+ from xossynchronizer.modelaccessor import model_accessor
+
+ self.model_accessor = model_accessor
+
+ # import all class names to globals
+ for (k, v) in model_accessor.all_model_classes.items():
+ globals()[k] = v
+
+ # Some of the functions we call have side-effects, reset the world.
+ model_accessor.reset_all_object_stores()
+
+ self.policy = TestserviceServiceInstancePolicy(self.model_accessor)
+ self.si = Mock(sync_after_policy=False,
+ sync_during_policy=False,
+ policy_after_sync=False,
+ policy_during_sync=False,
+ update_during_sync=False,
+ update_during_policy=False,
+ create_duplicate=False)
+
+ def tearDown(self):
+ sys.path = self.sys_path_save
+ self.si = None
+
+# def test_not_synced(self):
+# self.si.valid = "awaiting"
+# self.si.updated = 2
+# self.si.enacted = 1
+# self.si.backend_code = 0
+#
+# with self.assertRaises(Exception) as e:
+# self.policy.handle_update(self.si)
+#
+# self.assertIn("has not been synced yet", e.exception.message)
+
+ def test_skip_update(self):
+ self.si.valid = "awaiting"
+ self.si.backend_code = 1
+
+ self.policy.handle_update(self.si)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/testservice/xos/synchronizer/models/testservice.xproto b/testservice/xos/synchronizer/models/testservice.xproto
new file mode 100644
index 0000000..a352ee3
--- /dev/null
+++ b/testservice/xos/synchronizer/models/testservice.xproto
@@ -0,0 +1,96 @@
+option app_label = "testservice";
+option name = "testservice";
+
+message TestserviceService (Service){
+ option verbose_name = "Testservice Service";
+}
+
+message TestserviceServiceInstance (ServiceInstance){
+ option owner_class_name = "TestserviceService";
+ option verbose_name = "Testservice Service Instance";
+
+ required bool sync_after_policy = 1 [default = False];
+ required bool sync_during_policy = 2 [default = False];
+ required bool policy_after_sync = 3 [default = False];
+ required bool policy_during_sync = 4 [default = False];
+
+ required bool update_during_sync = 5 [default = False];
+ required bool update_during_policy = 6 [default = False];
+
+ required bool create_duplicate = 9 [default = False];
+
+ // used by the policy/syncstep ordering tests
+ required int32 some_integer = 7 [default = 0];
+ required int32 some_other_integer = 8 [default = 0];
+
+ // optional strings
+ optional string optional_string = 20 [];
+ optional string optional_string_with_default = 21 [default="some_default"];
+ optional string optional_string_with_choices = 22 [choices="(('one', 'one'), ('two', 'two'))"];
+ optional string optional_string_max_length = 23 [max_length=32];
+ optional string optional_string_stripped = 24 [content_type="stripped", max_length=32];
+ optional string optional_string_date = 25 [content_type = "date"];
+ optional string optional_string_ip = 26 [content_type = "ip"];
+ optional string optional_string_indexed = 27 [db_index = True];
+
+ // required strings
+ required string required_string = 30 [default="some_default"];
+
+ // booleans
+ required bool required_bool_default_false = 40 [default=False];
+ required bool required_bool_default_true = 41 [default=True];
+
+ // integers
+ optional int32 optional_int = 50 [];
+ optional int32 optional_int_with_default = 51 [default=123];
+ optional int32 optional_int_with_min = 52 [min_value=100];
+ optional int32 optional_int_with_max = 53 [max_value=199];
+ required int32 required_int_with_default = 54 [default=456];
+
+ // floats
+ optional float optional_float = 60 [];
+ optional float optional_float_with_default = 61 [default=3.3];
+}
+
+message TestserviceDuplicateServiceInstance (XOSBase){
+ option verbose_name = "Testservice Duplicate Service Instance";
+
+ required manytoone serviceinstance->TestserviceServiceInstance:duplicates = 1:1001 [
+ help_text = "Link to the ServiceInstance this was duplicated from"];
+
+ required string name = 2 [
+ help_text = "Named copied From serviceInstance",
+ max_length = 256];
+
+ // used by the policy/syncstep ordering tests
+ required int32 some_integer = 7 [default = 0];
+ required int32 some_other_integer = 8 [default = 0];
+
+ // optional strings
+ optional string optional_string = 20 [];
+ optional string optional_string_with_default = 21 [default="some_default"];
+ optional string optional_string_with_choices = 22 [choices="(('one', 'one'), ('two', 'two'))"];
+ optional string optional_string_max_length = 23 [max_length=32];
+ optional string optional_string_stripped = 24 [content_type="stripped", max_length=32];
+ optional string optional_string_date = 25 [content_type = "date"];
+ optional string optional_string_ip = 26 [content_type = "ip"];
+ optional string optional_string_indexed = 27 [db_index = True];
+
+ // required strings
+ required string required_string = 30 [default="some_default"];
+
+ // booleans
+ required bool required_bool_default_false = 40 [default=False];
+ required bool required_bool_default_true = 41 [default=True];
+
+ // integers
+ optional int32 optional_int = 50 [];
+ optional int32 optional_int_with_default = 51 [default=123];
+ optional int32 optional_int_with_min = 52 [min_value=100];
+ optional int32 optional_int_with_max = 53 [max_value=199];
+ required int32 required_int_with_default = 54 [default=456];
+
+ // floats
+ optional float optional_float = 60 [];
+ optional float optional_float_with_default = 61 [default=3.3];
+}
diff --git a/testservice/xos/synchronizer/steps/sync_testservice_serviceinstance.py b/testservice/xos/synchronizer/steps/sync_testservice_serviceinstance.py
new file mode 100644
index 0000000..5c4b11a
--- /dev/null
+++ b/testservice/xos/synchronizer/steps/sync_testservice_serviceinstance.py
@@ -0,0 +1,73 @@
+# Copyright 2018-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.
+
+from __future__ import absolute_import
+
+import os
+import sys
+from xossynchronizer.steps.syncstep import SyncStep
+from xossynchronizer.modelaccessor import TestserviceServiceInstance
+
+from xosconfig import Config
+from multistructlog import create_logger
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from helpers import put_signal, wait_for_signal
+
+log = create_logger(Config().get('logging'))
+
+
+class SyncTestserviceServiceInstance(SyncStep):
+ """
+ SyncTestserviceServiceInstance
+ Implements sync step for syncing Testservice Services
+ """
+
+ provides = [TestserviceServiceInstance]
+ observes = [TestserviceServiceInstance]
+ requested_interval = 0
+
+ def sync_record(self, model):
+ log.info("TEST:SYNC_START",
+ model_class=model.model_name,
+ model_name=model.name,
+ id=model.id,
+ some_integer=model.some_integer,
+ some_other_integer=model.some_other_integer)
+
+ put_signal(log, model, "sync_start")
+
+ if model.sync_after_policy or model.policy_during_sync:
+ wait_for_signal(log, model, "policy_done")
+
+ if model.sync_during_policy:
+ wait_for_signal(log, model, "policy_start")
+
+ if model.update_during_sync:
+ model.some_integer = model.some_integer + 1
+ model.save(update_fields=["some_integer"])
+
+ def after_sync_save(self, model):
+ put_signal(log, model, "sync_done")
+ log.info("TEST:SYNC_DONE",
+ model_class=model.model_name,
+ model_name=model.name,
+ id=model.id,
+ some_integer=model.some_integer,
+ some_other_integer=model.some_other_integer)
+
+ def delete_record(self, model):
+ log.info("Deleting TestserviceServiceInstance",
+ object=str(model))
+ # TODO: Implement delete step
diff --git a/testservice/xos/synchronizer/steps/test_sync_testservice_serviceinstance.py b/testservice/xos/synchronizer/steps/test_sync_testservice_serviceinstance.py
new file mode 100644
index 0000000..c9e8af9
--- /dev/null
+++ b/testservice/xos/synchronizer/steps/test_sync_testservice_serviceinstance.py
@@ -0,0 +1,92 @@
+# Copyright 2018-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.
+
+from __future__ import absolute_import
+
+import imp
+import os
+import sys
+import unittest
+from mock import Mock
+
+test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+
+
+class TestSyncTestserviceServiceInstance(unittest.TestCase):
+
+ def setUp(self):
+
+ self.sys_path_save = sys.path
+
+ # Setting up the config module
+ from xosconfig import Config
+ config = os.path.join(test_path, "../test_config.yaml")
+ Config.clear()
+ Config.init(config, "synchronizer-config-schema.yaml")
+ # END Setting up the config module
+
+ from xossynchronizer.mock_modelaccessor_build import build_mock_modelaccessor
+
+ # Can't use mock_modelaccessor_config because we're not in the xos-services directory, so do it
+ # the long way...
+ xos_dir = os.path.join(test_path, "../../../../xos")
+ services_dir = os.path.join(test_path, "../../../..")
+ service_xprotos = [os.path.join(test_path, "../models/testservice.xproto")]
+ build_mock_modelaccessor(None, xos_dir, services_dir, service_xprotos)
+
+ import xossynchronizer.modelaccessor
+ import mock_modelaccessor
+ imp.reload(mock_modelaccessor) # in case nose2 loaded it in a previous test
+ imp.reload(xossynchronizer.modelaccessor) # in case nose2 loaded it in a previous test
+
+ from sync_testservice_serviceinstance import SyncTestserviceServiceInstance
+ from xossynchronizer.modelaccessor import model_accessor
+
+ self.model_accessor = model_accessor
+
+ # import all class names to globals
+ for (k, v) in model_accessor.all_model_classes.items():
+ globals()[k] = v
+
+ self.sync_step = SyncTestserviceServiceInstance
+
+ # TODO: Use modelaccessor instead
+ # create a mock instance instance
+ self.model = Mock(name="Example",
+ some_integer=0,
+ sync_after_policy=False,
+ sync_during_policy=False,
+ policy_after_sync=False,
+ policy_during_sync=False,
+ update_during_sync=False,
+ update_during_policy=False,
+ create_duplicate=False)
+
+ def tearDown(self):
+ self.model = None
+ sys.path = self.sys_path_save
+
+ # TODO - The following two tests are very simple, replace with more meaningful ones
+
+ def test_save(self):
+ # Tests that the model can be saved
+
+ self.model.name = "testservice_test"
+ self.model.update_during_sync = True
+ self.sync_step(model_accessor=self.model_accessor).sync_record(self.model)
+ self.model.save.assert_called()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/testservice/xos/synchronizer/test_config.yaml b/testservice/xos/synchronizer/test_config.yaml
new file mode 100644
index 0000000..f2f0715
--- /dev/null
+++ b/testservice/xos/synchronizer/test_config.yaml
@@ -0,0 +1,28 @@
+# Copyright 2018-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.
+
+name: testservice-testconfig
+accessor:
+ username: xosadmin@opencord.org
+ password: "sample"
+ kind: "testframework"
+logging:
+ version: 1
+ handlers:
+ console:
+ class: logging.StreamHandler
+ loggers:
+ 'multistructlog':
+ handlers:
+ - console
diff --git a/testservice/xos/synchronizer/testservice-synchronizer.py b/testservice/xos/synchronizer/testservice-synchronizer.py
new file mode 100644
index 0000000..86313be
--- /dev/null
+++ b/testservice/xos/synchronizer/testservice-synchronizer.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+# Copyright 2018-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.
+
+"""
+testservice-synchronizer.py
+This is the main entrypoint for the synchronizer. It loads the config file, and
+then starts the synchronizer.
+"""
+
+from __future__ import absolute_import
+
+import os
+from xossynchronizer import Synchronizer
+from xosconfig import Config
+
+base_config_file = os.path.abspath(os.path.dirname(
+ os.path.realpath(__file__)) + '/config.yaml')
+mounted_config_file = os.path.abspath(os.path.dirname(
+ os.path.realpath(__file__)) + '/mounted_config.yaml')
+
+if os.path.isfile(mounted_config_file):
+ Config.init(base_config_file, 'synchronizer-config-schema.yaml',
+ mounted_config_file)
+else:
+ Config.init(base_config_file, 'synchronizer-config-schema.yaml')
+
+Synchronizer().run()
diff --git a/testservice/xos/unittest.cfg b/testservice/xos/unittest.cfg
new file mode 100644
index 0000000..49cc8fd
--- /dev/null
+++ b/testservice/xos/unittest.cfg
@@ -0,0 +1,7 @@
+[unittest]
+plugins=nose2.plugins.junitxml
+code-directories=synchronizer
+ model_policies
+ steps
+ pull_steps
+ event_steps