RIFT OSM R1 Initial Submission
Signed-off-by: Jeremy Mordkoff <jeremy.mordkoff@riftio.com>
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..ff329dd
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,35 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+# Author(s): Anil Gunturu
+# Creation Date: 03/26/2014
+#
+
+cmake_minimum_required(VERSION 2.8)
+
+set(PKG_NAME rwmano_examples)
+set(PKG_VERSION 1.0)
+set(PKG_RELEASE 1)
+set(PKG_LONG_NAME ${PKG_NAME}-${PKG_VERSION})
+
+
+##
+# Include the subdirs
+##
+set(subdirs
+ ping_pong_ns
+ )
+
+rift_add_subdirs(SUBDIR_LIST ${subdirs})
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
index 0000000..2b691a8
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,36 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+# Author(s): Tim Mortsolf
+# Creation Date: 11/25/2013
+#
+
+##
+# Define a Makefile function: find_upwards(filename)
+#
+# Searches for a file of the given name in the directory ., .., ../.., ../../.., etc.,
+# until the file is found or the root directory is reached
+##
+find_upward = $(word 1, $(shell while [ `pwd` != / ] ; do find `pwd` -maxdepth 1 -name $1 ; cd .. ; done))
+
+##
+# Call find_upward("Makefile.top") to find the nearest upwards adjacent Makefile.top
+##
+makefile.top := $(call find_upward, "Makefile.top")
+
+##
+# If Makefile.top was found, then include it
+##
+include $(makefile.top)
diff --git a/examples/ping_pong_ns/CMakeLists.txt b/examples/ping_pong_ns/CMakeLists.txt
new file mode 100644
index 0000000..9667465
--- /dev/null
+++ b/examples/ping_pong_ns/CMakeLists.txt
@@ -0,0 +1,80 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+# Author(s): Anil Gunturu
+# Creation Date: 03/26/2014
+#
+
+cmake_minimum_required(VERSION 2.8)
+
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/generate_packages.sh.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generate_packages.sh
+ ESCAPE_QUOTES @ONLY
+ )
+
+set(PACKAGE_OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/ping_pong_nsd.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/ping_vnfd.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/pong_vnfd.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/ping_pong_nsd_aws.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/ping_vnfd_aws.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/pong_vnfd_aws.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/ping_pong_nsd_with_epa.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/ping_vnfd_with_epa.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/pong_vnfd_with_epa.tar.gz)
+
+add_custom_command(
+ OUTPUT ${PACKAGE_OUTPUT}
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/generate_packages.sh
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ping_pong_nsd.py
+ )
+
+add_custom_target(ping_pong_pkg_gen ALL
+ DEPENDS mano_yang ${PACKAGE_OUTPUT}
+ )
+
+install(
+ FILES ${PACKAGE_OUTPUT}
+ DESTINATION
+ usr/rift/mano/examples/ping_pong_ns
+ COMPONENT ${PKG_LONG_NAME}
+ )
+
+install(
+ FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/ping_vnfd_with_image.tar.gz
+ ${CMAKE_CURRENT_BINARY_DIR}/pong_vnfd_with_image.tar.gz
+ DESTINATION
+ usr/rift/mano/examples/ping_pong_ns
+ COMPONENT ${PKG_LONG_NAME}
+ OPTIONAL
+ )
+
+rift_python_install_tree(
+ COMPONENT ${PKG_LONG_NAME}
+ FILES
+ rift/mano/examples/ping_pong_nsd.py
+ rift/mano/examples/start_traffic.py
+ )
+
+install(
+ PROGRAMS
+ rift/mano/examples/ping_config.py
+ stand_up_ping_pong
+ DESTINATION usr/bin
+ COMPONENT ${PKG_LONG_NAME}
+ )
+
diff --git a/examples/ping_pong_ns/Makefile b/examples/ping_pong_ns/Makefile
new file mode 100644
index 0000000..2b691a8
--- /dev/null
+++ b/examples/ping_pong_ns/Makefile
@@ -0,0 +1,36 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+# Author(s): Tim Mortsolf
+# Creation Date: 11/25/2013
+#
+
+##
+# Define a Makefile function: find_upwards(filename)
+#
+# Searches for a file of the given name in the directory ., .., ../.., ../../.., etc.,
+# until the file is found or the root directory is reached
+##
+find_upward = $(word 1, $(shell while [ `pwd` != / ] ; do find `pwd` -maxdepth 1 -name $1 ; cd .. ; done))
+
+##
+# Call find_upward("Makefile.top") to find the nearest upwards adjacent Makefile.top
+##
+makefile.top := $(call find_upward, "Makefile.top")
+
+##
+# If Makefile.top was found, then include it
+##
+include $(makefile.top)
diff --git a/examples/ping_pong_ns/config_desc.py b/examples/ping_pong_ns/config_desc.py
new file mode 100755
index 0000000..fcd1400
--- /dev/null
+++ b/examples/ping_pong_ns/config_desc.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+import argparse
+import logging
+import rift.auto.proxy
+import rift.vcs
+import sys
+
+import gi
+gi.require_version('RwYang', '1.0')
+
+from gi.repository import NsdYang, VldYang, VnfdYang, RwYang
+
+logging.basicConfig(level=logging.DEBUG)
+logger = logging.getLogger(__name__)
+
+model = RwYang.Model.create_libncx()
+model.load_schema_ypbc(VldYang.get_schema())
+model.load_schema_ypbc(NsdYang.get_schema())
+model.load_schema_ypbc(VnfdYang.get_schema())
+
+
+def configure_vld(proxy, vld_xml_hdl):
+ vld_xml = vld_xml_hdl.read()
+ logger.debug("Attempting to deserialize XML into VLD protobuf: %s", vld_xml)
+ vld = VldYang.YangData_Vld_VldCatalog_Vld()
+ vld.from_xml_v2(model, vld_xml)
+
+ logger.debug("Sending VLD to netconf: %s", vld)
+ proxy.merge_config(vld.to_xml_v2(model))
+
+
+def configure_vnfd(proxy, vnfd_xml_hdl):
+ vnfd_xml = vnfd_xml_hdl.read()
+ logger.debug("Attempting to deserialize XML into VNFD protobuf: %s", vnfd_xml)
+ vnfd = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd()
+ vnfd.from_xml_v2(model, vnfd_xml)
+
+ logger.debug("Sending VNFD to netconf: %s", vnfd)
+ proxy.merge_config(vnfd.to_xml_v2(model))
+
+
+def configure_nsd(proxy, nsd_xml_hdl):
+ nsd_xml = nsd_xml_hdl.read()
+ logger.debug("Attempting to deserialize XML into NSD protobuf: %s", nsd_xml)
+ nsd = NsdYang.YangData_Nsd_NsdCatalog_Nsd()
+ nsd.from_xml_v2(model, nsd_xml)
+
+ logger.debug("Sending NSD to netconf: %s", nsd)
+ proxy.merge_config(nsd.to_xml_v2(model))
+
+
+def parse_args(argv=sys.argv[1:]):
+ """Create a parser which includes all generic demo arguments and parse args
+
+ Arguments:
+ argv - arguments to be parsed
+
+ Returns: List of parsed arguments
+ """
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--confd-host',
+ default="127.0.0.1",
+ help="Hostname or IP where the confd netconf server is running.")
+
+ parser.add_argument(
+ '--vld-xml-file',
+ action="append",
+ default=[],
+ type=argparse.FileType(),
+ help="VLD XML File Path",
+ )
+
+ parser.add_argument(
+ '--vnfd-xml-file',
+ action="append",
+ default=[],
+ type=argparse.FileType(),
+ help="VNFD XML File Path",
+ )
+
+ parser.add_argument(
+ '--nsd-xml-file',
+ action="append",
+ default=[],
+ type=argparse.FileType(),
+ help="VNFD XML File Path",
+ )
+
+ parser.add_argument(
+ '-v', '--verbose',
+ action='store_true',
+ help="Logging is normally set to an INFO level. When this flag "
+ "is used logging is set to DEBUG. ")
+
+ args = parser.parse_args(argv)
+
+ return args
+
+
+def connect(args):
+ # Initialize Netconf Management Proxy
+ mgmt_proxy = rift.auto.proxy.NetconfProxy(args.confd_host)
+ mgmt_proxy.connect()
+
+ # Ensure system started
+ vcs_component_info = rift.vcs.mgmt.VcsComponentInfo(mgmt_proxy)
+ vcs_component_info.wait_until_system_started()
+
+ return mgmt_proxy
+
+
+def main():
+ args = parse_args()
+ proxy = connect(args)
+ for xml_file in args.vnfd_xml_file:
+ configure_vnfd(proxy, xml_file)
+
+ for xml_file in args.vld_xml_file:
+ configure_vld(proxy, xml_file)
+
+ for xml_file in args.nsd_xml_file:
+ configure_nsd(proxy, xml_file)
+
+
+if __name__ == "__main__":
+ main()
+
diff --git a/examples/ping_pong_ns/generate_packages.sh.in b/examples/ping_pong_ns/generate_packages.sh.in
new file mode 100755
index 0000000..ae54052
--- /dev/null
+++ b/examples/ping_pong_ns/generate_packages.sh.in
@@ -0,0 +1,140 @@
+#! /bin/bash
+
+set -e
+set -x
+
+SOURCE_DIR=@CMAKE_CURRENT_SOURCE_DIR@
+BINARY_DIR=@CMAKE_CURRENT_BINARY_DIR@
+PROJECT_TOP_DIR=@PROJECT_TOP_DIR@
+QCOW_IMAGE=${RIFT_ROOT}/images/Fedora-x86_64-20-20131211.1-sda.qcow2
+RIFT_QCOW_IMAGE=${RIFT_ROOT}/images/Fedora-x86_64-20-20131211.1-sda.qcow2
+PONG_QCOW_IMAGE=${RIFT_ROOT}/images/Fedora-x86_64-20-20131211.1-sda-pong.qcow2
+PING_QCOW_IMAGE=${RIFT_ROOT}/images/Fedora-x86_64-20-20131211.1-sda-ping.qcow2
+
+# These paths are needed for finding the overrides and so files
+PYTHONPATH=${PYTHONPATH}:@RIFT_SUBMODULE_SOURCE_ROOT@/rwvcs/ra:@RIFT_SUBMODULE_BINARY_ROOT@/models/plugins/yang
+PYTHON3PATH=${PYTHON3PATH}:@RIFT_SUBMODULE_SOURCE_ROOT@/rwvcs/ra:@RIFT_SUBMODULE_BINARY_ROOT@/models/plugins/yang
+LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:@RIFT_SUBMODULE_BINARY_ROOT@/models/plugins/yang
+
+#Rift Logos
+PING_VNFD_LOGO=${SOURCE_DIR}/rift_logo.png
+PONG_VNFD_LOGO=${SOURCE_DIR}/rift_logo.png
+PING_PONG_NSD_LOGO=${SOURCE_DIR}/rift_logo.png
+
+# Remove any old directories
+rm -rf ${BINARY_DIR}/ping_vnfd
+rm -rf ${BINARY_DIR}/pong_vnfd
+rm -rf ${BINARY_DIR}/ping_pong_nsd
+
+rm -rf ${BINARY_DIR}/ping_vnfd_with_image
+rm -rf ${BINARY_DIR}/pong_vnfd_with_image
+
+
+rm -rf ${BINARY_DIR}/ping_vnfd_aws
+rm -rf ${BINARY_DIR}/pong_vnfd_aws
+rm -rf ${BINARY_DIR}/ping_pong_nsd_aws
+
+rm -rf ${BINARY_DIR}/ping_vnfd_with_epa
+rm -rf ${BINARY_DIR}/pong_vnfd_with_epa
+rm -rf ${BINARY_DIR}/ping_pong_nsd_with_epa
+
+
+# Generate image md5sum
+ping_md5sum="$(md5sum ${PING_QCOW_IMAGE} | cut -f1 -d" ")"
+pong_md5sum="$(md5sum ${PONG_QCOW_IMAGE} | cut -f1 -d" ")"
+
+# Generate the descriptors (in various formats)
+${SOURCE_DIR}/ping_pong_nsd.py --outdir=${BINARY_DIR} --format=yaml --ping-image-md5=${ping_md5sum} --pong-image-md5=${pong_md5sum} --pong-cloud-init=pong_cloud_init.cfg --ping-cloud-init=ping_cloud_init.cfg
+
+
+# create directories for packages with images
+cp -r ${BINARY_DIR}/ping_vnfd ${BINARY_DIR}/ping_vnfd_with_image
+cp -r ${BINARY_DIR}/pong_vnfd ${BINARY_DIR}/pong_vnfd_with_image
+mkdir -p ${BINARY_DIR}/ping_vnfd_with_image/images
+mkdir -p ${BINARY_DIR}/pong_vnfd_with_image/images
+
+### Generate descriptors for AWS
+${SOURCE_DIR}/ping_pong_nsd.py --outdir=${BINARY_DIR}/aws --format=json --aws
+
+### Move the generated artifacts to appropriate directories
+mv ${BINARY_DIR}/aws/ping_vnfd ${BINARY_DIR}/ping_vnfd_aws
+mv ${BINARY_DIR}/aws/pong_vnfd ${BINARY_DIR}/pong_vnfd_aws
+mv ${BINARY_DIR}/aws/ping_pong_nsd ${BINARY_DIR}/ping_pong_nsd_aws
+
+### ReMove the original directories
+rm -rf ${BINARY_DIR}/aws
+
+### Generate descriptors with EPA
+${SOURCE_DIR}/ping_pong_nsd.py --outdir=${BINARY_DIR}/with_epa --format=json --epa --ping-image-md5=${ping_md5sum} --pong-image-md5=${pong_md5sum}
+
+### Move the generated artifacts to appropriate directories
+mv ${BINARY_DIR}/with_epa/ping_vnfd ${BINARY_DIR}/ping_vnfd_with_epa
+mv ${BINARY_DIR}/with_epa/pong_vnfd ${BINARY_DIR}/pong_vnfd_with_epa
+mv ${BINARY_DIR}/with_epa/ping_pong_nsd ${BINARY_DIR}/ping_pong_nsd_with_epa
+
+### ReMove the original directories
+rm -rf ${BINARY_DIR}/with_epa
+
+# copy a dummy image for now
+if [ -e ${PING_QCOW_IMAGE} ]; then
+# Add RIFT Logos
+ mkdir -p ${BINARY_DIR}/ping_vnfd_with_image/icons
+ cp ${PING_VNFD_LOGO} ${BINARY_DIR}/ping_vnfd_with_image/icons/
+
+ cp ${PING_QCOW_IMAGE} ${BINARY_DIR}/ping_vnfd_with_image/images/
+ ${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} ping_vnfd_with_image
+else
+ echo >&2 "Warn: Skipped creating ping_vnfd_with_image due to missing image: ${PING_QCOW_IMAGE}"
+fi
+
+if [ -e ${PONG_QCOW_IMAGE} ]; then
+# Add RIFT Logos
+ mkdir -p ${BINARY_DIR}/pong_vnfd_with_image/icons
+ cp ${PONG_VNFD_LOGO} ${BINARY_DIR}/pong_vnfd_with_image/icons/
+
+ cp ${PONG_QCOW_IMAGE} ${BINARY_DIR}/pong_vnfd_with_image/images/
+ ${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} pong_vnfd_with_image
+else
+ echo >&2 "Warn: Skipped creating pong_vnfd_with_image due to missing image: ${PONG_QCOW_IMAGE}"
+fi
+
+# Add RIFT Logos
+mkdir -p ${BINARY_DIR}/ping_vnfd/icons
+mkdir -p ${BINARY_DIR}/pong_vnfd/icons
+mkdir -p ${BINARY_DIR}/ping_pong_nsd/icons
+
+cp ${PING_VNFD_LOGO} ${BINARY_DIR}/ping_vnfd/icons/
+cp ${PONG_VNFD_LOGO} ${BINARY_DIR}/pong_vnfd/icons/
+cp ${PING_PONG_NSD_LOGO} ${BINARY_DIR}/ping_pong_nsd/icons/
+
+# Generate the tar files
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} ping_vnfd
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} pong_vnfd
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} ping_pong_nsd
+
+
+# Add RIFT Logos
+mkdir -p ${BINARY_DIR}/ping_vnfd_aws/icons
+mkdir -p ${BINARY_DIR}/pong_vnfd_aws/icons
+mkdir -p ${BINARY_DIR}/ping_pong_nsd_aws/icons
+
+cp ${PING_VNFD_LOGO} ${BINARY_DIR}/ping_vnfd_aws/icons/
+cp ${PONG_VNFD_LOGO} ${BINARY_DIR}/pong_vnfd_aws/icons/
+cp ${PING_PONG_NSD_LOGO} ${BINARY_DIR}/ping_pong_nsd_aws/icons/
+
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} ping_vnfd_aws
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} pong_vnfd_aws
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} ping_pong_nsd_aws
+
+# Add RIFT Logos
+mkdir -p ${BINARY_DIR}/ping_vnfd_with_epa/icons
+mkdir -p ${BINARY_DIR}/pong_vnfd_with_epa/icons
+mkdir -p ${BINARY_DIR}/ping_pong_nsd_with_epa/icons
+
+cp ${PING_VNFD_LOGO} ${BINARY_DIR}/ping_vnfd_with_epa/icons/
+cp ${PONG_VNFD_LOGO} ${BINARY_DIR}/pong_vnfd_with_epa/icons/
+cp ${PING_PONG_NSD_LOGO} ${BINARY_DIR}/ping_pong_nsd_with_epa/icons/
+
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} ping_vnfd_with_epa
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} pong_vnfd_with_epa
+${RIFT_INSTALL}/usr/rift/toolchain/cmake/bin/generate_descriptor_pkg.sh ${BINARY_DIR} ping_pong_nsd_with_epa
diff --git a/examples/ping_pong_ns/ping_pong_ns/__init__.py b/examples/ping_pong_ns/ping_pong_ns/__init__.py
new file mode 100644
index 0000000..966870a
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/__init__.py
@@ -0,0 +1,15 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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/examples/ping_pong_ns/ping_pong_ns/ping.py b/examples/ping_pong_ns/ping_pong_ns/ping.py
new file mode 100644
index 0000000..00da96c
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/ping.py
@@ -0,0 +1,312 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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 datetime import date
+import logging
+import json
+import socket
+import threading
+import time
+
+import tornado.web
+
+from util.util import get_url_target
+
+class Ping(object):
+ def __init__(self):
+ self._log = logging.getLogger("ping")
+ self._log.setLevel(logging.DEBUG)
+
+ self._ping_count = 0;
+ self._request_count = 0;
+ self._response_count = 0;
+
+ self._pong_ip = None
+ self._pong_port = None
+
+ self._send_rate = 1 # per second
+
+ self._close_lock = threading.Lock()
+
+ self._enabled = False
+ self._socket = None
+
+ @property
+ def rate(self):
+ return self._send_rate
+
+ @rate.setter
+ def rate(self, value):
+ self._log.debug("new rate: %s" % value)
+ self._send_rate = value
+
+ @property
+ def pong_port(self):
+ return self._pong_port
+
+ @pong_port.setter
+ def pong_port(self, value):
+ self._log.debug("new pong port: %s" % value)
+ self._pong_port = value
+
+ @property
+ def pong_ip(self):
+ return self._pong_ip
+
+ @pong_ip.setter
+ def pong_ip(self, value):
+
+ self._log.debug("new pong ip: %s" % value)
+ self._pong_ip = value
+
+ @property
+ def enabled(self):
+ return self._enabled
+
+ @property
+ def request_count(self):
+ return self._request_count
+
+ @property
+ def response_count(self):
+ return self._response_count
+
+ def start(self):
+ self._log.debug("starting")
+ self._enabled = True
+ # self.open_socket()
+ self.send_thread = threading.Thread(target=self.send_ping)
+ self.recv_thread = threading.Thread(target=self.recv_resp)
+ self.send_thread.start()
+ self.recv_thread.start()
+
+ def stop(self):
+ self._log.debug("stopping")
+ self._enabled = False
+ self.close_socket("stopping")
+
+ def close_socket(self, msg):
+ self._close_lock.acquire()
+ if self._socket != None:
+ self._socket.close()
+ self._socket = None
+ self._log.info("Closed socket with msg={}".format(msg))
+ self._close_lock.release()
+
+ def open_socket(self):
+ try:
+ self._log.debug("construct socket")
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.settimeout(1)
+ except socket.error as msg:
+ self._log.error("error constructing socket %s" % msg)
+ self._socket = None
+
+ while self._enabled:
+ try:
+ self._log.info("Trying to connect....")
+ self._socket.connect((self.pong_ip, self.pong_port))
+ self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+ self._log.info("Socket connected")
+ break
+ except socket.error as msg:
+ time.sleep(1)
+
+
+ def send_ping(self):
+ self.open_socket()
+
+ while self._enabled:
+ if self._socket != None:
+ req = "rwpingpong-{}".format(self._ping_count)
+ try:
+ self._log.info("sending: %s" %req)
+ self._socket.sendall(req)
+ self._ping_count += 1
+ self._request_count += 1
+ except socket.error as msg:
+ self._log.error("Error({}) sending data".format(msg))
+ self.close_socket(msg)
+ return
+
+ time.sleep(1.0/self._send_rate)
+
+ self._log.info("Stopping send_ping")
+
+ def recv_resp(self):
+ while self._enabled:
+ respb = None
+ if self._socket != None:
+ try:
+ respb = self._socket.recv(1024)
+ except socket.timeout:
+ continue
+ except socket.error as msg:
+ self._log.error("Error({}) receiving data".format(msg))
+ time.sleep(1)
+ continue
+ # self.close_socket(msg)
+ # return
+
+ if not respb:
+ continue
+
+ resp = respb.decode('UTF-8')
+ self._response_count += 1
+ self._log.info("receive: %s" % resp)
+
+ self._log.info("Stopping recv_resp")
+
+class PingServerHandler(tornado.web.RequestHandler):
+ def initialize(self, ping_instance):
+ self._ping_instance = ping_instance
+
+ def get(self, args):
+ response = {'ip': self._ping_instance.pong_ip,
+ 'port': self._ping_instance.pong_port}
+
+ self.write(response)
+
+ def post(self, args):
+ target = get_url_target(self.request.uri)
+ body = self.request.body.decode("utf-8")
+ body_header = self.request.headers.get("Content-Type")
+
+ if "json" not in body_header:
+ self.write("Content-Type must be some kind of json 2")
+ self.set_status(405)
+ return
+
+ try:
+ json_dicts = json.loads(body)
+ except:
+ self.write("Content-Type must be some kind of json 1")
+ self.set_status(405)
+ return
+
+ if target == "server":
+ if type(json_dicts['port']) is not int:
+ self.set_status(405)
+ return
+
+ if type(json_dicts['ip']) not in (str, unicode):
+ self.set_status(405)
+ return
+
+ self._ping_instance.pong_ip = json_dicts['ip']
+ self._ping_instance.pong_port = json_dicts['port']
+
+ else:
+ self.set_status(404)
+ return
+
+ self.set_status(200)
+
+class PingAdminStatusHandler(tornado.web.RequestHandler):
+ def initialize(self, ping_instance):
+ self._ping_instance = ping_instance
+
+ def get(self, args):
+ target = get_url_target(self.request.uri)
+ if target == "state":
+ value = "enabled" if self._ping_instance.enabled else "disabled"
+
+ response = { 'adminstatus': value }
+ else:
+ self.set_status(404)
+ return
+
+ self.write(response)
+
+ def post(self, args):
+ target = get_url_target(self.request.uri)
+ body = self.request.body.decode("utf-8")
+ body_header = self.request.headers.get("Content-Type")
+
+ if "json" not in body_header:
+ self.write("Content-Type must be some kind of json 2")
+ self.set_status(405)
+ return
+
+ try:
+ json_dicts = json.loads(body)
+ except:
+ self.write("Content-Type must be some kind of json 1")
+ self.set_status(405)
+ return
+
+ if target == "state":
+ if type(json_dicts['enable']) is not bool:
+ self.set_status(405)
+ return
+
+ if json_dicts['enable']:
+ if not self._ping_instance.enabled:
+ self._ping_instance.start()
+ else:
+ self._ping_instance.stop()
+
+ else:
+ self.set_status(404)
+ return
+
+ self.set_status(200)
+
+class PingStatsHandler(tornado.web.RequestHandler):
+ def initialize(self, ping_instance):
+ self._ping_instance = ping_instance
+
+ def get(self):
+ response = {'ping-request-tx-count': self._ping_instance.request_count,
+ 'ping-response-rx-count': self._ping_instance.response_count}
+
+ self.write(response)
+
+class PingRateHandler(tornado.web.RequestHandler):
+ def initialize(self, ping_instance):
+ self._ping_instance = ping_instance
+
+ def get(self, args):
+ response = { 'rate': self._ping_instance.rate }
+
+ self.write(response)
+
+ def post(self, args):
+ target = get_url_target(self.request.uri)
+ body = self.request.body.decode("utf-8")
+ body_header = self.request.headers.get("Content-Type")
+
+ if "json" not in body_header:
+ self.set_status(405)
+ return
+
+ try:
+ json_dicts = json.loads(body)
+ except:
+ self.set_status(405)
+ return
+
+ if target == "rate":
+ if type(json_dicts['rate']) is not int:
+ self.set_status(405)
+ return
+
+ self._ping_instance.rate = json_dicts['rate']
+ else:
+ self.set_status(404)
+ return
+
+ self.set_status(200)
diff --git a/examples/ping_pong_ns/ping_pong_ns/ping.service b/examples/ping_pong_ns/ping_pong_ns/ping.service
new file mode 100644
index 0000000..cd0ac65
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/ping.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Ping Client
+After=syslog.target network.target
+
+[Service]
+Type=simple
+ExecStart=/opt/rift/ping_pong_ns/start_ping
+
+[Install]
+WantedBy=multi-user.target
+
+
diff --git a/examples/ping_pong_ns/ping_pong_ns/pong.py b/examples/ping_pong_ns/ping_pong_ns/pong.py
new file mode 100644
index 0000000..ee5c2d2
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/pong.py
@@ -0,0 +1,334 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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 datetime import date
+from Queue import Queue
+import logging
+import json
+import socket
+import threading
+import time
+
+import tornado.web
+
+from util.util import get_url_target
+
+class Stats(object):
+ def __init__(self):
+ self._request_count = 0
+ self._response_count = 0
+
+ self._lock = threading.Lock()
+
+ @property
+ def request_count(self):
+ with self._lock:
+ return self._request_count
+
+ @request_count.setter
+ def request_count(self, value):
+ with self._lock:
+ self._request_count = value
+
+ @property
+ def response_count(self):
+ with self._lock:
+ return self._response_count
+
+ @response_count.setter
+ def response_count(self, value):
+ with self._lock:
+ self._response_count = value
+
+class Worker(threading.Thread):
+ def __init__(self, log, connections, stats):
+ super(Worker, self).__init__()
+ self._log = log
+ self._connections = connections
+ self._stats = stats
+
+ self._running = True
+
+ self._lock = threading.Lock()
+
+ @property
+ def running(self):
+ return self._running
+
+ @running.setter
+ def running(self, value):
+ self._running = value
+
+ def run(self):
+ while self.running:
+ try:
+ connection = self._connections.get_nowait()
+ except:
+ continue
+
+ try:
+ req = connection.recv(1024)
+ except socket.error as msg:
+ self._log.error("error with connection read: " % msg)
+ self._connections.put(connection)
+ continue
+
+ if not req:
+ self._connections.put(connection)
+ continue
+
+ resp = req.decode('UTF-8')
+ self._log.debug("got: %s", resp)
+
+ self._stats.request_count += 1
+
+ try:
+ connection.sendall(resp)
+ self._stats.response_count += 1
+ except socket.error as msg:
+ self._log.error("error with connection read: " % msg)
+ self._connections.put(connection)
+ continue
+
+ self._connections.put(connection)
+
+class Pong(object):
+ def __init__(self, worker_count=5):
+ self._log = logging.getLogger("pong")
+ self._log.setLevel(logging.DEBUG)
+
+ self.listen_ip = None
+ self.listen_port = None
+
+ self._lock = threading.Lock()
+
+ self._connections = Queue()
+
+ self._stats = Stats()
+
+ self._workers = list()
+
+ self._enabled = False
+
+ for _ in range(worker_count):
+ self._workers.append(Worker(self._log, self._connections, self._stats))
+
+ @property
+ def listen_port(self):
+ return self._listen_port
+
+ @listen_port.setter
+ def listen_port(self, value):
+ self._log.debug("new listen port: %s" % value)
+ self._listen_port = value
+
+ @property
+ def listen_ip(self):
+ return self._listen_ip
+
+ @listen_ip.setter
+ def listen_ip(self, value):
+ self._log.debug("listen pong ip: %s" % value)
+ self._listen_ip = value
+
+
+ @property
+ def enabled(self):
+ with self._lock:
+ return self._enabled
+
+ @property
+ def request_count(self):
+ return self._stats.request_count
+
+ @property
+ def response_count(self):
+ return self._stats.response_count
+
+ def start(self):
+ self._log.debug("starting")
+ self._enabled = True
+ self.listener_thread = threading.Thread(target=self._listen)
+ self.listener_thread.start()
+ for worker in self._workers:
+ worker.start()
+
+ def stop(self):
+ with self._lock:
+ self._enabled = False
+
+ self._log.debug("stopping workers")
+ for worker in self._workers:
+ worker.running = False
+
+ self._log.debug("joining on workers")
+ for worker in self._workers:
+ if worker.is_alive():
+ worker.join()
+
+ while self._connections.full():
+ try:
+ connection = self._connections.get_nowait()
+ connection.close()
+ except:
+ pass
+
+ def close_socket(self, msg):
+ with self._lock:
+ if self._socket != None:
+ self._socket.shutdown(socket.SHUT_RD)
+ self._socket.close()
+ self._socket = None
+ self._log.info("Closed socket with msg={}".format(msg))
+
+ def _listen(self):
+ if self._listen_ip is None or self.listen_port is None:
+ self._log.error("address not properly configured to listen")
+ return
+
+ self._log.info("listen for incomming connections")
+ try:
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+ # self._socket.bind((self.listen_ip, self.listen_port))
+ self._socket.bind(("0.0.0.0", self.listen_port))
+ self._socket.settimeout(1)
+
+ while self.enabled:
+
+ try:
+ self._socket.listen(1)
+ connection, address = self._socket.accept()
+ except socket.timeout:
+ continue
+ self._log.info("Accepted connection from {}".format(address))
+
+ self._connections.put(connection)
+ else:
+ self.stop()
+ except socket.error as msg:
+ self.close_socket(msg)
+
+class PongStatsHandler(tornado.web.RequestHandler):
+ def initialize(self, pong_instance):
+ self._pong_instance = pong_instance
+
+ def get(self):
+ response = {'ping-request-rx-count': self._pong_instance.request_count,
+ 'ping-response-tx-count': self._pong_instance.response_count}
+
+ self.write(response)
+
+
+class PongServerHandler(tornado.web.RequestHandler):
+ def initialize(self, pong_instance):
+ self._pong_instance = pong_instance
+
+ def get(self, args):
+ response = {'ip': self._pong_instance.listen_ip,
+ 'port': self._pong_instance.listen_port}
+
+ self.write(response)
+
+ def post(self, args):
+ target = get_url_target(self.request.uri)
+ body = self.request.body.decode("utf-8")
+ body_header = self.request.headers.get("Content-Type")
+
+ if "json" not in body_header:
+ self.write("Content-Type must be some kind of json")
+ self.set_status(405)
+ return
+
+ try:
+ json_dicts = json.loads(body)
+ except:
+ self.write("Content-Type must be some kind of json")
+ self.set_status(405)
+ return
+
+ if target == "server":
+
+ if type(json_dicts['port']) is not int:
+ self.set_status(405)
+ return
+
+ if type(json_dicts['ip']) not in (str, unicode):
+ self.set_status(405)
+ return
+
+ self._pong_instance.listen_ip = json_dicts['ip']
+ self._pong_instance.listen_port = json_dicts['port']
+
+ else:
+ self.set_status(404)
+ return
+
+ self.set_status(200)
+
+class PongAdminStatusHandler(tornado.web.RequestHandler):
+ def initialize(self, pong_instance):
+ self._pong_instance = pong_instance
+
+ def get(self, args):
+ target = get_url_target(self.request.uri)
+
+ if target == "state":
+ value = "enabled" if self._pong_instance.enabled else "disabled"
+
+ response = { 'adminstatus': value }
+ else:
+ self.set_status(404)
+ return
+
+ self.write(response)
+
+ def post(self, args):
+ target = get_url_target(self.request.uri)
+ body = self.request.body.decode("utf-8")
+ body_header = self.request.headers.get("Content-Type")
+
+ if "json" not in body_header:
+ self.write("Content-Type must be some kind of json")
+ self.set_status(405)
+ return
+
+ try:
+ json_dicts = json.loads(body)
+ except:
+ self.write("Content-Type must be some kind of json")
+ self.set_status(405)
+ return
+
+ if target == "state":
+ if type(json_dicts['enable']) is not bool:
+ self.set_status(405)
+ return
+
+ if json_dicts['enable']:
+ if not self._pong_instance.enabled:
+ self._pong_instance.start()
+ else:
+ self._pong_instance.stop()
+
+ else:
+ self.set_status(404)
+ return
+
+ self.set_status(200)
+
+
diff --git a/examples/ping_pong_ns/ping_pong_ns/pong.service b/examples/ping_pong_ns/ping_pong_ns/pong.service
new file mode 100644
index 0000000..7d94836
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/pong.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Ping Client
+After=syslog.target network.target
+
+[Service]
+Type=simple
+ExecStart=/opt/rift/ping_pong_ns/start_pong
+
+[Install]
+WantedBy=multi-user.target
+
+
diff --git a/examples/ping_pong_ns/ping_pong_ns/prepare_ping_pong_qcow.sh b/examples/ping_pong_ns/ping_pong_ns/prepare_ping_pong_qcow.sh
new file mode 100755
index 0000000..e73144a
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/prepare_ping_pong_qcow.sh
@@ -0,0 +1,137 @@
+#! /bin/bash
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+# Author(s): Anil Gunturu
+# Creation Date: 07/24/2014
+#
+
+##
+# This script is used to copy the riftware software into the qcow image
+# This script must be run on the grunt machine as root
+##
+
+set -x
+set -e
+
+if ! [ $# -eq 1 ]; then
+ echo "Usage: $0 <ping-pong-ns-dir>"
+ echo " Example:"
+ echo " $0 /net/boson/home1/agunturu/lepton/atg/modules/core/mc/examples/ping_pong_ns"
+ exit 1
+fi
+
+# Currently returning 0 on error as this script fails in Bangalore
+# systems and causes the jenkins spot_debug to fail
+function cleanup {
+ if [ "$(ls -A $MOUNT_PT)" ]; then
+ guestunmount $MOUNT_PT
+ fi
+ exit 0
+}
+trap cleanup EXIT
+
+MOUNT_PT=ping_pong/mnt$$
+
+if [ -d $MOUNT_PT ]; then
+ echo "ping_pong_mnt directory exists - deleting..!!"
+ guestunmount $MOUNT_PT || true
+ rm -rf ping_pong
+fi
+
+mkdir -p $MOUNT_PT
+FC20QCOW=Fedora-x86_64-20-20131211.1-sda.qcow2
+PINGQCOW=Fedora-x86_64-20-20131211.1-sda-ping.qcow2
+PONGQCOW=Fedora-x86_64-20-20131211.1-sda-pong.qcow2
+
+if [ ! -e ${RIFT_ROOT}/images/${FC20QCOW} ]; then
+ echo >&2 "Warn: Cannot prepare ping_pong qcow due to missing FC20 image: ${RIFT_ROOT}/images/${FC20QCOW}"
+ exit 0
+fi
+
+echo "Copying $FC20QCOW"
+cp ${RIFT_ROOT}/images/${FC20QCOW} ping_pong/${PINGQCOW}
+chmod +w ping_pong/${PINGQCOW}
+cp ${RIFT_ROOT}/images/${FC20QCOW} ping_pong/${PONGQCOW}
+chmod +w ping_pong/${PONGQCOW}
+
+CURRENT_DIR=$PWD
+echo "Mounting guestfs for $PINGQCOW"
+guestmount -a ping_pong/$PINGQCOW -m /dev/sda1 $MOUNT_PT
+
+echo "Setting up resolv.conf"
+# removed RIFT.io lab-centric setup in RIFT-11991
+#echo "search lab.riftio.com eng.riftio.com riftio.com" > $MOUNT_PT/etc/resolv.conf
+#echo "nameserver 10.64.1.3" >> $MOUNT_PT/etc/resolv.conf
+#echo "PEERDNS=no" >> $MOUNT_PT/etc/sysconfig/network-scripts/ifcfg-eth0
+
+# add a valid DNS server just in case
+echo "nameserver 8.8.8.8" > $MOUNT_PT/etc/resolv.conf
+echo "DEFROUTE=yes" >> $MOUNT_PT/etc/sysconfig/network-scripts/ifcfg-eth0
+
+for i in 1 2
+do
+ cat <<EOF >> $MOUNT_PT/etc/sysconfig/network-scripts/ifcfg-eth$i
+DEVICE="eth$i"
+BOOTPROTO="dhcp"
+ONBOOT="no"
+TYPE="Ethernet"
+DEFROUTE=no
+PEERDNS=no
+EOF
+done
+
+
+echo "Copying ping/pong ns..."
+cd $MOUNT_PT/opt
+mkdir rift
+cd rift
+cp -r $1 .
+cd $CURRENT_DIR
+mv $MOUNT_PT/opt/rift/ping_pong_ns/ping.service $MOUNT_PT/etc/systemd/system
+cp -ar /usr/lib/python2.7/site-packages/tornado $MOUNT_PT/usr/lib/python2.7/site-packages/
+guestunmount $MOUNT_PT
+
+echo "Mounting guestfs for $PINGQCOW"
+guestmount -a ping_pong/$PONGQCOW -m /dev/sda1 $MOUNT_PT
+
+echo "Setting up resolv.conf"
+echo "search lab.riftio.com eng.riftio.com riftio.com" > $MOUNT_PT/etc/resolv.conf
+echo "nameserver 10.64.1.3" >> $MOUNT_PT/etc/resolv.conf
+echo "PEERDNS=no" >> $MOUNT_PT/etc/sysconfig/network-scripts/ifcfg-eth0
+echo "DEFROUTE=yes" >> $MOUNT_PT/etc/sysconfig/network-scripts/ifcfg-eth0
+
+for i in 1 2
+do
+ cat <<EOF >> $MOUNT_PT/etc/sysconfig/network-scripts/ifcfg-eth$i
+DEVICE="eth$i"
+BOOTPROTO="dhcp"
+ONBOOT="no"
+DEFROUTE=no
+TYPE="Ethernet"
+PEERDNS=no
+EOF
+done
+
+echo "Copying ping/pong ns..."
+cd $MOUNT_PT/opt
+mkdir rift
+cd rift
+cp -r $1 .
+cd $CURRENT_DIR
+cp -ar /usr/lib/python2.7/site-packages/tornado $MOUNT_PT/usr/lib/python2.7/site-packages/
+mv $MOUNT_PT/opt/rift/ping_pong_ns/pong.service $MOUNT_PT/etc/systemd/system
+guestunmount $MOUNT_PT
diff --git a/examples/ping_pong_ns/ping_pong_ns/start_ping b/examples/ping_pong_ns/ping_pong_ns/start_ping
new file mode 100755
index 0000000..fb29422
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/start_ping
@@ -0,0 +1,5 @@
+#!/bin/bash
+ulimit -c 0
+#yum install -y python-tornado
+python /opt/rift/ping_pong_ns/start_ping.py 2>&1 | logger
+
diff --git a/examples/ping_pong_ns/ping_pong_ns/start_ping.py b/examples/ping_pong_ns/ping_pong_ns/start_ping.py
new file mode 100644
index 0000000..ace5981
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/start_ping.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+import argparse
+import signal
+import logging
+
+import tornado
+import tornado.httpserver
+
+from ping import (
+ Ping,
+ PingAdminStatusHandler,
+ PingServerHandler,
+ PingRateHandler,
+ PingStatsHandler,
+)
+from util.util import (
+ VersionHandler,
+)
+
+logging.basicConfig(level=logging.DEBUG,
+ format='(%(threadName)-10s) %(name)-8s :: %(message)s',
+)
+
+def main():
+ log = logging.getLogger("main")
+
+ # parse arguments
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--ping-manager-port",
+ required=False,
+ default="18888",
+ help="port number for ping")
+
+ arguments = parser.parse_args()
+
+ # setup application
+ log.debug("setup application")
+ ping_instance = Ping()
+ ping_application_arguments = {'ping_instance': ping_instance}
+ ping_application = tornado.web.Application([
+ (r"/api/v1/ping/stats", PingStatsHandler, ping_application_arguments),
+ (r"/api/v1/ping/adminstatus/([a-z]+)", PingAdminStatusHandler, ping_application_arguments),
+ (r"/api/v1/ping/server/?([0-9a-z\.]*)", PingServerHandler, ping_application_arguments),
+ (r"/api/v1/ping/rate/?([0-9]*)", PingRateHandler, ping_application_arguments),
+ (r"/version", VersionHandler, ping_application_arguments)
+ ])
+ ping_server = tornado.httpserver.HTTPServer(
+ ping_application)
+
+ # setup SIGINT handler
+ log.debug("setup SIGINT handler")
+ def signal_handler(signal, frame):
+ print("") # print newline to clear user input
+ log.info("Exiting")
+ ping_instance.stop()
+ ping_server.stop()
+ log.info("Sayonara!")
+ quit()
+
+ signal.signal(signal.SIGINT, signal_handler)
+
+ # start
+ log.debug("start")
+ try:
+ ping_server.listen(arguments.ping_manager_port)
+ except OSError:
+ print("port %s is already is use, exiting" % arguments.ping_manager_port)
+ return
+
+ tornado.ioloop.IOLoop.instance().start()
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/examples/ping_pong_ns/ping_pong_ns/start_pong b/examples/ping_pong_ns/ping_pong_ns/start_pong
new file mode 100755
index 0000000..af46646
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/start_pong
@@ -0,0 +1,5 @@
+#!/bin/bash
+ulimit -c 0
+#yum install -y python-tornado
+python /opt/rift/ping_pong_ns/start_pong.py 2>&1 | logger
+
diff --git a/examples/ping_pong_ns/ping_pong_ns/start_pong.py b/examples/ping_pong_ns/ping_pong_ns/start_pong.py
new file mode 100644
index 0000000..235efb2
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/start_pong.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+import argparse
+import signal
+import logging
+
+import tornado
+import tornado.httpserver
+
+from pong import (
+ Pong,
+ PongAdminStatusHandler,
+ PongServerHandler,
+ PongStatsHandler,
+)
+from util.util import (
+ VersionHandler,
+)
+
+logging.basicConfig(level=logging.DEBUG,
+ format='(%(threadName)-10s) %(name)-8s :: %(message)s',
+)
+
+def main():
+ log = logging.getLogger("main")
+
+ # parse arguments
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--pong-manager-port",
+ required=False,
+ default="18889",
+ help="port number for pong")
+ parser.add_argument(
+ "--worker-count",
+ required=False,
+ default=5,
+ help="ip address of pong")
+
+ arguments = parser.parse_args()
+
+ # setup application
+ log.debug("setup application")
+ pong_instance = Pong(arguments.worker_count)
+ pong_application_arguments = {'pong_instance': pong_instance}
+ pong_application = tornado.web.Application([
+ (r"/version", VersionHandler, pong_application_arguments),
+ (r"/api/v1/pong/stats", PongStatsHandler, pong_application_arguments),
+ (r"/api/v1/pong/server/?([0-9a-z\.]*)", PongServerHandler, pong_application_arguments),
+ (r"/api/v1/pong/adminstatus/([a-z]+)", PongAdminStatusHandler, pong_application_arguments)
+ ])
+ pong_server = tornado.httpserver.HTTPServer(
+ pong_application)
+
+ # setup SIGINT handler
+ log.debug("setup SIGINT handler")
+ def signal_handler(signal, frame):
+ print("") # print newline to clear user input
+ log.info("Exiting")
+ pong_instance.stop()
+ pong_server.stop()
+ log.info("Sayonara!")
+ quit()
+
+ signal.signal(signal.SIGINT, signal_handler)
+
+ # start
+ log.debug("pong application listening on %s" % arguments.pong_manager_port)
+ try:
+ pong_server.listen(arguments.pong_manager_port)
+ except OSError:
+ print("port %s is already is use, exiting" % arguments.ping_manager_port)
+ return
+ tornado.ioloop.IOLoop.instance().start()
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/examples/ping_pong_ns/ping_pong_ns/test/test.sh b/examples/ping_pong_ns/ping_pong_ns/test/test.sh
new file mode 100644
index 0000000..c05a7d5
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/test/test.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+
+pong_ip='14.0.0.2'
+pong_port=18889
+
+ping_ip='14.0.0.3'
+
+ping_port=18888
+
+if [ "$1" == "pong" ];
+then
+ if [ "$2" == "enable" ];
+ then
+ echo "enable pong"
+
+ curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"enable\":true}" \
+ http://${pong_ip}:${pong_port}/api/v1/pong/adminstatus/state
+ fi
+ if [ "$2" == "disable" ];
+ then
+ echo "disable pong"
+
+ curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"enable\":false}" \
+ http://${pong_ip}:${pong_port}/api/v1/pong/adminstatus/state
+ fi
+
+ if [ "$2" == "server" ];
+ then
+ echo "set server"
+
+ curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"ip\":\"$3\", \"port\":$4}" \
+ http://${pong_ip}:${pong_port}/api/v1/pong/server
+ fi
+
+ echo ""
+fi
+
+if [ "$1" == "ping" ];
+then
+ if [ "$2" == "enable" ];
+ then
+ echo "enable ping"
+
+ curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"enable\":true}" \
+ http://${ping_ip}:${ping_port}/api/v1/ping/adminstatus/state
+ fi
+ if [ "$2" == "disable" ];
+ then
+ echo "disable ping"
+
+ curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"enable\":false}" \
+ http://${ping_ip}:${ping_port}/api/v1/ping/adminstatus/state
+ fi
+ echo ""
+
+ if [ "$2" == "rate" ];
+ then
+ echo "disable ping"
+
+ curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"rate\":$3}" \
+ http://${ping_ip}:${ping_port}/api/v1/ping/rate
+ fi
+ echo ""
+
+ if [ "$2" == "server" ];
+ then
+ echo "set server"
+
+ curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"ip\":\"$3\", \"port\":$4}" \
+ http://${ping_ip}:${ping_port}/api/v1/ping/server
+ fi
+ echo ""
+
+
+fi
+
+if [ "$1" == "stats" ];
+then
+ echo "ping stats:"
+ curl http://${ping_ip}:${ping_port}/api/v1/ping/stats
+ echo ""
+
+ echo "pong stats:"
+ curl http://${pong_ip}:${pong_port}/api/v1/pong/stats
+ echo ""
+fi
+
+if [ "$1" == "config" ];
+then
+ echo "ping server:"
+ curl http://${ping_ip}:${ping_port}/api/v1/ping/server
+ echo ""
+ echo "ping rate:"
+ curl http://${ping_ip}:${ping_port}/api/v1/ping/rate
+ echo ""
+ echo "ping admin status:"
+ curl http://${ping_ip}:${ping_port}/api/v1/ping/adminstatus/state
+ echo ""
+ echo "pong server:"
+ curl http://${pong_ip}:${pong_port}/api/v1/pong/server
+ echo ""
+ echo "pong admin status:"
+ curl http://${pong_ip}:${pong_port}/api/v1/pong/adminstatus/state
+ echo ""
+fi
diff --git a/examples/ping_pong_ns/ping_pong_ns/user-data b/examples/ping_pong_ns/ping_pong_ns/user-data
new file mode 100644
index 0000000..9bf1d5b
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/user-data
@@ -0,0 +1,8 @@
+#cloud-config
+password: fedora
+chpasswd: { expire: False }
+ssh_pwauth: True
+runcmd:
+ - [ systemctl, daemon-reload ]
+ - [ systemctl, enable, ping.service ]
+ - [ systemctl, start, --no-block, ping.service ]
diff --git a/examples/ping_pong_ns/ping_pong_ns/util/__init__.py b/examples/ping_pong_ns/ping_pong_ns/util/__init__.py
new file mode 100644
index 0000000..966870a
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/util/__init__.py
@@ -0,0 +1,15 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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/examples/ping_pong_ns/ping_pong_ns/util/util.py b/examples/ping_pong_ns/ping_pong_ns/util/util.py
new file mode 100644
index 0000000..66c11fc
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_ns/util/util.py
@@ -0,0 +1,38 @@
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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 datetime import date
+import urlparse
+
+import tornado.web
+
+class VersionHandler(tornado.web.RequestHandler):
+ def initialize(self, instance):
+ self._instance = instance
+
+ def get(self):
+ response = { 'version': '3.5.1',
+ 'last_build': date.today().isoformat() }
+ self.write(response)
+
+def get_url_target(url):
+ is_operation = False
+ url_parts = urlparse.urlsplit(url)
+ whole_url = url_parts[2]
+
+ url_pieces = whole_url.split("/")
+
+ return url_pieces[-1]
diff --git a/examples/ping_pong_ns/ping_pong_nsd.py b/examples/ping_pong_ns/ping_pong_nsd.py
new file mode 120000
index 0000000..3147ac8
--- /dev/null
+++ b/examples/ping_pong_ns/ping_pong_nsd.py
@@ -0,0 +1 @@
+rift/mano/examples/ping_pong_nsd.py
\ No newline at end of file
diff --git a/examples/ping_pong_ns/rift/mano/__init__.py b/examples/ping_pong_ns/rift/mano/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/examples/ping_pong_ns/rift/mano/__init__.py
diff --git a/examples/ping_pong_ns/rift/mano/examples/__init__.py b/examples/ping_pong_ns/rift/mano/examples/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/examples/ping_pong_ns/rift/mano/examples/__init__.py
diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_config.py b/examples/ping_pong_ns/rift/mano/examples/ping_config.py
new file mode 100755
index 0000000..4e5fd35
--- /dev/null
+++ b/examples/ping_pong_ns/rift/mano/examples/ping_config.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+import argparse
+import logging
+import os
+import stat
+import subprocess
+import sys
+import time
+import yaml
+
+def ping_config(run_dir, mgmt_ip, mgmt_port, pong_cp, logger, dry_run):
+ sh_file = "{}/ping_config-{}.sh".format(run_dir, time.strftime("%Y%m%d%H%M%S"))
+ logger.debug("Creating script file %s" % sh_file)
+ f = open(sh_file, "w")
+ f.write(r'''
+#!/bin/bash
+
+# Rest API config
+ping_mgmt_ip='{}'
+ping_mgmt_port={}
+
+# VNF specific configuration
+pong_server_ip='{}'
+ping_rate=5
+server_port=5555
+'''.format(mgmt_ip, mgmt_port, pong_cp))
+
+ f.write(r'''
+# Check if the port is open
+DELAY=1
+MAX_TRIES=60
+COUNT=0
+while true; do
+ COUNT=$(expr $COUNT + 1)
+ timeout 1 bash -c "cat < /dev/null > /dev/tcp/${ping_mgmt_ip}/${ping_mgmt_port}"
+ rc=$?
+ if [ $rc -ne 0 ]
+ then
+ echo "Failed to connect to server ${ping_mgmt_ip}:${ping_mgmt_port} for ping with $rc!"
+ if [ ${COUNT} -gt ${MAX_TRIES} ]; then
+ exit $rc
+ fi
+ sleep ${DELAY}
+ else
+ break
+ fi
+done
+
+# Make rest API calls to configure VNF
+curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
+ http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server
+rc=$?
+if [ $rc -ne 0 ]
+then
+ echo "Failed to set server info for ping!"
+ exit $rc
+fi
+
+curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"rate\":$ping_rate}" \
+ http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/rate
+rc=$?
+if [ $rc -ne 0 ]
+then
+ echo "Failed to set ping rate!"
+ exit $rc
+fi
+
+output=$(curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"enable\":true}" \
+ http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/adminstatus/state)
+if [[ $output == *"Internal Server Error"* ]]
+then
+ echo $output
+ exit 3
+else
+ echo $output
+fi
+
+exit 0
+''')
+ f.close()
+ os.chmod(sh_file, stat.S_IRWXU)
+ if not dry_run:
+ rc = subprocess.call(sh_file, shell=True)
+ if rc:
+ logger.error("Config failed: {}".format(rc))
+ return False
+ return True
+
+
+
+def main(argv=sys.argv[1:]):
+ try:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("yaml_cfg_file", type=argparse.FileType('r'))
+ parser.add_argument("--dry-run", action="store_true")
+ parser.add_argument("--quiet", "-q", dest="verbose", action="store_false")
+ args = parser.parse_args()
+
+ run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift")
+ if not os.path.exists(run_dir):
+ os.makedirs(run_dir)
+ log_file = "{}/rift_ping_config-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S"))
+ logging.basicConfig(filename=log_file, level=logging.DEBUG)
+ logger = logging.getLogger()
+
+ ch = logging.StreamHandler()
+ if args.verbose:
+ ch.setLevel(logging.DEBUG)
+ else:
+ ch.setLevel(logging.INFO)
+
+ # create formatter and add it to the handlers
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ ch.setFormatter(formatter)
+ logger.addHandler(ch)
+
+ except Exception as e:
+ print("Got exception:{}".format(e))
+ raise e
+
+ try:
+ dry_run = args.dry_run
+
+ yaml_str = args.yaml_cfg_file.read()
+ logger.debug("Input YAML file: {}".format(yaml_str))
+ yaml_cfg = yaml.load(yaml_str)
+ logger.debug("Input YAML: {}".format(yaml_cfg))
+
+ # Check if this is post scale out trigger
+ if yaml_cfg['trigger'] != 'post_scale_out':
+ logger.error("Unexpected trigger {}".
+ format(yaml_cfg['trigger']))
+ raise
+
+ pong_cp = ""
+ for vnfr in yaml_cfg['vnfrs_others']:
+ # Find the pong VNFR, assuming vnfr name will
+ # have pong_vnfd as a substring
+ if 'pong_vnfd' in vnfr['name']:
+ for cp in vnfr['connection_points']:
+ logger.debug("Connection point {}".format(cp))
+ if 'cp0' in cp['name']:
+ pong_cp = cp['ip_address']
+ break
+ if not len(pong_cp):
+ logger.error("Did not get Pong cp0 IP")
+ raise
+
+ for vnfr in yaml_cfg['vnfrs_in_group']:
+ mgmt_ip = vnfr['rw_mgmt_ip']
+ mgmt_port = vnfr['rw_mgmt_port']
+ if ping_config(run_dir, mgmt_ip, mgmt_port, pong_cp, logger, dry_run):
+ logger.info("Successfully configured Ping {} at {}".
+ format(vnfr['name'], mgmt_ip))
+ else:
+ logger.error("Config of ping {} with {} failed".
+ format(vnfr['name'], mgmt_ip))
+ raise
+
+ except Exception as e:
+ logger.error("Got exception {}".format(e))
+ logger.exception(e)
+ raise e
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_config_ut.sh b/examples/ping_pong_ns/rift/mano/examples/ping_config_ut.sh
new file mode 100755
index 0000000..67f3f19
--- /dev/null
+++ b/examples/ping_pong_ns/rift/mano/examples/ping_config_ut.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+
+echo "Executed ping config!"
diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py
new file mode 100755
index 0000000..bb834ed
--- /dev/null
+++ b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py
@@ -0,0 +1,1075 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+
+import argparse
+import os
+import shutil
+import sys
+import uuid
+
+import gi
+gi.require_version('RwYang', '1.0')
+gi.require_version('RwVnfdYang', '1.0')
+gi.require_version('VnfdYang', '1.0')
+gi.require_version('RwNsdYang', '1.0')
+gi.require_version('NsdYang', '1.0')
+
+
+from gi.repository import (
+ RwNsdYang,
+ NsdYang,
+ RwVnfdYang,
+ VnfdYang,
+ RwYang,
+ )
+
+
+try:
+ import rift.mano.config_data.config as config_data
+except ImportError:
+ # Load modules from common which are not yet installed
+ path = os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "../../../common/python/rift/mano")
+ sys.path.append(path)
+
+ import config_data.config as config_data
+
+
+NUM_PING_INSTANCES = 1
+MAX_VNF_INSTANCES_PER_NS = 10
+use_epa = False
+aws = False
+pingcount = NUM_PING_INSTANCES
+use_ping_cloud_init_file = ""
+use_pong_cloud_init_file = ""
+
+PING_USERDATA_FILE = '''#cloud-config
+password: fedora
+chpasswd: { expire: False }
+ssh_pwauth: True
+runcmd:
+ - [ systemctl, daemon-reload ]
+ - [ systemctl, enable, ping.service ]
+ - [ systemctl, start, --no-block, ping.service ]
+ - [ ifup, eth1 ]
+'''
+
+PONG_USERDATA_FILE = '''#cloud-config
+password: fedora
+chpasswd: { expire: False }
+ssh_pwauth: True
+runcmd:
+ - [ systemctl, daemon-reload ]
+ - [ systemctl, enable, pong.service ]
+ - [ systemctl, start, --no-block, pong.service ]
+ - [ ifup, eth1 ]
+'''
+
+
+class UnknownVNFError(Exception):
+ pass
+
+
+class ManoDescriptor(object):
+ def __init__(self, name):
+ self.name = name
+ self.descriptor = None
+
+ def write_to_file(self, module_list, outdir, output_format):
+ model = RwYang.Model.create_libncx()
+ for module in module_list:
+ model.load_module(module)
+
+ if output_format == 'json':
+ with open('%s/%s.json' % (outdir, self.name), "w") as fh:
+ fh.write(self.descriptor.to_json(model))
+ elif output_format.strip() == 'xml':
+ with open('%s/%s.xml' % (outdir, self.name), "w") as fh:
+ fh.write(self.descriptor.to_xml_v2(model))
+ elif output_format.strip() == 'yaml':
+ with open('%s/%s.yaml' % (outdir, self.name), "w") as fh:
+ fh.write(self.descriptor.to_yaml(model))
+ else:
+ raise Exception("Invalid output format for the descriptor")
+
+ def get_json(self, module_list):
+ model = RwYang.Model.create_libncx()
+ for module in module_list:
+ model.load_module(module)
+ print(self.descriptor.to_json(model))
+
+
+class VirtualNetworkFunction(ManoDescriptor):
+ def __init__(self, name, instance_count=1):
+ self.vnfd_catalog = None
+ self.vnfd = None
+ self.instance_count = instance_count
+ self._placement_groups = []
+ super(VirtualNetworkFunction, self).__init__(name)
+
+ def add_placement_group(self, group):
+ self._placement_groups.append(group)
+
+ def compose(self, image_name, cloud_init="", cloud_init_file="", endpoint=None, mon_params=[],
+ mon_port=8888, mgmt_port=8888, num_vlr_count=1, num_ivlr_count=1,
+ num_vms=1, image_md5sum=None, mano_ut=False):
+ self.descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog()
+ self.id = str(uuid.uuid1())
+ vnfd = self.descriptor.vnfd.add()
+ vnfd.id = self.id
+ vnfd.name = self.name
+ vnfd.short_name = self.name
+ vnfd.vendor = 'RIFT.io'
+ vnfd.logo = 'rift_logo.png'
+ vnfd.description = 'This is an example RIFT.ware VNF'
+ vnfd.version = '1.0'
+
+ self.vnfd = vnfd
+
+ if mano_ut is True:
+ internal_vlds = []
+ for i in range(num_ivlr_count):
+ internal_vld = vnfd.internal_vld.add()
+ internal_vld.id = 'ivld%s' % i
+ internal_vld.name = 'fabric%s' % i
+ internal_vld.short_name = 'fabric%s' % i
+ internal_vld.description = 'Virtual link for internal fabric%s' % i
+ internal_vld.type_yang = 'ELAN'
+ internal_vlds.append(internal_vld)
+
+ for i in range(num_vlr_count):
+ cp = vnfd.connection_point.add()
+ cp.type_yang = 'VPORT'
+ cp.name = '%s/cp%d' % (self.name, i)
+
+ if endpoint is not None:
+ endp = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd_HttpEndpoint(
+ path=endpoint, port=mon_port, polling_interval_secs=2
+ )
+ vnfd.http_endpoint.append(endp)
+
+ # Monitoring params
+ for monp_dict in mon_params:
+ monp = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd_MonitoringParam.from_dict(monp_dict)
+ monp.http_endpoint_ref = endpoint
+ vnfd.monitoring_param.append(monp)
+
+
+ for i in range(num_vms):
+ # VDU Specification
+ vdu = vnfd.vdu.add()
+ vdu.id = 'iovdu_%s' % i
+ vdu.name = 'iovdu_%s' % i
+ vdu.count = 1
+ # vdu.mgmt_vpci = '0000:00:20.0'
+
+ # specify the VM flavor
+ if use_epa:
+ vdu.vm_flavor.vcpu_count = 4
+ vdu.vm_flavor.memory_mb = 1024
+ vdu.vm_flavor.storage_gb = 4
+ else:
+ vdu.vm_flavor.vcpu_count = 1
+ vdu.vm_flavor.memory_mb = 512
+ vdu.vm_flavor.storage_gb = 4
+
+ # Management interface
+ mgmt_intf = vnfd.mgmt_interface
+ mgmt_intf.vdu_id = vdu.id
+ mgmt_intf.port = mgmt_port
+ mgmt_intf.dashboard_params.path = endpoint
+ mgmt_intf.dashboard_params.port = mgmt_port
+
+ if cloud_init_file and len(cloud_init_file):
+ vdu.cloud_init_file = cloud_init_file
+ else:
+ vdu.cloud_init = cloud_init
+ if aws:
+ vdu.cloud_init += " - [ systemctl, restart, --no-block, elastic-network-interfaces.service ]\n"
+
+ # sepcify the guest EPA
+ if use_epa:
+ vdu.guest_epa.trusted_execution = False
+ vdu.guest_epa.mempage_size = 'LARGE'
+ vdu.guest_epa.cpu_pinning_policy = 'DEDICATED'
+ vdu.guest_epa.cpu_thread_pinning_policy = 'PREFER'
+ vdu.guest_epa.numa_node_policy.node_cnt = 2
+ vdu.guest_epa.numa_node_policy.mem_policy = 'STRICT'
+
+ node = vdu.guest_epa.numa_node_policy.node.add()
+ node.id = 0
+ node.memory_mb = 512
+ node.vcpu = [0, 1]
+
+ node = vdu.guest_epa.numa_node_policy.node.add()
+ node.id = 1
+ node.memory_mb = 512
+ node.vcpu = [2, 3]
+
+ # specify the vswitch EPA
+ vdu.vswitch_epa.ovs_acceleration = 'DISABLED'
+ vdu.vswitch_epa.ovs_offload = 'DISABLED'
+
+ # Specify the hypervisor EPA
+ vdu.hypervisor_epa.type_yang = 'PREFER_KVM'
+
+ # Specify the host EPA
+ # vdu.host_epa.cpu_model = 'PREFER_SANDYBRIDGE'
+ # vdu.host_epa.cpu_arch = 'PREFER_X86_64'
+ # vdu.host_epa.cpu_vendor = 'PREFER_INTEL'
+ # vdu.host_epa.cpu_socket_count = 2
+ # vdu.host_epa.cpu_core_count = 8
+ # vdu.host_epa.cpu_core_thread_count = 2
+ # vdu.host_epa.cpu_feature = ['PREFER_AES', 'REQUIRE_VME', 'PREFER_MMX','REQUIRE_SSE2']
+
+ if aws:
+ vdu.image = 'rift-ping-pong'
+ else:
+ vdu.image = image_name
+ if image_md5sum is not None:
+ vdu.image_checksum = image_md5sum
+
+ if mano_ut is True:
+ for i in range(num_ivlr_count):
+ internal_cp = vdu.internal_connection_point.add()
+ if vnfd.name.find("ping") >= 0:
+ cp_name = "ping"
+ else:
+ cp_name = "pong"
+ internal_cp.name = cp_name + "/icp{}".format(i)
+ internal_cp.id = cp_name + "/icp{}".format(i)
+ internal_cp.type_yang = 'VPORT'
+ internal_vlds[i].internal_connection_point_ref.append(internal_cp.id)
+
+ internal_interface = vdu.internal_interface.add()
+ internal_interface.name = 'fab%d' % i
+ internal_interface.vdu_internal_connection_point_ref = internal_cp.id
+ internal_interface.virtual_interface.type_yang = 'VIRTIO'
+
+ # internal_interface.virtual_interface.vpci = '0000:00:1%d.0'%i
+
+ for i in range(num_vlr_count):
+ external_interface = vdu.external_interface.add()
+ external_interface.name = 'eth%d' % i
+ external_interface.vnfd_connection_point_ref = '%s/cp%d' % (self.name, i)
+ if use_epa:
+ external_interface.virtual_interface.type_yang = 'VIRTIO'
+ else:
+ external_interface.virtual_interface.type_yang = 'VIRTIO'
+ # external_interface.virtual_interface.vpci = '0000:00:2%d.0'%i
+
+ for group in self._placement_groups:
+ placement_group = vnfd.placement_groups.add()
+ placement_group.name = group.name
+ placement_group.requirement = group.requirement
+ placement_group.strategy = group.strategy
+ if group.vdu_list:
+ ### Add specific VDUs to placement group
+ for vdu in group.vdu_list:
+ member_vdu = placement_group.member_vdus.add()
+ member_vdu.member_vdu_ref = vdu.id
+ else:
+ ### Add all VDUs to placement group
+ for vdu in vnfd.vdu:
+ member_vdu = placement_group.member_vdus.add()
+ member_vdu.member_vdu_ref = vdu.id
+
+
+ def write_to_file(self, outdir, output_format):
+ dirpath = "%s/%s" % (outdir, self.name)
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+ super(VirtualNetworkFunction, self).write_to_file(['vnfd', 'rw-vnfd'],
+ dirpath,
+ output_format)
+ self.add_scripts(outdir)
+
+ def add_scripts(self, outdir):
+ script_dir = os.path.join(outdir, self.name, 'cloud_init')
+ try:
+ os.makedirs(script_dir)
+ except OSError:
+ if not os.path.isdir(script_dir):
+ raise
+
+ if 'ping' in self.name:
+ script_file = os.path.join(script_dir, 'ping_cloud_init.cfg')
+ cfg = PING_USERDATA_FILE
+ else:
+ script_file = os.path.join(script_dir, 'pong_cloud_init.cfg')
+ cfg = PONG_USERDATA_FILE
+
+ with open(script_file, "w") as f:
+ f.write("{}".format(cfg))
+
+
+class NetworkService(ManoDescriptor):
+ def __init__(self, name):
+ super(NetworkService, self).__init__(name)
+ self._scale_groups = []
+ self.vnfd_config = {}
+ self._placement_groups = []
+
+ def ping_config(self, mano_ut, use_ns_init_conf):
+ suffix = ''
+ if mano_ut:
+ ping_cfg = r'''
+#!/bin/bash
+
+echo "!!!!!!!! Executed ping Configuration !!!!!!!!!"
+ '''
+ else:
+ ping_cfg = r'''
+#!/bin/bash
+
+# Rest API config
+ping_mgmt_ip='<rw_mgmt_ip>'
+ping_mgmt_port=18888
+
+# VNF specific configuration
+pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
+ping_rate=5
+server_port=5555
+
+# Make rest API calls to configure VNF
+curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
+ http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server
+rc=$?
+if [ $rc -ne 0 ]
+then
+ echo "Failed to set server info for ping!"
+ exit $rc
+fi
+
+curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"rate\":$ping_rate}" \
+ http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/rate
+rc=$?
+if [ $rc -ne 0 ]
+then
+ echo "Failed to set ping rate!"
+ exit $rc
+fi
+
+''' % suffix
+ if use_ns_init_conf:
+ ping_cfg += "exit 0\n"
+ else:
+ ping_cfg +='''
+output=$(curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"enable\":true}" \
+ http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/adminstatus/state)
+if [[ $output == *"Internal Server Error"* ]]
+then
+ echo $output
+ exit 3
+else
+ echo $output
+fi
+
+exit 0
+'''
+ return ping_cfg
+
+ def pong_config(self, mano_ut, use_ns_init_conf):
+ suffix = ''
+ if mano_ut:
+ pong_cfg = r'''
+#!/bin/bash
+
+echo "!!!!!!!! Executed pong Configuration !!!!!!!!!"
+ '''
+ else:
+ pong_cfg = r'''
+#!/bin/bash
+
+# Rest API configuration
+pong_mgmt_ip='<rw_mgmt_ip>'
+pong_mgmt_port=18889
+# username=<rw_username>
+# password=<rw_password>
+
+# VNF specific configuration
+pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
+server_port=5555
+
+# Make Rest API calls to configure VNF
+curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
+ http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/server
+rc=$?
+if [ $rc -ne 0 ]
+then
+ echo "Failed to set server(own) info for pong!"
+ exit $rc
+fi
+
+''' % suffix
+
+ if use_ns_init_conf:
+ pong_cfg += "exit 0\n"
+ else:
+ pong_cfg +='''
+curl -D /dev/stdout \
+ -H "Accept: application/vnd.yang.data+xml" \
+ -H "Content-Type: application/vnd.yang.data+json" \
+ -X POST \
+ -d "{\"enable\":true}" \
+ http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/adminstatus/state
+rc=$?
+if [ $rc -ne 0 ]
+then
+ echo "Failed to enable pong service!"
+ exit $rc
+fi
+
+exit 0
+'''
+ return pong_cfg
+
+ def pong_fake_juju_config(self, vnf_config):
+
+ if vnf_config:
+ # Select "script" configuration
+ vnf_config.juju.charm = 'clearwater-aio-proxy'
+
+ # Set the initital-config
+ vnf_config.create_initial_config_primitive()
+ init_config = VnfdYang.InitialConfigPrimitive.from_dict({
+ "seq": 1,
+ "name": "config",
+ "parameter": [
+ {"name": "proxied_ip", "value": "<rw_mgmt_ip>"},
+ ]
+ })
+ vnf_config.initial_config_primitive.append(init_config)
+
+ init_config_action = VnfdYang.InitialConfigPrimitive.from_dict({
+ "seq": 2,
+ "name": "action1",
+ "parameter": [
+ {"name": "Pong Connection Point", "value": "pong_vnfd/cp0"},
+ ]
+ })
+ vnf_config.initial_config_primitive.append(init_config_action)
+ init_config_action = VnfdYang.InitialConfigPrimitive.from_dict({
+ "seq": 3,
+ "name": "action2",
+ "parameter": [
+ {"name": "Ping Connection Point", "value": "ping_vnfd/cp0"},
+ ]
+ })
+ vnf_config.initial_config_primitive.append(init_config_action)
+
+ # Config parameters can be taken from config.yaml and
+ # actions from actions.yaml in the charm
+ # Config to set the home domain
+ vnf_config.create_service_primitive()
+ config = VnfdYang.ServicePrimitive.from_dict({
+ "name": "config",
+ "parameter": [
+ {"name": "home_domain", "data_type": "STRING"},
+ {"name": "base_number", "data_type": "STRING"},
+ {"name": "number_count", "data_type": "INTEGER"},
+ {"name": "password", "data_type": "STRING"},
+ ]
+ })
+ vnf_config.service_primitive.append(config)
+
+ config = VnfdYang.ServicePrimitive.from_dict({
+ "name": "create-update-user",
+ # "user-defined-script":"/tmp/test.py",
+ "parameter": [
+ {"name": "number", "data_type": "STRING", "mandatory": True},
+ {"name": "password", "data_type": "STRING", "mandatory": True},
+ ]
+ })
+ vnf_config.service_primitive.append(config)
+
+ config = VnfdYang.ServicePrimitive.from_dict({
+ "name": "delete-user",
+ "parameter": [
+ {"name": "number", "data_type": "STRING", "mandatory": True},
+ ]
+ })
+ vnf_config.service_primitive.append(config)
+
+ def default_config(self, const_vnfd, vnfd, mano_ut, use_ns_init_conf):
+ vnf_config = vnfd.vnfd.vnf_configuration
+
+ vnf_config.config_attributes.config_priority = 0
+ vnf_config.config_attributes.config_delay = 0
+
+ # Select "script" configuration
+ vnf_config.script.script_type = 'bash'
+
+ if vnfd.name == 'pong_vnfd' or vnfd.name == 'pong_vnfd_with_epa' or vnfd.name == 'pong_vnfd_aws':
+ vnf_config.config_attributes.config_priority = 1
+ vnf_config.config_template = self.pong_config(mano_ut, use_ns_init_conf)
+ # First priority config delay will delay the entire NS config delay
+ if mano_ut is False:
+ vnf_config.config_attributes.config_delay = 60
+ else:
+ # This is PONG and inside mano_ut
+ # This is test only
+ vnf_config.config_attributes.config_delay = 10
+ # vnf_config.config_template = self.pong_config(vnf_config, use_ns_init_conf)
+
+ if vnfd.name == 'ping_vnfd' or vnfd.name == 'ping_vnfd_with_epa' or vnfd.name == 'ping_vnfd_aws':
+ vnf_config.config_attributes.config_priority = 2
+ vnf_config.config_template = self.ping_config(mano_ut, use_ns_init_conf)
+
+ def ns_config(self, nsd, vnfd_list, mano_ut):
+ # Used by scale group
+ if mano_ut:
+ nsd.service_primitive.add().from_dict(
+ {
+ "name": "ping config",
+ "user_defined_script": "{}".format(os.path.join(
+ os.environ['RIFT_ROOT'],
+ 'modules/core/mano',
+ 'examples/ping_pong_ns/rift/mano/examples',
+ 'ping_config_ut.sh'))
+ })
+ else:
+ nsd.service_primitive.add().from_dict(
+ {
+ "name": "ping config",
+ "user_defined_script": "ping_config.py"
+ })
+
+ def ns_initial_config(self, nsd):
+ nsd.initial_config_primitive.add().from_dict(
+ {
+ "seq": 1,
+ "name": "start traffic",
+ "user_defined_script": "start_traffic.py",
+ "parameter": [
+ {
+ 'name': 'userid',
+ 'value': 'rift',
+ },
+ ],
+ }
+ )
+
+ def add_scale_group(self, scale_group):
+ self._scale_groups.append(scale_group)
+
+ def add_placement_group(self, placement_group):
+ self._placement_groups.append(placement_group)
+
+ def create_mon_params(self, vnfds):
+ NsdMonParam = NsdYang.YangData_Nsd_NsdCatalog_Nsd_MonitoringParam
+ param_id = 1
+ for vnfd_obj in vnfds:
+ for mon_param in vnfd_obj.vnfd.monitoring_param:
+ nsd_monp = NsdMonParam.from_dict({
+ 'id': str(param_id),
+ 'name': mon_param.name,
+ 'aggregation_type': "AVERAGE",
+ 'value_type': mon_param.value_type,
+ 'vnfd_monitoring_param': [
+ {'vnfd_id_ref': vnfd_obj.vnfd.id,
+ 'vnfd_monitoring_param_ref': mon_param.id}]
+ })
+
+ self.nsd.monitoring_param.append(nsd_monp)
+ param_id += 1
+
+
+
+
+ def compose(self, vnfd_list, cpgroup_list, mano_ut, use_ns_init_conf=True):
+
+ if mano_ut:
+ # Disable NS initial config primitive
+ use_ns_init_conf=False
+
+ self.descriptor = RwNsdYang.YangData_Nsd_NsdCatalog()
+ self.id = str(uuid.uuid1())
+ nsd = self.descriptor.nsd.add()
+ self.nsd = nsd
+ nsd.id = self.id
+ nsd.name = self.name
+ nsd.short_name = self.name
+ nsd.vendor = 'RIFT.io'
+ nsd.logo = 'rift_logo.png'
+ nsd.description = 'Toy NS'
+ nsd.version = '1.0'
+ nsd.input_parameter_xpath.append(
+ NsdYang.YangData_Nsd_NsdCatalog_Nsd_InputParameterXpath(
+ xpath="/nsd:nsd-catalog/nsd:nsd/nsd:vendor",
+ )
+ )
+
+ ip_profile = nsd.ip_profiles.add()
+ ip_profile.name = "InterVNFLink"
+ ip_profile.description = "Inter VNF Link"
+ ip_profile.ip_profile_params.ip_version = "ipv4"
+ ip_profile.ip_profile_params.subnet_address = "31.31.31.0/24"
+ ip_profile.ip_profile_params.gateway_address = "31.31.31.210"
+
+ vld_id = 1
+ for cpgroup in cpgroup_list:
+ vld = nsd.vld.add()
+ vld.id = 'ping_pong_vld%s' % vld_id
+ vld_id += 1
+ vld.name = 'ping_pong_vld' # hard coded
+ vld.short_name = vld.name
+ vld.vendor = 'RIFT.io'
+ vld.description = 'Toy VL'
+ vld.version = '1.0'
+ vld.type_yang = 'ELAN'
+ vld.ip_profile_ref = 'InterVNFLink'
+ for cp in cpgroup:
+ cpref = vld.vnfd_connection_point_ref.add()
+ cpref.member_vnf_index_ref = cp[0]
+ cpref.vnfd_id_ref = cp[1]
+ cpref.vnfd_connection_point_ref = cp[2]
+
+ vnfd_index_map = {}
+ member_vnf_index = 1
+ for vnfd in vnfd_list:
+ for i in range(vnfd.instance_count):
+ constituent_vnfd = nsd.constituent_vnfd.add()
+ constituent_vnfd.member_vnf_index = member_vnf_index
+ vnfd_index_map[vnfd] = member_vnf_index
+
+ # Set the start by default to false for ping vnfd,
+ # if scaling is enabled
+ if (len(self._scale_groups) and
+ vnfd.descriptor.vnfd[0].name == 'ping_vnfd'):
+ constituent_vnfd.start_by_default = False
+
+ constituent_vnfd.vnfd_id_ref = vnfd.descriptor.vnfd[0].id
+ self.default_config(constituent_vnfd, vnfd, mano_ut,
+ use_ns_init_conf,)
+ member_vnf_index += 1
+
+ # Enable config primitives if either mano_ut or
+ # scale groups are enabled
+ if mano_ut or len(self._scale_groups):
+ self.ns_config(nsd, vnfd_list, mano_ut)
+
+ # Add NS initial config to start traffic
+ if use_ns_init_conf:
+ self.ns_initial_config(nsd)
+
+ for scale_group in self._scale_groups:
+ group_desc = nsd.scaling_group_descriptor.add()
+ group_desc.name = scale_group.name
+ group_desc.max_instance_count = scale_group.max_count
+ group_desc.min_instance_count = scale_group.min_count
+ for vnfd, count in scale_group.vnfd_count_map.items():
+ member = group_desc.vnfd_member.add()
+ member.member_vnf_index_ref = vnfd_index_map[vnfd]
+ member.count = count
+
+ for trigger in scale_group.config_action:
+ config_action = group_desc.scaling_config_action.add()
+ config_action.trigger = trigger
+ config = scale_group.config_action[trigger]
+ config_action.ns_config_primitive_name_ref = config['ns-config-primitive-name-ref']
+
+ for placement_group in self._placement_groups:
+ group = nsd.placement_groups.add()
+ group.name = placement_group.name
+ group.strategy = placement_group.strategy
+ group.requirement = placement_group.requirement
+ for member_vnfd in placement_group.vnfd_list:
+ member = group.member_vnfd.add()
+ member.vnfd_id_ref = member_vnfd.descriptor.vnfd[0].id
+ member.member_vnf_index_ref = vnfd_index_map[member_vnfd]
+
+ # self.create_mon_params(vnfd_list)
+
+ def write_config(self, outdir, vnfds):
+
+ converter = config_data.ConfigPrimitiveConvertor()
+ yaml_data = converter.extract_nsd_config(self.nsd)
+
+ ns_config_dir = os.path.join(outdir, self.name, "ns_config")
+ os.makedirs(ns_config_dir, exist_ok=True)
+ vnf_config_dir = os.path.join(outdir, self.name, "vnf_config")
+ os.makedirs(vnf_config_dir, exist_ok=True)
+
+ if len(yaml_data):
+ with open('%s/%s.yaml' % (ns_config_dir, self.id), "w") as fh:
+ fh.write(yaml_data)
+
+ for i, vnfd in enumerate(vnfds, start=1):
+ yaml_data = converter.extract_vnfd_config(vnfd)
+
+ if len(yaml_data):
+ with open('%s/%s__%s.yaml' % (vnf_config_dir, vnfd.id, i), "w") as fh:
+ fh.write(yaml_data)
+
+ def write_initial_config_script(self, outdir):
+ script_name = 'start_traffic.py'
+
+ src_path = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
+ script_src = os.path.join(src_path, script_name)
+ if not os.path.exists(script_src):
+ src_path = os.path.join(os.environ['RIFT_ROOT'],
+ 'modules/core/mano/examples/ping_pong_ns/rift/mano/examples')
+ script_src = os.path.join(src_path, script_name)
+
+ dest_path = os.path.join(outdir, 'scripts')
+ os.makedirs(dest_path, exist_ok=True)
+
+ shutil.copy2(script_src, dest_path)
+
+ def write_to_file(self, outdir, output_format):
+ dirpath = os.path.join(outdir, self.name)
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+
+ super(NetworkService, self).write_to_file(["nsd", "rw-nsd"],
+ dirpath,
+ output_format)
+
+ # Write the initial config script
+ self.write_initial_config_script(dirpath)
+
+
+def get_ping_mon_params(path):
+ return [
+ {
+ 'id': '1',
+ 'name': 'ping-request-tx-count',
+ 'http_endpoint_ref': path,
+ 'json_query_method': "NAMEKEY",
+ 'value_type': "INT",
+ 'description': 'no of ping requests',
+ 'group_tag': 'Group1',
+ 'widget_type': 'COUNTER',
+ 'units': 'packets'
+ },
+
+ {
+ 'id': '2',
+ 'name': 'ping-response-rx-count',
+ 'http_endpoint_ref': path,
+ 'json_query_method': "NAMEKEY",
+ 'value_type': "INT",
+ 'description': 'no of ping responses',
+ 'group_tag': 'Group1',
+ 'widget_type': 'COUNTER',
+ 'units': 'packets'
+ },
+ ]
+
+
+def get_pong_mon_params(path):
+ return [
+ {
+ 'id': '1',
+ 'name': 'ping-request-rx-count',
+ 'http_endpoint_ref': path,
+ 'json_query_method': "NAMEKEY",
+ 'value_type': "INT",
+ 'description': 'no of ping requests',
+ 'group_tag': 'Group1',
+ 'widget_type': 'COUNTER',
+ 'units': 'packets'
+ },
+
+ {
+ 'id': '2',
+ 'name': 'ping-response-tx-count',
+ 'http_endpoint_ref': path,
+ 'json_query_method': "NAMEKEY",
+ 'value_type': "INT",
+ 'description': 'no of ping responses',
+ 'group_tag': 'Group1',
+ 'widget_type': 'COUNTER',
+ 'units': 'packets'
+ },
+ ]
+
+
+class ScaleGroup(object):
+ def __init__(self, name, min_count=1, max_count=1):
+ self.name = name
+ self.min_count = min_count
+ self.max_count = max_count
+ self.vnfd_count_map = {}
+ self.config_action = {}
+
+ def add_vnfd(self, vnfd, vnfd_count):
+ self.vnfd_count_map[vnfd] = vnfd_count
+
+ def add_config(self):
+ self.config_action['post_scale_out']= {'ns-config-primitive-name-ref':
+ 'ping config'}
+
+class PlacementGroup(object):
+ def __init__(self, name):
+ self.name = name
+ self.strategy = ''
+ self.requirement = ''
+
+ def add_strategy(self, strategy):
+ self.strategy = strategy
+
+ def add_requirement(self, requirement):
+ self.requirement = requirement
+
+class NsdPlacementGroup(PlacementGroup):
+ def __init__(self, name):
+ self.vnfd_list = []
+ super(NsdPlacementGroup, self).__init__(name)
+
+ def add_member(self, vnfd):
+ self.vnfd_list.append(vnfd)
+
+
+class VnfdPlacementGroup(PlacementGroup):
+ def __init__(self, name):
+ self.vdu_list = []
+ super(VnfdPlacementGroup, self).__init__(name)
+
+ def add_member(self, vdu):
+ self.vdu_list.append(vdu)
+
+
+
+
+def generate_ping_pong_descriptors(fmt="json",
+ write_to_file=False,
+ out_dir="./",
+ pingcount=NUM_PING_INSTANCES,
+ external_vlr_count=1,
+ internal_vlr_count=1,
+ num_vnf_vms=1,
+ ping_md5sum=None,
+ pong_md5sum=None,
+ mano_ut=False,
+ use_scale_group=False,
+ ping_fmt=None,
+ pong_fmt=None,
+ nsd_fmt=None,
+ use_mon_params=True,
+ ping_userdata=None,
+ pong_userdata=None,
+ ex_ping_userdata=None,
+ ex_pong_userdata=None,
+ use_placement_group=True,
+ use_ns_init_conf=True,
+ ):
+ # List of connection point groups
+ # Each connection point group refers to a virtual link
+ # the CP group consists of tuples of connection points
+ cpgroup_list = []
+ for i in range(external_vlr_count):
+ cpgroup_list.append([])
+
+ suffix = ''
+ ping = VirtualNetworkFunction("ping_vnfd%s" % (suffix), pingcount)
+
+ if use_placement_group:
+ ### Add group name Eris
+ group = VnfdPlacementGroup('Eris')
+ group.add_strategy('COLOCATION')
+ group.add_requirement('''Place this VM on the Kuiper belt object Eris''')
+ ping.add_placement_group(group)
+
+ # ping = VirtualNetworkFunction("ping_vnfd", pingcount)
+ if not ping_userdata:
+ ping_userdata = PING_USERDATA_FILE
+
+ if ex_ping_userdata:
+ ping_userdata = '''\
+{ping_userdata}
+{ex_ping_userdata}
+ '''.format(
+ ping_userdata=ping_userdata,
+ ex_ping_userdata=ex_ping_userdata
+ )
+
+ ping.compose(
+ "Fedora-x86_64-20-20131211.1-sda-ping.qcow2",
+ ping_userdata,
+ use_ping_cloud_init_file,
+ "api/v1/ping/stats",
+ get_ping_mon_params("api/v1/ping/stats") if use_mon_params else [],
+ mon_port=18888,
+ mgmt_port=18888,
+ num_vlr_count=external_vlr_count,
+ num_ivlr_count=internal_vlr_count,
+ num_vms=num_vnf_vms,
+ image_md5sum=ping_md5sum,
+ mano_ut=mano_ut,
+ )
+
+ pong = VirtualNetworkFunction("pong_vnfd%s" % (suffix))
+
+ if use_placement_group:
+ ### Add group name Weywot
+ group = VnfdPlacementGroup('Weywot')
+ group.add_strategy('COLOCATION')
+ group.add_requirement('''Place this VM on the Kuiper belt object Weywot''')
+ pong.add_placement_group(group)
+
+
+ # pong = VirtualNetworkFunction("pong_vnfd")
+
+ if not pong_userdata:
+ pong_userdata = PONG_USERDATA_FILE
+
+ if ex_pong_userdata:
+ pong_userdata = '''\
+{pong_userdata}
+{ex_pong_userdata}
+ '''.format(
+ pong_userdata=pong_userdata,
+ ex_pong_userdata=ex_pong_userdata
+ )
+
+
+ pong.compose(
+ "Fedora-x86_64-20-20131211.1-sda-pong.qcow2",
+ pong_userdata,
+ use_pong_cloud_init_file,
+ "api/v1/pong/stats",
+ get_pong_mon_params("api/v1/pong/stats") if use_mon_params else [],
+ mon_port=18889,
+ mgmt_port=18889,
+ num_vlr_count=external_vlr_count,
+ num_ivlr_count=internal_vlr_count,
+ num_vms=num_vnf_vms,
+ image_md5sum=pong_md5sum,
+ mano_ut=mano_ut,
+ )
+
+ # Initialize the member VNF index
+ member_vnf_index = 1
+
+ # define the connection point groups
+ for index, cp_group in enumerate(cpgroup_list):
+ desc_id = ping.descriptor.vnfd[0].id
+ filename = 'ping_vnfd{}/cp{}'.format(suffix, index)
+
+ for idx in range(pingcount):
+ cp_group.append((
+ member_vnf_index,
+ desc_id,
+ filename,
+ ))
+
+ member_vnf_index += 1
+
+ desc_id = pong.descriptor.vnfd[0].id
+ filename = 'pong_vnfd{}/cp{}'.format(suffix, index)
+
+ cp_group.append((
+ member_vnf_index,
+ desc_id,
+ filename,
+ ))
+
+ member_vnf_index += 1
+
+ vnfd_list = [ping, pong]
+
+ nsd_catalog = NetworkService("ping_pong_nsd%s" % (suffix))
+
+ if use_scale_group:
+ group = ScaleGroup("ping_group", max_count=10)
+ group.add_vnfd(ping, 1)
+ group.add_config()
+ nsd_catalog.add_scale_group(group)
+
+ if use_placement_group:
+ ### Add group name Orcus
+ group = NsdPlacementGroup('Orcus')
+ group.add_strategy('COLOCATION')
+ group.add_requirement('''Place this VM on the Kuiper belt object Orcus''')
+
+ for member_vnfd in vnfd_list:
+ group.add_member(member_vnfd)
+
+ nsd_catalog.add_placement_group(group)
+
+ ### Add group name Quaoar
+ group = NsdPlacementGroup('Quaoar')
+ group.add_strategy('COLOCATION')
+ group.add_requirement('''Place this VM on the Kuiper belt object Quaoar''')
+
+ for member_vnfd in vnfd_list:
+ group.add_member(member_vnfd)
+
+ nsd_catalog.add_placement_group(group)
+
+
+ nsd_catalog.compose(vnfd_list,
+ cpgroup_list,
+ mano_ut,
+ use_ns_init_conf=use_ns_init_conf,)
+
+ if write_to_file:
+ ping.write_to_file(out_dir, ping_fmt if ping_fmt is not None else fmt)
+ pong.write_to_file(out_dir, pong_fmt if ping_fmt is not None else fmt)
+ nsd_catalog.write_config(out_dir, vnfd_list)
+ nsd_catalog.write_to_file(out_dir, ping_fmt if nsd_fmt is not None else fmt)
+
+ return (ping, pong, nsd_catalog)
+
+
+def main(argv=sys.argv[1:]):
+ global outdir, output_format, use_epa, aws, use_ping_cloud_init_file, use_pong_cloud_init_file
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-o', '--outdir', default='.')
+ parser.add_argument('-f', '--format', default='json')
+ parser.add_argument('-e', '--epa', action="store_true", default=False)
+ parser.add_argument('-a', '--aws', action="store_true", default=False)
+ parser.add_argument('-n', '--pingcount', default=NUM_PING_INSTANCES)
+ parser.add_argument('--ping-image-md5')
+ parser.add_argument('--pong-image-md5')
+ parser.add_argument('--ping-cloud-init', default=None)
+ parser.add_argument('--pong-cloud-init', default=None)
+ args = parser.parse_args()
+ outdir = args.outdir
+ output_format = args.format
+ use_epa = args.epa
+ aws = args.aws
+ pingcount = args.pingcount
+ use_ping_cloud_init_file = args.ping_cloud_init
+ use_pong_cloud_init_file = args.pong_cloud_init
+
+ generate_ping_pong_descriptors(args.format, True, args.outdir, pingcount,
+ ping_md5sum=args.ping_image_md5, pong_md5sum=args.pong_image_md5,
+ mano_ut=False,
+ use_scale_group=False,)
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/ping_pong_ns/rift/mano/examples/start_traffic.py b/examples/ping_pong_ns/rift/mano/examples/start_traffic.py
new file mode 100755
index 0000000..af6f62f
--- /dev/null
+++ b/examples/ping_pong_ns/rift/mano/examples/start_traffic.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+
+############################################################################
+# Copyright 2016 RIFT.IO Inc #
+# #
+# 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. #
+############################################################################
+
+
+import argparse
+import logging
+import os
+import subprocess
+import sys
+import time
+
+import yaml
+
+
+def start_traffic(yaml_cfg, logger):
+ '''Use curl and set admin status to enable on pong and ping vnfs'''
+
+ def enable_service(mgmt_ip, port, vnf_type):
+ curl_cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \
+ '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \
+ '-X POST -d "{{\\"enable\\":true}}" http://{mgmt_ip}:' \
+ '{mgmt_port}/api/v1/{vnf_type}/adminstatus/state'. \
+ format(
+ mgmt_ip=mgmt_ip,
+ mgmt_port=port,
+ vnf_type=vnf_type)
+
+ logger.debug("Executing cmd: %s", curl_cmd)
+ subprocess.check_call(curl_cmd, shell=True)
+
+ # Enable pong service first
+ for index, vnfr in yaml_cfg['vnfr'].items():
+ logger.debug("VNFR {}: {}".format(index, vnfr))
+
+ # Check if it is pong vnf
+ if 'pong_vnfd' in vnfr['name']:
+ vnf_type = 'pong'
+ port = 18889
+ enable_service(vnfr['mgmt_ip_address'], port, vnf_type)
+ break
+
+ # Add a delay to provide pong port to come up
+ time.sleep(0.1)
+
+ # Enable ping service next
+ for index, vnfr in yaml_cfg['vnfr'].items():
+ logger.debug("VNFR {}: {}".format(index, vnfr))
+
+ # Check if it is pong vnf
+ if 'ping_vnfd' in vnfr['name']:
+ vnf_type = 'ping'
+ port = 18888
+ enable_service(vnfr['mgmt_ip_address'], port, vnf_type)
+ break
+
+def main(argv=sys.argv[1:]):
+ try:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("yaml_cfg_file", type=argparse.FileType('r'))
+ parser.add_argument("-q", "--quiet", dest="verbose", action="store_false")
+ args = parser.parse_args()
+
+ run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift")
+ if not os.path.exists(run_dir):
+ os.makedirs(run_dir)
+ log_file = "{}/ping_pong_start_traffic-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S"))
+ logging.basicConfig(filename=log_file, level=logging.DEBUG)
+ logger = logging.getLogger()
+
+ except Exception as e:
+ print("Exception in {}: {}".format(__file__, e))
+ sys.exit(1)
+
+ try:
+ ch = logging.StreamHandler()
+ if args.verbose:
+ ch.setLevel(logging.DEBUG)
+ else:
+ ch.setLevel(logging.INFO)
+
+ # create formatter and add it to the handlers
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ ch.setFormatter(formatter)
+ logger.addHandler(ch)
+
+ except Exception as e:
+ logger.exception(e)
+ raise e
+
+ try:
+ yaml_str = args.yaml_cfg_file.read()
+ # logger.debug("Input YAML file:\n{}".format(yaml_str))
+ yaml_cfg = yaml.load(yaml_str)
+ logger.debug("Input YAML: {}".format(yaml_cfg))
+
+ start_traffic(yaml_cfg, logger)
+
+ except Exception as e:
+ logger.exception(e)
+ raise e
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/ping_pong_ns/rift_logo.png b/examples/ping_pong_ns/rift_logo.png
new file mode 100644
index 0000000..09b47c7
--- /dev/null
+++ b/examples/ping_pong_ns/rift_logo.png
Binary files differ
diff --git a/examples/ping_pong_ns/stand_up_ping_pong b/examples/ping_pong_ns/stand_up_ping_pong
new file mode 100644
index 0000000..a8e5e55
--- /dev/null
+++ b/examples/ping_pong_ns/stand_up_ping_pong
@@ -0,0 +1,232 @@
+#!/bin/bash
+
+# arguments
+if [ -z $1 ]
+then
+ echo "must supply ip for launchpad"
+ exit -1
+else
+ lp_ip=${1}
+fi
+
+username=$(whoami)
+
+# make sure we're in a rift shell
+if [ -z $RIFT_ROOT ]
+then
+ echo "must be in a rift-shell"
+ exit -1
+fi
+
+# make sure the system is up
+system_is_up() {
+ response=$(curl --silent --insecure \
+ -o /dev/null \
+ --write-out "%{http_code}"\
+ --user admin:admin \
+ https://${lp_ip}:8008/api/config/launchpad-config \
+ --request GET \
+ )
+
+ if [ ${response} -eq 200 ]
+ then
+ return 0
+ else
+ if [ ${response} -eq 404 ]
+ then
+ # not running a launchpad!
+ echo "riftware is running on ${lp_ip} but there is no launchpad-config"
+ exit -1
+ fi
+ return 1
+ fi
+}
+
+echo -n "wait for system"
+while ! system_is_up
+do
+ echo -n "."
+ sleep 5s
+done
+
+echo "system is up"
+
+# setup the openstack account
+echo -n "adding account"
+
+post_json_to_rwrest() {
+ if [ -z "$1" ]
+ then
+ echo "must supply url"
+ exit -1
+ else
+ url=$1
+ fi
+
+ if [ -z "$2" ]
+ then
+ echo "must supply payload"
+ exit -1
+ else
+ payload="${2}"
+ fi
+
+ response=$(curl --silent --insecure \
+ --header "content-type:application/vnd.yang.data+json" \
+ --header "Accept:application/vnd.yang.data+json" \
+ --user admin:admin \
+ https://${lp_ip}:8008${url} \
+ --request POST --data "${payload}" \
+ )
+
+ added_account=$(echo "${response}" | grep -e \"success|ok\" | wc -l)
+ already_exists=$(echo "${response}" | grep \"data-exists\" | wc -l)
+ success=$((added_account + already_exists))
+}
+
+account_payload=" {
+ \"account\": [
+ {
+ \"name\": \"OS\",
+ \"account-type\": \"openstack\",
+ \"openstack\": {
+ \"auth_url\": \"http://engstack.eng.riftio.com:5000/v3/\",
+ \"secret\": \"mypasswd\",
+ \"key\": \"${username}_automation\",
+ \"tenant\": \"${username}_automation\",
+ \"mgmt-network\": \"private\"
+ }
+ }
+ ]
+}"
+
+post_json_to_rwrest "/api/config/cloud/account" "${account_payload}"
+
+if [ ${success} -ne 1 ];
+then
+ echo -en "\r" # clear pending line
+ echo "failed to add cloud account:"
+ echo ${response}
+ exit 0
+else
+ echo " success"
+fi
+
+# onboard descriptors
+cd $RIFT_BUILD/modules/core/mano/src/core_mano-build/examples/ping_pong_ns
+
+wait_for_package() {
+ if [ -z "$1" ]
+ then
+ echo "must supply transaction id to wait for"
+ exit -1
+ fi
+
+ response=$(curl --silent --insecure https://${lp_ip}:4567/api/upload/${transaction_id}/state)
+ transaction_state=$(echo ${response} | awk -F "status" '{print $2}' | awk '{print $2}')
+ transaction_state=${transaction_state:1:-2}
+
+ if [ ${transaction_state} == "pending" ];
+ then
+ return 0
+ else
+ return 1
+ fi
+}
+
+upload_package() {
+ if [ -z "$1" ]
+ then
+ echo "must supply package to upload"
+ exit -1
+ else
+ package=$1
+ fi
+
+ echo -n "upload ${package} package"
+
+ response=$(curl --silent --insecure -F "descriptor=@${package}" https://${lp_ip}:4567/api/upload)
+ transaction_id=$(echo ${response} | awk '{print $2}')
+ transaction_id=${transaction_id:1:-2}
+
+ while wait_for_package transaction_id
+ do
+ echo -n "."
+ sleep 1s
+ done
+ if [ ${transaction_state} == "failure" ];
+ then
+ echo "failed"
+ status=1
+ else
+ echo "success"
+ status=0
+ fi
+
+}
+
+upload_package "ping_vnfd.tar.gz"
+ping_status=${status}
+upload_package "pong_vnfd.tar.gz"
+pong_status=${status}
+
+success=$((ping_status + pong_status))
+
+if [ ${success} -ne 0 ];
+then
+ echo -en "\r" # clear pending line
+ echo "cannot on-board nsd when a vnfd fails"
+ exit -1
+fi
+
+upload_package "ping_pong_nsd.tar.gz"
+if [ ${status} -ne 0 ];
+then
+ echo -en "\r" # clear pending line
+ echo "failed to on-board nsd"
+ exit -1
+fi
+
+# instantiate ping_pong_nsd
+echo "instantiate ping pong nsd"
+
+tag=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 4 | head -n 1)
+
+tmpdir="/tmp/${tag}"
+mkdir ${tmpdir}
+
+tar -xf ping_pong_nsd.tar.gz -C ${tmpdir}
+
+nsdfile="${tmpdir}/ping_pong_nsd/ping_pong_nsd.yaml"
+
+nsd_id=$(cat ${nsdfile} | grep "nsd:id" | head -n1 | awk '{print $2}')
+
+rm -r ${tmpdir}
+
+nsr_id=$(cat /proc/sys/kernel/random/uuid)
+nsd_payload="{
+ \"nsr\":[
+ {
+ \"id\":\"${nsr_id}\",
+ \"nsd-ref\":\"${nsd_id}\",
+ \"name\":\"${username}-${tag}-ping-pong-nsd\",
+ \"short-name\":\"pingpong\",
+ \"description\":\"ping pong nsd instantiated by ${username} with tag ${tag}\",
+ \"admin-status\":\"ENABLED\",
+ \"cloud-account\":\"OS\"
+ }
+ ]
+}"
+
+post_json_to_rwrest "/api/config/ns-instance-config" "${nsd_payload}"
+
+if [ ${success} -ne 1 ];
+then
+ echo -en "\r" # clear pending line
+ echo "failed to instantiate nsd:"
+ echo ${response}
+ exit -1
+else
+ echo " success"
+fi
+