From: Austin Cormier Date: Mon, 23 May 2016 23:12:00 +0000 (-0400) Subject: Initial Makefile for descriptor packages X-Git-Tag: v0.0~5 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=ab7952990b98453c4131f05999933cbfc00a7f57;p=osm%2Fdescriptor-packages.git Initial Makefile for descriptor packages --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b2211d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0a01862 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +BUILD_DIR = build + +NSDS := gw_corpa_ns ims_allin1_corpa mwc16_gen_ns mwc16_pe_ns +NSD_SRC_DIR := src/nsd +NSD_BUILD_DIR := $(BUILD_DIR)/nsd + +NSD_SRC_DIRS := $(addprefix $(NSD_SRC_DIR)/, $(NSDS)) +NSD_BUILD_DIRS := $(addprefix $(NSD_BUILD_DIR)/, $(NSDS)) +NSD_PKGS := $(addsuffix .tar.gz, $(NSDS)) +NSD_BUILD_PKGS := $(addprefix $(NSD_BUILD_DIR)_pkgs/, $(NSD_PKGS)) + +VNFDS := 6wind_vnf gw_corpa_pe1_vnf gw_corpa_pe2_vnf ims_allin1_2p_vnf tidgen_mwc16_vnf +VNFD_SRC_DIR := src/vnfd +VNFD_BUILD_DIR := $(BUILD_DIR)/vnfd + +VNFD_SRC_DIRS := $(addprefix $(VNFD_SRC_DIR)/, $(VNFDS)) +VNFD_BUILD_DIRS := $(addprefix $(VNFD_BUILD_DIR)/, $(VNFDS)) +VNFD_PKGS := $(addsuffix .tar.gz, $(VNFDS)) +VNFD_BUILD_PKGS := $(addprefix $(VNFD_BUILD_DIR)_pkgs/, $(VNFD_PKGS)) + +all: $(VNFD_BUILD_PKGS) ${NSD_BUILD_PKGS} + echo $@ + +clean: + -@ $(RM) -rf $(BUILD_DIR) + +$(VNFD_BUILD_DIR)/%: $(VNFD_SRC_DIR)/% + mkdir -p $(VNFD_BUILD_DIR) + cp -rf $< $(VNFD_BUILD_DIR) + + src/gen_vnfd_pkg.sh $< $@ + src/generate_descriptor_pkg.sh $(BUILD_DIR)/vnfd_pkgs $@ + +$(NSD_BUILD_DIR)/%: $(NSD_SRC_DIR)/% + mkdir -p $(NSD_BUILD_DIR) + cp -rf $< $(NSD_BUILD_DIR) + + src/gen_nsd_pkg.sh $< $@ + +$(BUILD_DIR)/nsd_pkgs/%.tar.gz: $(NSD_BUILD_DIR)/% + src/generate_descriptor_pkg.sh $(BUILD_DIR)/nsd_pkgs $< + +$(BUILD_DIR)/vnfd_pkgs/%.tar.gz: $(VNFD_BUILD_DIR)/% + src/generate_descriptor_pkg.sh $(BUILD_DIR)/vnfd_pkgs $< diff --git a/src/gen_nsd_pkg.sh b/src/gen_nsd_pkg.sh new file mode 100755 index 0000000..ea5e6a7 --- /dev/null +++ b/src/gen_nsd_pkg.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Generates a NSD descriptor package from a source directory +# Usage: +# gen_nsd_pkg.sh + +set -o nounset + +if [ $# -ne 2 ]; then + echo "Error: Must provide 2 parameters" >@2 + exit 1 +fi + +pkg_src_dir="$1" +pkg_dest_dir="$2" + +if [ ! -e ${pkg_src_dir} ]; then + echo "Error: ${pkg_src_dir} does not exist" + exit 1 +fi + +if [ ! -e ${pkg_dest_dir} ]; then + echo "Error: ${pkg_src_dir} does not exist" + exit 1 +fi + +echo "Generating package in directory: ${pkg_dest_dir}" + +# Create any missing directories/files so each package has +# a complete hierachy +nsd_dirs=( ns_config vnf_config icons scripts ) +nsd_files=( README ) + +nsd_dir="${pkg_src_dir}" +echo $(pwd) + +mkdir -p "${pkg_dest_dir}" +cp -rf ${nsd_dir}/* "${pkg_dest_dir}" +for sub_dir in ${nsd_dirs[@]}; do + dir_path=${pkg_dest_dir}/${sub_dir} + mkdir -p ${dir_path} +done + +for file in ${nsd_files[@]}; do + file_path=${pkg_dest_dir}/${file} + touch ${file_path} +done diff --git a/src/gen_vnfd_pkg.sh b/src/gen_vnfd_pkg.sh new file mode 100755 index 0000000..478d5c5 --- /dev/null +++ b/src/gen_vnfd_pkg.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Generates a NSD descriptor package from a source directory +# Usage: +# gen_vnfd_pkg.sh + +set -o nounset + +if [ $# -ne 2 ]; then + echo "Error: Must provide 2 parameters" >@2 + exit 1 +fi + +pkg_src_dir="$1" +pkg_dest_dir="$2" + +if [ ! -e ${pkg_src_dir} ]; then + echo "Error: ${pkg_src_dir} does not exist" + exit 1 +fi + +if [ ! -e ${pkg_dest_dir} ]; then + echo "Error: ${pkg_src_dir} does not exist" + exit 1 +fi + +echo "Generating package in directory: ${pkg_dest_dir}" + +# Create any missing directories/files so each package has +# a complete hierachy +vnfd_dirs=( charms icons scripts images ) +vnfd_files=( README ) + +vnfd_dir="${pkg_src_dir}" +echo $(pwd) + +mkdir -p "${pkg_dest_dir}" +cp -rf ${vnfd_dir}/* "${pkg_dest_dir}" +for sub_dir in ${vnfd_dirs[@]}; do + dir_path=${pkg_dest_dir}/${sub_dir} + mkdir -p ${dir_path} +done + +for file in ${vnfd_files[@]}; do + file_path=${pkg_dest_dir}/${file} + touch ${file_path} +done diff --git a/src/generate_descriptor_pkg.sh b/src/generate_descriptor_pkg.sh new file mode 100755 index 0000000..82ecda1 --- /dev/null +++ b/src/generate_descriptor_pkg.sh @@ -0,0 +1,27 @@ +#! /usr/bin/bash +# STANDARD_RIFT_IO_COPYRIGHT +# Author(s): Anil Gunturu +# Creation Date: 2015/10/09 +# +# This shell script is used to create a descriptor package +# The main functions of this script include: +# - Generate checksums.txt file +# - Generate a tar.gz file + +# Usage: generate_descriptor_pkg.sh + +mkdir -p $1 +mkdir -p $2 + +dest_dir=$(cd $1 && pwd) +pkg_dir=$(cd $2 && pwd) + +echo $(pwd) +cd ${pkg_dir} +rm -rf checksums.txt +find * -type f | + while read file; do + md5sum $file >> checksums.txt + done +cd .. +tar -zcvf ${dest_dir}/$(basename $pkg_dir).tar.gz $(basename $pkg_dir) diff --git a/src/nsd/gw_corpa_ns/README b/src/nsd/gw_corpa_ns/README new file mode 100644 index 0000000..e69de29 diff --git a/src/nsd/gw_corpa_ns/gwcorpA__nsd.yaml b/src/nsd/gw_corpa_ns/gwcorpA__nsd.yaml new file mode 100644 index 0000000..34caf2d --- /dev/null +++ b/src/nsd/gw_corpa_ns/gwcorpA__nsd.yaml @@ -0,0 +1,75 @@ +nsd:nsd-catalog: + nsd: + - id: gw_corpA + name: gw_corpA + short-name: gw_corpA + description: Gateways to access as corpA to PE1 and PE2 + logo: osm_2x.png + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: gw_corpA_PE1 + - member-vnf-index: '2' + vnfd-id-ref: gw_corpA_PE2 + vld: + - id: connection_0 + name: connection_0 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mgmt + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth1 + vnfd-id-ref: gw_corpA_PE1 + - id: connection_1 + name: connection_1 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mgmt + vnfd-connection-point-ref: + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: eth1 + vnfd-id-ref: gw_corpA_PE2 + - id: connection_2 + name: connection_2 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc1 + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth0 + vnfd-id-ref: gw_corpA_PE1 + - id: connection_3 + name: connection_3 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc2 + vnfd-connection-point-ref: + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: eth0 + vnfd-id-ref: gw_corpA_PE2 + - id: connection_4 + name: connection_4 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc16data1 + segmentation_id: '101' + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: gw_corpA_PE1 + - id: connection_5 + name: connection_5 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc16data2 + segmentation_id: '102' + vnfd-connection-point-ref: + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: gw_corpA_PE2 diff --git a/src/nsd/gw_corpa_ns/icons/osm_2x.png b/src/nsd/gw_corpa_ns/icons/osm_2x.png new file mode 100644 index 0000000..62012d2 Binary files /dev/null and b/src/nsd/gw_corpa_ns/icons/osm_2x.png differ diff --git a/src/nsd/ims_allin1_corpa/IMS-corpA__nsd.yaml b/src/nsd/ims_allin1_corpa/IMS-corpA__nsd.yaml new file mode 100644 index 0000000..27a1197 --- /dev/null +++ b/src/nsd/ims_allin1_corpa/IMS-corpA__nsd.yaml @@ -0,0 +1,57 @@ +nsd:nsd-catalog: + nsd: + - id: IMS-corpA + name: IMS-corpA + short-name: IMS-corpA + description: All in one Clearwater IMS for corporation A in MWC16 + logo: osm_2x.png + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: IMS-ALLIN1_2p + vld: + - id: data + name: data + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: net-corp + segmentation_id: '108' + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth0 + vnfd-id-ref: IMS-ALLIN1_2p + - id: management + name: management + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: net-mgmtOS + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth1 + vnfd-id-ref: IMS-ALLIN1_2p + config-primitive: + - name: Update Domain + vnf-primitive-group: + - member-vnf-index-ref: '1' + vnfd-id-ref: IMS-ALLIN1_2p + vnfd-name: cwims_vnfd + primitive: + - index: '1' + name: config + - name: Add User + vnf-primitive-group: + - member-vnf-index-ref: '1' + vnfd-id-ref: IMS-ALLIN1_2p + vnfd-name: cwims_vnfd + primitive: + - index: '1' + name: create-update-user + - name: Delete User + vnf-primitive-group: + - member-vnf-index-ref: '1' + vnfd-id-ref: IMS-ALLIN1_2p + vnfd-name: cwims_vnfd + primitive: + - index: '1' + name: delete-user diff --git a/src/nsd/ims_allin1_corpa/icons/osm_2x.png b/src/nsd/ims_allin1_corpa/icons/osm_2x.png new file mode 100644 index 0000000..62012d2 Binary files /dev/null and b/src/nsd/ims_allin1_corpa/icons/osm_2x.png differ diff --git a/src/nsd/ims_allin1_corpa/vnf_config/IMS-ALLIN1_2p__1.yaml b/src/nsd/ims_allin1_corpa/vnf_config/IMS-ALLIN1_2p__1.yaml new file mode 100644 index 0000000..33c5ef9 --- /dev/null +++ b/src/nsd/ims_allin1_corpa/vnf_config/IMS-ALLIN1_2p__1.yaml @@ -0,0 +1,4 @@ +initial_config_primitive: +- name: config + parameter: + proxied_ip: diff --git a/src/nsd/mwc16_gen_ns/icons/osm_2x.png b/src/nsd/mwc16_gen_ns/icons/osm_2x.png new file mode 100644 index 0000000..62012d2 Binary files /dev/null and b/src/nsd/mwc16_gen_ns/icons/osm_2x.png differ diff --git a/src/nsd/mwc16_gen_ns/mwc16-gen__nsd.yaml b/src/nsd/mwc16_gen_ns/mwc16-gen__nsd.yaml new file mode 100644 index 0000000..534b52a --- /dev/null +++ b/src/nsd/mwc16_gen_ns/mwc16-gen__nsd.yaml @@ -0,0 +1,53 @@ +nsd:nsd-catalog: + nsd: + - id: mwc16_traffic_generator + name: mwc16_traffic_generator + short-name: mwc16_traffic_generator + description: Traffic generator connected to the demo environment + logo: osm_2x.png + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: mwc16gen + vld: + - id: connection 0 + name: connection 0 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth0 + vnfd-id-ref: mwc16gen + - id: connection 1 + name: connection 1 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mgmt + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth1 + vnfd-id-ref: mwc16gen + - id: connection 2 + name: connection 2 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc16data1 + segmentation_id: '3000' + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: mwc16gen + - id: connection 3 + name: connection 3 + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc16data2 + segmentation_id: '3000' + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe2 + vnfd-id-ref: mwc16gen diff --git a/src/nsd/mwc16_pe_ns/icons/osm_2x.png b/src/nsd/mwc16_pe_ns/icons/osm_2x.png new file mode 100644 index 0000000..62012d2 Binary files /dev/null and b/src/nsd/mwc16_pe_ns/icons/osm_2x.png differ diff --git a/src/nsd/mwc16_pe_ns/mwc16-pe__nsd.yaml b/src/nsd/mwc16_pe_ns/mwc16-pe__nsd.yaml new file mode 100644 index 0000000..7a2d38a --- /dev/null +++ b/src/nsd/mwc16_pe_ns/mwc16-pe__nsd.yaml @@ -0,0 +1,247 @@ +nsd:nsd-catalog: + nsd: + - id: mwc16-pe + name: mwc16-pe + short-name: mwc16-pe + description: mwc16-pe + logo: osm_2x.png + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: 6WindTR1.1.2 + - member-vnf-index: '2' + vnfd-id-ref: 6WindTR1.1.2 + - member-vnf-index: '3' + vnfd-id-ref: 6WindTR1.1.2 + vld: + - id: 6WindTR1.1.2__3 to OpenStack + name: 6WindTR1.1.2__3 to OpenStack + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: interDC + vnfd-connection-point-ref: + - member-vnf-index-ref: '3' + vnfd-connection-point-ref: xe3 + vnfd-id-ref: 6WindTR1.1.2 + - id: 6WindTR1.1.2__1 enty point + name: 6WindTR1.1.2__1 enty point + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc16data1 + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe2 + vnfd-id-ref: 6WindTR1.1.2 + - id: 6WindTR1.1.2__2 entry point + name: 6WindTR1.1.2__2 entry point + type: ELAN + provider-network: + overlay-type: VLAN + physical-network: mwc16data2 + vnfd-connection-point-ref: + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe2 + vnfd-id-ref: 6WindTR1.1.2 + - id: management + name: management + provider-network: + overlay-type: VLAN + physical-network: mgmt + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth0 + vnfd-id-ref: 6WindTR1.1.2 + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: eth0 + vnfd-id-ref: 6WindTR1.1.2 + - member-vnf-index-ref: '3' + vnfd-connection-point-ref: eth0 + vnfd-id-ref: 6WindTR1.1.2 + - id: 6WindTR1.1.2__2-6WindTR1.1.2__3 + name: 6WindTR1.1.2__2-6WindTR1.1.2__3 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: 6WindTR1.1.2 + - member-vnf-index-ref: '3' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: 6WindTR1.1.2 + - id: 6WindTR1.1.2__1-6WindTR1.1.2__3 + name: 6WindTR1.1.2__1-6WindTR1.1.2__3 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: 6WindTR1.1.2 + - member-vnf-index-ref: '3' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: 6WindTR1.1.2 + - id: 6WindTR1.1.2__1-6WindTR1.1.2__2 + name: 6WindTR1.1.2__1-6WindTR1.1.2__2 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: 6WindTR1.1.2 + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: 6WindTR1.1.2 + config-primitive: + - name: Add SP Test Corporation + parameter: + - name: Corporation Name + data-type: string + default-value: SP Test Corp + mandatory: 'true' + - name: Tunnel Key + data-type: integer + default-value: '10' + hidden: 'true' + mandatory: 'true' + parameter-group: + - mandatory: 'false' + name: PE1 + parameter: + - name: Vlan ID + data-type: integer + default-value: '3000' + hidden: 'true' + mandatory: 'true' + - name: Interface Name + data-type: string + default-value: eth3 + hidden: 'true' + mandatory: 'true' + - name: Corp. Network + data-type: string + default-value: 10.0.1.0/24 + hidden: 'true' + mandatory: 'true' + - name: Corp. Gateway + data-type: string + default-value: 10.0.1.1 + hidden: 'true' + mandatory: 'true' + - mandatory: 'false' + name: PE2 + parameter: + - name: Vlan ID + data-type: integer + default-value: '3000' + hidden: 'true' + mandatory: 'true' + - name: Interface Name + data-type: string + default-value: eth3 + hidden: 'true' + mandatory: 'true' + - name: Corp. Network + data-type: string + default-value: 10.0.2.0/24 + hidden: 'true' + mandatory: 'true' + - name: Corp. Gateway + data-type: string + default-value: 10.0.2.1 + hidden: 'true' + mandatory: 'true' + - mandatory: 'false' + name: PE3 + parameter: + - name: Vlan ID + data-type: integer + default-value: '3000' + hidden: 'true' + mandatory: 'true' + - name: Interface Name + data-type: string + default-value: eth3 + hidden: 'true' + mandatory: 'true' + - name: Corp. Network + data-type: string + default-value: 10.0.3.0/24 + hidden: 'true' + mandatory: 'true' + - name: Corp. Gateway + data-type: string + default-value: 10.0.3.1 + hidden: 'true' + mandatory: 'true' + user-defined-script: add_corporation.py + - name: Add Corporation + parameter: + - name: Corporation Name + data-type: string + default-value: CorpA + mandatory: 'true' + - name: Tunnel Key + data-type: integer + default-value: '1' + hidden: 'true' + mandatory: 'true' + parameter-group: + - mandatory: 'false' + name: PE1 + parameter: + - name: Vlan ID + data-type: integer + default-value: '101' + mandatory: 'true' + read-only: 'true' + - name: Interface Name + data-type: string + default-value: eth3 + mandatory: 'true' + - name: Corp. Network + data-type: string + default-value: 10.0.1.0/24 + mandatory: 'true' + - name: Corp. Gateway + data-type: string + default-value: 10.0.1.1 + mandatory: 'true' + - mandatory: 'false' + name: PE2 + parameter: + - name: Vlan ID + data-type: integer + default-value: '102' + mandatory: 'true' + read-only: 'true' + - name: Interface Name + data-type: string + default-value: eth3 + mandatory: 'true' + - name: Corp. Network + data-type: string + default-value: 10.0.2.0/24 + mandatory: 'true' + - name: Corp. Gateway + data-type: string + default-value: 10.0.2.1 + mandatory: 'true' + - mandatory: 'false' + name: PE3 + parameter: + - name: Vlan ID + data-type: integer + default-value: '108' + mandatory: 'true' + read-only: 'true' + - name: Interface Name + data-type: string + default-value: eth4 + mandatory: 'true' + - name: Corp. Network + data-type: string + default-value: 10.0.4.0/24 + mandatory: 'true' + - name: Corp. Gateway + data-type: string + default-value: 10.0.4.1 + mandatory: 'true' + user-defined-script: add_corporation.py diff --git a/src/nsd/mwc16_pe_ns/ns_config/mwc16-pe.yaml b/src/nsd/mwc16_pe_ns/ns_config/mwc16-pe.yaml new file mode 100644 index 0000000..42276d2 --- /dev/null +++ b/src/nsd/mwc16_pe_ns/ns_config/mwc16-pe.yaml @@ -0,0 +1,40 @@ +Add Corporation: + parameter: + Tunnel Key: '1' + Corporation Name: 'CorpA' + parameter_group: + PE1: + Corp. Gateway: 10.0.1.1 + Corp. Network: 10.0.1.0/24 + Interface Name: eth3 + Vlan ID: '101' + PE2: + Corp. Gateway: 10.0.2.1 + Corp. Network: 10.0.2.0/24 + Interface Name: eth3 + Vlan ID: '102' + PE3: + Corp. Gateway: 10.0.4.1 + Corp. Network: 10.0.4.0/24 + Interface Name: eth4 + Vlan ID: '108' +Add SP Test Corporation: + parameter: + Tunnel Key: '10' + Corporation Name: 'SP Test Corp' + parameter_group: + PE1: + Corp. Gateway: 10.0.1.1 + Corp. Network: 10.0.1.0/24 + Interface Name: eth3 + Vlan ID: '3000' + PE2: + Corp. Gateway: 10.0.2.1 + Corp. Network: 10.0.2.0/24 + Interface Name: eth3 + Vlan ID: '3000' + PE3: + Corp. Gateway: 10.0.3.1 + Corp. Network: 10.0.3.0/24 + Interface Name: eth3 + Vlan ID: '3000' diff --git a/src/nsd/mwc16_pe_ns/scripts/add_corporation.py b/src/nsd/mwc16_pe_ns/scripts/add_corporation.py new file mode 100755 index 0000000..4b2bb4c --- /dev/null +++ b/src/nsd/mwc16_pe_ns/scripts/add_corporation.py @@ -0,0 +1,513 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import ipaddress +import itertools +import jujuclient +import logging +import sys +import time +import yaml + + +logging.basicConfig(filename="/tmp/rift_ns_add_corp.log", level=logging.DEBUG) +logger = logging.getLogger() + +ch = logging.StreamHandler() +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) + + +dry_run = False + +class JujuActionError(Exception): + pass + + +class JujuClient(object): + """Class for executing Juju actions """ + def __init__(self, ip, port, user, passwd): + self._ip = ip + self._port = port + self._user = user + self._passwd = passwd + + endpoint = 'wss://%s:%d' % (ip, port) + logger.debug("Using endpoint=%s", endpoint) + if dry_run: + return + self.env = jujuclient.Environment(endpoint) + self.env.login(passwd, user) + + def get_service(self, name): + return self.env.get_service(name) + + def _get_units(self, name): + """ + Get the units associated with service + """ + units = self.env.status(name)['Services'][name]['Units'] + units = list(units.keys()) + + # convert to a friendly format for juju-python-client + units[:] = [('unit-%s' % u).replace('/', '-') for u in units] + return units + + def exec_action(self, name, action_name, params, block=False): + logger.debug("execute actiion %s using params %s", action_name, params) + if dry_run: + return + + actions = jujuclient.Actions(self.env) + results = actions.enqueue_units(self._get_units(name), + action_name, + params) + if not block: + return results + + if 'error' in results['results'][0].keys(): + raise JujuActionError("Juju action error: %s" % results['results'][0]) + + action = results['results'][0]['action'] + info = actions.info([action]) + i = 0 + logging.debug("Initial action results: %s", results['results'][0]) + while info['results'][0]['status'] not in ['completed', 'failed']: + time.sleep(1) + info = actions.info([action]) + + # break out if the action doesn't complete in 10 secs + i += 1 + if i == 10: + raise JujuActionError("Juju action timed out after 30 seconds") + + if info['results'][0]['status'] != 'completed': + raise JujuActionError("Action %s failure: %s" % (action_name, info['results'][0])) + + return info + + +class CharmAction(object): + def __init__(self, deployed_name, action_name, action_params=None): + self._deployed_name = deployed_name + self._action_name = action_name + self._params = action_params if action_params is not None else [] + + def execute(self, juju_client): + logger.info("Executing charm (%s) action (%s) with params (%s)", + self._deployed_name, self._action_name, self._params) + try: + info = juju_client.exec_action( + name=self._deployed_name, + action_name=self._action_name, + params=self._params, + block=True + ) + + except JujuActionError as e: + logger.error("Juju charm (%s) action (%s) failed: %s", + self._deployed_name, self._action_name, str(e)) + raise + + logger.debug("Juju charm (%s) action (%s) success.", + self._deployed_name, self._action_name) + + +class DeployedProxyCharm(object): + def __init__(self, juju_client, service_name, mgmt_ip=None, charm_name=None): + self._juju_client = juju_client + self.service_name = service_name + self.mgmt_ip = mgmt_ip + self.charm_name = charm_name + + def do_action(self, action_name, action_params={}): + action = CharmAction(self.service_name, action_name, action_params) + action.execute(self._juju_client) + + +class SixWindPEProxyCharm(DeployedProxyCharm): + USER = "root" + PASSWD = "6windos" + + def configure_interface(self, iface_name, ipv4_interface_str=None): + action = "configure-interface" + params = {'iface-name', iface_name} + + if ipv4_interface_str is None: + # Use ipaddress module to validate ipv4 interface string + ip_intf = ipaddress.IPv4Interface(ipv4_interface_str) + params["cidr"] = ip_intf.with_prefixlen + + self.do_action(action, params) + else: + self.do_action(action, params) + + + def add_corporation(self, domain_name, user_iface_name, vlan_id, corp_gw, + corp_net, local_net="10.255.255.0/24", local_net_area="0"): + logger.debug("Add corporation called with params: %s", locals()) + + action = "add-corporation" + params = { + "domain-name": domain_name, + "iface-name": user_iface_name, + "vlan-id": int(vlan_id), + "cidr": corp_net, + "area": corp_gw, + "subnet-cidr":local_net, + "subnet-area":local_net_area, + } + + self.do_action(action, params) + + def connect_domains(self, domain_name, core_iface_name, local_ip, remote_ip, + internal_local_ip, internal_remote_ip, tunnel_name, + tunnel_key, tunnel_type="gre"): + + logger.debug("Connect domains called with params: %s", locals()) + + action = "connect-domains" + params = { + "domain-name": domain_name, + "iface-name": core_iface_name, + "tunnel-name": tunnel_name, + "local-ip": local_ip, + "remote-ip": remote_ip, + "tunnel-key": tunnel_key, + "internal-local-ip": internal_local_ip, + "internal-remote-ip": internal_remote_ip, + "tunnel-type":tunnel_type, + } + + self.do_action(action, params) + + +class PEGroupConfig(object): + def __init__(self, pe_group_cfg): + self._pe_group_cfg = pe_group_cfg + + def _get_param_value(self, param_name): + for param in self._pe_group_cfg["parameter"]: + if param["name"] == param_name: + return param["value"] + + raise ValueError("PE param not found: %s" % param_name) + + @property + def vlan_id(self): + return self._get_param_value("Vlan ID") + + @property + def interface_name(self): + return self._get_param_value("Interface Name") + + @property + def corp_network(self): + return self._get_param_value("Corp. Network") + + @property + def corp_gateway(self): + return self._get_param_value("Corp. Gateway") + + +class AddCorporationRequest(object): + def __init__(self, add_corporation_rpc): + self._add_corporation_rpc = add_corporation_rpc + + @property + def name(self): + return self._add_corporation_rpc["name"] + + @property + def param_groups(self): + return self._add_corporation_rpc["parameter_group"] + + @property + def params(self): + return self._add_corporation_rpc["parameter"] + + @property + def corporation_name(self): + for param in self.params: + if param["name"] == "Corporation Name": + return param["value"] + + raise ValueError("Could not find 'Corporation Name' field") + + @property + def tunnel_key(self): + for param in self.params: + if param["name"] == "Tunnel Key": + return param["value"] + + raise ValueError("Could not find 'Tunnel Key' field") + + def get_pe_parameter_group_map(self): + group_name_map = {} + for group in self.param_groups: + group_name_map[group["name"]] = group + + return group_name_map + + def get_parameter_name_map(self): + name_param_map = {} + for param in self.params: + name_param_map[param["name"]] = param + + return name_param_map + + @classmethod + def from_yaml_cfg(cls, yaml_hdl): + config = yaml.load(yaml_hdl) + return cls( + config["rpc_ip"], + ) + + +class JujuVNFConfig(object): + def __init__(self, vnfr_index_map, vnf_name_map, vnf_init_config_map): + self._vnfr_index_map = vnfr_index_map + self._vnf_name_map = vnf_name_map + self._vnf_init_config_map = vnf_name_map + + def get_service_name(self, vnf_index): + for vnfr_id, index in self._vnfr_index_map.items(): + if index != vnf_index: + continue + + return self._vnf_name_map[vnfr_id] + + raise ValueError("VNF Index not found: %s" % vnf_index) + + def get_vnfr_id(self, vnf_index): + for vnfr_id, index in self._vnfr_index_map.items(): + if index != vnf_index: + continue + + return vnfr_id + + raise ValueError("VNF Index not found: %s" % vnf_index) + + @classmethod + def from_yaml_cfg(cls, yaml_hdl): + config = yaml.load(yaml_hdl) + return cls( + config["vnfr_index_map"], + config["unit_names"], + config["init_config"], + ) + + +class JujuClientConfig(object): + def __init__(self, juju_ctrl_cfg): + self._juju_ctrl_cfg = juju_ctrl_cfg + + @property + def name(self): + return self._juju_ctrl_cfg["name"] + + @property + def host(self): + return self._juju_ctrl_cfg["host"] + + @property + def port(self): + return self._juju_ctrl_cfg["port"] + + @property + def user(self): + return self._juju_ctrl_cfg["user"] + + @property + def secret(self): + return self._juju_ctrl_cfg["secret"] + + @classmethod + def from_yaml_cfg(cls, yaml_hdl): + config = yaml.load(yaml_hdl) + return cls( + config["config_agent"], + ) + + +class OSM_MWC_Demo(object): + VNF_INDEX_NAME_MAP = { + "PE1": 1, + "PE2": 2, + "PE3": 3, + } + + CORE_PE_CONN_MAP = { + "PE1": { + "PE2": { + "ifacename": "eth1", + "ip": "10.10.10.9", + "mask": "30", + "internal_local_ip": "10.255.255.1" + }, + "PE3": { + "ifacename": "eth2", + "ip": "10.10.10.1", + "mask": "30", + "internal_local_ip": "10.255.255.1" + }, + }, + "PE2": { + "PE1": { + "ifacename": "eth1", + "ip": "10.10.10.10", + "mask": "30", + "internal_local_ip": "10.255.255.2" + }, + "PE3": { + "ifacename": "eth2", + "ip": "10.10.10.6", + "mask": "30", + "internal_local_ip": "10.255.255.2" + } + }, + "PE3": { + "PE1": { + "ifacename": "eth1", + "ip": "10.10.10.2", + "mask": "30", + "internal_local_ip": "10.255.255.3" + }, + "PE2": { + "ifacename": "eth2", + "ip": "10.10.10.5", + "mask": "30", + "internal_local_ip": "10.255.255.3" + } + } + } + + @staticmethod + def get_pe_vnf_index(pe_name): + if pe_name not in OSM_MWC_Demo.VNF_INDEX_NAME_MAP: + raise ValueError("Could not find PE name: %s", pe_name) + + return OSM_MWC_Demo.VNF_INDEX_NAME_MAP[pe_name] + + @staticmethod + def get_src_core_iface(src_pe_name, dest_pe_name): + return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["ifacename"] + + @staticmethod + def get_local_ip(src_pe_name, dest_pe_name): + return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["ip"] + + @staticmethod + def get_remote_ip(src_pe_name, dest_pe_name): + return OSM_MWC_Demo.CORE_PE_CONN_MAP[dest_pe_name][src_pe_name]["ip"] + + @staticmethod + def get_internal_local_ip(src_pe_name, dest_pe_name): + return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["internal_local_ip"] + + @staticmethod + def get_internal_remote_ip(src_pe_name, dest_pe_name): + return OSM_MWC_Demo.CORE_PE_CONN_MAP[dest_pe_name][src_pe_name]["internal_local_ip"] + + +def add_pe_corporation(src_pe_name, src_pe_charm, src_pe_group_cfg, corporation_name): + domain_name = corporation_name + vlan_id = src_pe_group_cfg.vlan_id + corp_gw = src_pe_group_cfg.corp_gateway + corp_net = src_pe_group_cfg.corp_network + + user_iface = src_pe_group_cfg.interface_name + + src_pe_charm.add_corporation(domain_name, user_iface, vlan_id, corp_gw, corp_net) + + +def connect_pe_domains(src_pe_name, src_pe_charm, dest_pe_name, corporation_name, tunnel_key): + domain_name = corporation_name + core_iface_name = OSM_MWC_Demo.get_src_core_iface(src_pe_name, dest_pe_name) + local_ip = OSM_MWC_Demo.get_local_ip(src_pe_name, dest_pe_name) + remote_ip = OSM_MWC_Demo.get_remote_ip(src_pe_name, dest_pe_name) + internal_local_ip = OSM_MWC_Demo.get_internal_local_ip(src_pe_name, dest_pe_name) + internal_remote_ip = OSM_MWC_Demo.get_internal_remote_ip(src_pe_name, dest_pe_name) + + + src_pe_idx = OSM_MWC_Demo.get_pe_vnf_index(src_pe_name) + dest_pe_idx = OSM_MWC_Demo.get_pe_vnf_index(dest_pe_name) + + # Create a 4 digit hash of the corporation name + hash_object = hashlib.md5(corporation_name.encode()) + corp_hash = hash_object.hexdigest()[-4:] + + # Tunnel name is the 4 digit corporation name hash followed by + # src index and dest index. When there are less than 10 PE's + # this creates a 8 character tunnel name which is the limit. + tunnel_name = "".join([corp_hash, "_", str(src_pe_idx), str(dest_pe_idx)]) + + src_pe_charm.connect_domains(domain_name, core_iface_name, local_ip, remote_ip, + internal_local_ip, internal_remote_ip, tunnel_name, + tunnel_key) + + +def main(argv=sys.argv[1:]): + 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() + if args.verbose: + ch.setLevel(logging.DEBUG) + + global dry_run + dry_run = args.dry_run + + yaml_str = args.yaml_cfg_file.read() + + juju_cfg = JujuClientConfig.from_yaml_cfg(yaml_str) + juju_client = JujuClient(juju_cfg.host, juju_cfg.port, juju_cfg.user, juju_cfg.secret) + + juju_vnf_config = JujuVNFConfig.from_yaml_cfg(yaml_str) + + rpc_request = AddCorporationRequest.from_yaml_cfg(yaml_str) + pe_param_group_map = rpc_request.get_pe_parameter_group_map() + + pe_name_charm_map = {} + for pe_name, pe_group_cfg in pe_param_group_map.items(): + # The PE name (i.e. PE1) must be in the parameter group name so we can correlate + # to an actual VNF in the descriptor. + pe_vnf_index = OSM_MWC_Demo.get_pe_vnf_index(pe_name) + + # Get the deployed VNFR charm service name + pe_charm_service_name = juju_vnf_config.get_service_name(pe_vnf_index) + + pe_name_charm_map[pe_name] = SixWindPEProxyCharm(juju_client, pe_charm_service_name) + + # At this point we have SixWindPEProxyCharm() instances for each PE and each + # PE param group configuration. + for src_pe_name in pe_param_group_map: + add_pe_corporation( + src_pe_name=src_pe_name, + src_pe_charm=pe_name_charm_map[src_pe_name], + src_pe_group_cfg=PEGroupConfig(pe_param_group_map[src_pe_name]), + corporation_name=rpc_request.corporation_name + ) + + # Create a permutation of all PE's involved in this topology and connect + # them together by creating tunnels with matching keys + for src_pe_name, dest_pe_name in itertools.permutations(pe_name_charm_map, 2): + connect_pe_domains( + src_pe_name=src_pe_name, + src_pe_charm=pe_name_charm_map[src_pe_name], + dest_pe_name=dest_pe_name, + corporation_name=rpc_request.corporation_name, + tunnel_key=rpc_request.tunnel_key, + ) + +if __name__ == "__main__": + try: + main() + except Exception as e: + logger.exception("Caught exception when executing add_corporation ns") + raise diff --git a/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__1.yaml b/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__1.yaml new file mode 100644 index 0000000..5edbeef --- /dev/null +++ b/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__1.yaml @@ -0,0 +1,18 @@ +initial_config_primitive: +- name: config + parameter: + hostname: pe1 + pass: 6windos + user: root + vpe-router: +- name: configure-interface + parameter: + cidr: 10.10.10.9/30 + iface-name: eth1 +- name: configure-interface + parameter: + cidr: 10.10.10.1/30 + iface-name: eth2 +- name: configure-interface + parameter: + iface-name: eth3 diff --git a/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__2.yaml b/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__2.yaml new file mode 100644 index 0000000..b96a023 --- /dev/null +++ b/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__2.yaml @@ -0,0 +1,18 @@ +initial_config_primitive: +- name: config + parameter: + hostname: pe2 + pass: 6windos + user: root + vpe-router: +- name: configure-interface + parameter: + cidr: 10.10.10.10/30 + iface-name: eth1 +- name: configure-interface + parameter: + cidr: 10.10.10.6/30 + iface-name: eth2 +- name: configure-interface + parameter: + iface-name: eth3 diff --git a/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__3.yaml b/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__3.yaml new file mode 100644 index 0000000..c0b2c98 --- /dev/null +++ b/src/nsd/mwc16_pe_ns/vnf_config/6WindTR1.1.2__3.yaml @@ -0,0 +1,21 @@ +initial_config_primitive: +- name: config + parameter: + hostname: pe3 + pass: 6windos + user: root + vpe-router: +- name: configure-interface + parameter: + cidr: 10.10.10.2/30 + iface-name: eth1 +- name: configure-interface + parameter: + cidr: 10.10.10.5/30 + iface-name: eth2 +- name: configure-interface + parameter: + iface-name: eth3 +- name: configure-interface + parameter: + iface-name: eth4 diff --git a/src/vnfd/6wind_vnf/6WindTR1.1.2__vnfd.yaml b/src/vnfd/6wind_vnf/6WindTR1.1.2__vnfd.yaml new file mode 100644 index 0000000..5b8bffc --- /dev/null +++ b/src/vnfd/6wind_vnf/6WindTR1.1.2__vnfd.yaml @@ -0,0 +1,183 @@ +vnfd:vnfd-catalog: + vnfd: + - id: 6WindTR1.1.2 + name: 6WindTR1.1.2 + short-name: 6WindTR1.1.2 + logo: 6wind_2x.png + mgmt-interface: + vdu-id: VM + vnf-configuration: + config-attributes: + config-delay: '0' + config-priority: '0' + config-primitive: + - name: configure-interface + parameter: + - name: iface-name + data-type: STRING + mandatory: 'true' + - name: cidr + data-type: STRING + - name: add-corporation + parameter: + - name: domain-name + data-type: STRING + mandatory: 'true' + - name: iface-name + data-type: STRING + mandatory: 'true' + - name: vlan-id + data-type: INTEGER + mandatory: 'true' + - name: cidr + data-type: STRING + mandatory: 'true' + - name: area + data-type: STRING + mandatory: 'true' + - name: subnet-cidr + data-type: STRING + mandatory: 'true' + - name: subnet-area + data-type: STRING + mandatory: 'true' + - name: delete-corporation + parameter: + - name: domain-name + data-type: STRING + mandatory: 'true' + - name: cidr + data-type: STRING + mandatory: 'true' + - name: area + data-type: STRING + mandatory: 'true' + - name: subnet-cidr + data-type: STRING + mandatory: 'true' + - name: subnet-area + data-type: STRING + mandatory: 'true' + - name: connect-domains + parameter: + - name: domain-name + data-type: STRING + mandatory: 'true' + - name: iface-name + data-type: STRING + mandatory: 'true' + - name: tunnel-name + data-type: STRING + mandatory: 'true' + - name: local-ip + data-type: STRING + mandatory: 'true' + - name: remote-ip + data-type: STRING + mandatory: 'true' + - name: tunnel-key + data-type: STRING + mandatory: 'true' + - name: internal-local-ip + data-type: STRING + mandatory: 'true' + - name: internal-remote-ip + data-type: STRING + mandatory: 'true' + - name: tunnel-type + data-type: STRING + mandatory: 'false' + default-value: 'gre' + - name: delete-domain-connection + parameter: + - name: domain-name + data-type: STRING + mandatory: 'true' + - name: tunnel-name + data-type: STRING + mandatory: 'true' + juju: + charm: vpe-router + connection-point: + - name: eth0 + type: VPORT + - name: xe0 + type: VPORT + - name: xe1 + type: VPORT + - name: xe2 + type: VPORT + - name: xe3 + type: VPORT + vdu: + - id: VM + name: VM + image: /mnt/powervault/virtualization/vnfs/6wind/6wind-turbo-router-1.1.2.img.qcow2 + mgmt-vpci: 0000:00:0a.0 + vm-flavor: + memory-mb: '8192' + vcpu-count: '12' + external-interface: + - name: eth0 + virtual-interface: + bandwidth: '1000000000' + type: OM-MGMT + vpci: '0000:00:03.0' + vnfd-connection-point-ref: eth0 + - name: xe0 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:05.0' + vnfd-connection-point-ref: xe0 + - name: xe1 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:06.0' + vnfd-connection-point-ref: xe1 + - name: xe2 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:07.0' + vnfd-connection-point-ref: xe2 + - name: xe3 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:08.0' + vnfd-connection-point-ref: xe3 + guest-epa: + cpu-pinning-policy: DEDICATED + cpu-thread-pinning-policy: PREFER + mempage-size: LARGE + numa-node-policy: + mem-policy: STRICT + node: + - id: '0' + paired-threads: + num-paired-threads: '6' + paired-thread-ids: + - thread-a: '0' + thread-b: '1' + - thread-a: '2' + thread-b: '3' + - thread-a: '4' + thread-b: '5' + - thread-a: '6' + thread-b: '7' + - thread-a: '8' + thread-b: '9' + - thread-a: '10' + thread-b: '11' + node-cnt: '1' + host-epa: + om-cpu-feature: + - 64b + - iommu + - lps + - tlbps + - hwsv + - dioc + - ht + om-cpu-model-string: Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz + hypervisor-epa: + type: REQUIRE_KVM + version: 10002|12001|2.6.32-358.el6.x86_64 diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/.build.manifest b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/.build.manifest new file mode 100644 index 0000000..41d6999 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/.build.manifest @@ -0,0 +1,219 @@ +{ + "layers": [ + "layer:basic", + "vpe-router", + "build" + ], + "signatures": { + "hooks/stop": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ], + "wheelhouse/ecdsa-0.13.tar.gz": [ + "vpe-router", + "dynamic", + "64cf1ee26d1cde3c73c6d7d107f835fed7c6a2904aef9eac223d57ad800c43fa" + ], + "wheelhouse/charms.reactive-0.3.8.tar.gz": [ + "layer:basic", + "dynamic", + "3f8722d85f7d489f8414d11fc2a3e8793c68000f7a1bc7b1ad71120e037aebee" + ], + "hooks/hook.template": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ], + "wheelhouse/pycrypto-2.6.1.tar.gz": [ + "vpe-router", + "dynamic", + "f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c" + ], + "hooks/start": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ], + "wheelhouse/pip-7.1.2.tar.gz": [ + "layer:basic", + "dynamic", + "ca047986f0528cfa975a14fb9f7f106271d4e0c3fe1ddced6c1db2e7ae57a477" + ], + "wheelhouse/PyYAML-3.11.tar.gz": [ + "layer:basic", + "dynamic", + "c36c938a872e5ff494938b33b14aaa156cb439ec67548fcab3535bb78b0846e8" + ], + "Makefile": [ + "layer:basic", + "static", + "f91213a68bc5edce9ebe0615b70cc908ea45466c2e205fb6cfe9c35d9c3fde4b" + ], + "reactive/__init__.py": [ + "layer:basic", + "static", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "wheelhouse/Jinja2-2.8.tar.gz": [ + "layer:basic", + "dynamic", + "bc1ff2ff88dbfacefde4ddde471d1417d3b304e8df103a7a9437d47269201bf4" + ], + "hooks/upgrade-charm": [ + "layer:basic", + "static", + "b78e405476402d34624c70822d9a60be2f4f85255765f619c0ecfe18f5f934ea" + ], + "actions/add-corporation": [ + "vpe-router", + "static", + "951055318724d05aa82fa9757143561ecf3617a3bd2eaebb08533ed1ae897ade" + ], + ".build.manifest": [ + "build", + "dynamic", + "unchecked" + ], + "wheelhouse/charmhelpers-0.6.1.tar.gz": [ + "layer:basic", + "dynamic", + "c41a4cb3dcf6aa35e115addf9fb83a94585a4ff3bddc63148983431af45905f8" + ], + "actions/delete-corporation": [ + "vpe-router", + "static", + "1e380d728790fa946e2429eaed31ff11aa4186cc287b818d8a91da7da291a6b3" + ], + "hooks/update-status": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ], + "hooks/leader-settings-changed": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ], + "wheelhouse/Tempita-0.5.2.tar.gz": [ + "layer:basic", + "dynamic", + "cacecf0baa674d356641f1d406b8bff1d756d739c46b869a54de515d08e6fc9c" + ], + "actions.yaml": [ + "vpe-router", + "static", + "619cedd685181c02ae36d04014bc4763c1e4e1b0a1b8a743d1a239c442f2883b" + ], + "README.md": [ + "layer:basic", + "static", + "5d5101eb0f2eb90eb0959438416ceb5e9b82c7746a385eb64ccb8a8ffe01e92b" + ], + "reactive/vpe_router.py": [ + "vpe-router", + "static", + "479874bbe5db71ddc0b3e4e0adab051540dbc8d30021800c42a1b058bf9dcd94" + ], + "tox.ini": [ + "layer:basic", + "static", + "5efb9280763f1f4cb861485e80863caafc9cd5ab1176543e911c27519436de7a" + ], + "metadata.yaml": [ + "vpe-router", + "dynamic", + "a7bf974efb4a29810de06626025fbc3b158053c70b0d67eb4b142a2ac087c5c0" + ], + "wheelhouse/pyaml-15.8.2.tar.gz": [ + "layer:basic", + "dynamic", + "9c54fb5f17b58572c4cef50affea60bb73f445ab153580dac07a12383712b5b8" + ], + "copyright": [ + "layer:basic", + "static", + "1e2afbd75c71affa132ae7ee3327cb29b5e4b9d9705f27dfd03857c326f50c5c" + ], + "requirements.txt": [ + "layer:basic", + "static", + "0f1c70d27e26005a96d66ad54482877ae20f7737693c833e29dd72bd6ac24892" + ], + "wheelhouse/netaddr-0.7.18.tar.gz": [ + "layer:basic", + "dynamic", + "a1f5c9fcf75ac2579b9995c843dade33009543c04f218ff7c007b3c81695bd19" + ], + "wheelhouse/paramiko-1.16.0.tar.gz": [ + "vpe-router", + "dynamic", + "3297ebd3cd072f573772f7c7426939a443c62c458d54bb632ff30fd6ecf96892" + ], + "actions/connect-domains": [ + "vpe-router", + "static", + "cdc11dd947a97b1e6ecb92b95e5e2b2676a5b1a366638a80b5fcf052b6fe240d" + ], + ".gitignore": [ + "layer:basic", + "static", + "0da5c4dcda27cd6406e5bb81cbf68ddccaf728ac764ec15053a165c1449d87d9" + ], + "lib/charms/router.py": [ + "vpe-router", + "static", + "b29712ab37799310107c99bb79ce90a991c5ebf95d513bad127b3fabd02df4a7" + ], + "hooks/install": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ], + "wheelhouse/MarkupSafe-0.23.tar.gz": [ + "layer:basic", + "dynamic", + "a4ec1aff59b95a14b45eb2e23761a0179e98319da5a7eb76b56ea8cdc7b871c3" + ], + "layer.yaml": [ + "vpe-router", + "dynamic", + "b6f7fe3a054fa4c8a17d4fa922ee2b0624f75cb39fdcc59c511cb55455425f8b" + ], + "config.yaml": [ + "vpe-router", + "dynamic", + "989e451c1dc464082f3e1122bd784362502af58680ad8e77b88ce00db0ec2246" + ], + "hooks/leader-elected": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ], + "lib/charms/bootstrap.py": [ + "layer:basic", + "static", + "bec7997003dbe44e9bbe85f0df598746c868fe72d1971a99d357bf3512453c70" + ], + "lib/charms/layer.py": [ + "layer:basic", + "static", + "3accb93272464875583f9b661dc024b4adc67617354bc21d8a7f74284ae4deb4" + ], + "actions/delete-domain-connection": [ + "vpe-router", + "static", + "0b59e146b4b0223f5593cd4bad9a829822713b10b2ccab46d07a531eb9e20216" + ], + "wheelhouse/six-1.10.0.tar.gz": [ + "layer:basic", + "dynamic", + "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a" + ], + "hooks/config-changed": [ + "layer:basic", + "static", + "21759be2af2e65c9e29531b293fd77fc1c710468ece35bc1cb4360cdefd997b0" + ] + } +} \ No newline at end of file diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/.gitignore b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/.gitignore new file mode 100644 index 0000000..56e95aa --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/.gitignore @@ -0,0 +1,5 @@ +*.pyc +*~ +.ropeproject +.settings +.tox diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/LICENSE b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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/src/vnfd/6wind_vnf/charms/trusty/vpe-router/Makefile b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/Makefile new file mode 100644 index 0000000..241e63b --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/Makefile @@ -0,0 +1,23 @@ +#!/usr/bin/make +PYTHON := /usr/bin/env python + +all: lint test build + + +build: unit_test + juju-compose -o ~/charms . + +lint: + @flake8 --exclude hooks/charmhelpers hooks unit_tests tests + @charm proof + +unit_test: + @echo Starting tests... + tox + +test: + @echo Starting Amulet tests... + # coreycb note: The -v should only be temporary until Amulet sends + # raise_status() messages to stderr: + # https://bugs.launchpad.net/amulet/+bug/1320357 + @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700 diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/README.md b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/README.md new file mode 100644 index 0000000..0550cbf --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/README.md @@ -0,0 +1,75 @@ +# Overview + +This is the base layer for all charms [built using layers][building]. It +provides all of the standard Juju hooks and runs the +[charms.reactive.main][charms.reactive] loop for them. It also bootstraps the +[charm-helpers][] and [charms.reactive][] libraries and all of their +dependencies for use by the charm. + +# Usage + +To create a charm layer using this base layer, you need only include it in +a `layer.yaml` file: + +```yaml +includes: ['layer:basic'] +``` + +This will fetch this layer from [interfaces.juju.solutions][] and incorporate +it into your charm layer. You can then add handlers under the `reactive/` +directory. Note that **any** file under `reactive/` will be expected to +contain handlers, whether as Python decorated functions or [executables][non-python] +using the [external handler protocol][]. + +You can also define Python libraries under `lib/charms/X` where `X` is a +package under the `charms.` namespace for your charm. See [PyPI][pypi charms.X] +for what packages already exist under the `charms.` namespace. + +# Hooks + +This layer provides hooks that other layers can react to using the decorators +of the [charms.reactive][] library: + + * `config-changed` + * `install` + * `leader-elected` + * `leader-settings-changed` + * `start` + * `stop` + * `upgrade-charm` + * `update-status` + +Other hooks are not implemented at this time. A new layer can implement storage +or relation hooks in their own layer by putting them in the `hooks` directory. + +**Note:** Because `update-status` is invoked every 5 minutes, you should take +care to ensure that your reactive handlers only invoke expensive operations +when absolutely necessary. It is recommended that you use helpers like +[`@only_once`][], [`@when_file_changed`][], and [`data_changed`][] to ensure +that handlers run only when necessary. + +# Layer Configuration + +This layer does not currently support any configuration. + + +# Reactive States + +This layer currently does not set any reactive states. + + +# Actions + +This layer currently does not define any actions. + + +[building]: https://jujucharms.com/docs/devel/authors-charm-building +[charm-helpers]: https://pythonhosted.org/charmhelpers/ +[charms.reactive]: https://pythonhosted.org/charms.reactive/ +[interfaces.juju.solutions]: http://interfaces.juju.solutions/ +[non-python]: https://pythonhosted.org/charms.reactive/#non-python-reactive-handlers +[external handler protocol]: https://pythonhosted.org/charms.reactive/charms.reactive.bus.html#charms.reactive.bus.ExternalHandler +[pypi charms.X]: https://pypi.python.org/pypi?%3Aaction=search&term=charms.&submit=search +[`@only_once`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.only_once +[`@when_file_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.when_file_changed +[`data_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.helpers.html#charms.reactive.helpers.data_changed diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions.yaml b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions.yaml new file mode 100644 index 0000000..913cc64 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions.yaml @@ -0,0 +1,96 @@ +configure-interface: + description: Configure an ethernet interface. + params: + iface-name: + type: string + description: Device name, e.g. eth1 + cidr: + type: string + description: Network range to assign to the interface + required: [iface-name] +add-corporation: + description: Add a new corporation to the router + params: + domain-name: + type: string + description: Name of the vlan corporation + iface-name: + type: string + description: Device name. eg eth1 + vlan-id: + type: integer + description: The name of the vlan? + cidr: + type: string + description: Network range to assign to the tagged vlan-id + area: + type: string + description: Link State Advertisements (LSA) type + subnet-cidr: + type: string + description: Network range + subnet-area: + type: string + description: Link State Advertisements (LSA) type + required: [domain-name, iface-name, vlan-id, cidr, area, subnet-cidr, subnet-area] +delete-corporation: + description: Remove the corporation from the router completely + params: + domain-name: + type: string + description: The domain of the corporation to remove + cidr: + type: string + description: Network range to assign to the tagged vlan-id + area: + type: string + description: Link State Advertisements (LSA) type + subnet-cidr: + type: string + description: Network range + subnet-area: + type: string + description: Link State Advertisements (LSA) type + required: [domain-name, cidr, area, subnet-cidr, subnet-area] +connect-domains: + description: Connect the router to another router, where the same domain is present + params: + domain-name: + type: string + description: The domain of the coproration to connect + iface-name: + type: string + description: Device name. eg eth1 + tunnel-name: + type: string + description: Name of the tunnel ? + local-ip: + type: string + description: local ip ? + remote-ip: + type: string + description: remote ip ? + tunnel-key: + type: string + description: tunnel key? + internal-local-ip: + type: string + description: internal local ip? + internal-remote-ip: + type: string + description: internal remote ip? + tunnel-type: + type: string + default: gre + description: The type of tunnel to establish. + required: [domain-name, iface-name, tunnel-name, local-ip, remote-ip, tunnel-key, internal-local-ip, internal-remote-ip] +delete-domain-connection: + description: Remove the tunnel to another router where the domain is present. + params: + domain-name: + type: string + description: The domain of the corporation to unlink + tunnel-name: + type: string + description: The name of the tunnel to unlink that the domain-name is attached to + required: [domain-name, tunnel-name] diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/add-corporation b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/add-corporation new file mode 100755 index 0000000..c8ab2f8 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/add-corporation @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +import sys +sys.path.append('lib') + +from charms.reactive import main +from charms.reactive import set_state +from charmhelpers.core.hookenv import action_fail + +""" +`set_state` only works here because it's flushed to disk inside the `main()` +loop. remove_state will need to be called inside the action method. +""" +set_state('vpe.add-corporation') + +try: + main() +except Exception as e: + action_fail(repr(e)) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/configure-interface b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/configure-interface new file mode 100755 index 0000000..db9a099 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/configure-interface @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +import sys +sys.path.append('lib') + +from charms.reactive import main +from charms.reactive import set_state +from charmhelpers.core.hookenv import action_fail + +""" +`set_state` only works here because it's flushed to disk inside the `main()` +loop. remove_state will need to be called inside the action method. +""" +set_state('vpe.configure-interface') + +try: + main() +except Exception as e: + action_fail(repr(e)) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/connect-domains b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/connect-domains new file mode 100755 index 0000000..48adfc7 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/connect-domains @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.reactive import main +from charms.reactive import set_state +from charmhelpers.core.hookenv import action_fail + +""" +`set_state` only works here because it's flushed to disk inside the `main()` +loop. remove_state will need to be called inside the action method. +""" +set_state('vpe.connect-domains') + +try: + main() +except Exception as e: + action_fail(repr(e)) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/delete-corporation b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/delete-corporation new file mode 100755 index 0000000..0576c08 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/delete-corporation @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +import sys +sys.path.append('lib') + +from charms.reactive import main +from charms.reactive import set_state +from charmhelpers.core.hookenv import action_fail + + +""" +`set_state` only works here because it's flushed to disk inside the `main()` +loop. remove_state will need to be called inside the action method. +""" +set_state('vpe.delete-corporation') + +try: + main() +except Exception as e: + action_fail(repr(e)) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/delete-domain-connection b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/delete-domain-connection new file mode 100755 index 0000000..5ba05f6 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/actions/delete-domain-connection @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +import sys +sys.path.append('lib') + +from charms.reactive import main +from charms.reactive import set_state +from charmhelpers.core.hookenv import action_fail + + +""" +`set_state` only works here because it's flushed to disk inside the `main()` +loop. remove_state will need to be called inside the action method. +""" +set_state('vpe.delete-domain-connection') + +try: + main() +except Exception as e: + action_fail(repr(e)) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/config.yaml b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/config.yaml new file mode 100644 index 0000000..562515f --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/config.yaml @@ -0,0 +1,17 @@ +options: + vpe-router: + default: + type: string + description: Hostname or IP of the vpe router to connect to + user: + type: string + default: root + description: Username for VPE Router + pass: + type: string + default: + description: Password for VPE Router + hostname: + type: string + default: + description: The hostname to set the vpe router to. diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/copyright b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/copyright new file mode 100644 index 0000000..afa853f --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/copyright @@ -0,0 +1,9 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0 + +Files: * +Copyright: 2015, Canonical Ltd. +License: GPL-3 + +License: GPL-3 + On Debian GNU/Linux system you can find the complete text of the + GPL-3 license in '/usr/share/common-licenses/GPL-3' diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/config-changed b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/config-changed new file mode 100755 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/config-changed @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/hook.template b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/hook.template new file mode 100644 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/hook.template @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/install b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/install new file mode 100755 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/install @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/leader-elected b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/leader-elected new file mode 100755 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/leader-elected @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/leader-settings-changed b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/leader-settings-changed new file mode 100755 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/leader-settings-changed @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/start b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/start new file mode 100755 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/start @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/stop b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/stop new file mode 100755 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/stop @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/update-status b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/update-status new file mode 100755 index 0000000..b74b146 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/update-status @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import sys +sys.path.append('lib') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/upgrade-charm b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/upgrade-charm new file mode 100755 index 0000000..41fc740 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/hooks/upgrade-charm @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +# Load modules from $CHARM_DIR/lib +import os +import sys +sys.path.append('lib') + +# This is an upgrade-charm context, make sure we install latest deps +if not os.path.exists('wheelhouse/.upgrade'): + open('wheelhouse/.upgrade', 'w').close() + if os.path.exists('wheelhouse/.bootstrapped'): + os.unlink('wheelhouse/.bootstrapped') +else: + os.unlink('wheelhouse/.upgrade') + +from charms.bootstrap import bootstrap_charm_deps +bootstrap_charm_deps() + + +# This will load and run the appropriate @hook and other decorated +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, +# and $CHARM_DIR/hooks/relations. +# +# See https://jujucharms.com/docs/stable/authors-charm-building +# for more information on this pattern. +from charms.reactive import main +main() diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/layer.yaml b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/layer.yaml new file mode 100644 index 0000000..ac6b1df --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/layer.yaml @@ -0,0 +1,3 @@ +includes: +- layer:basic +is: vpe-router diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/bootstrap.py b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/bootstrap.py new file mode 100644 index 0000000..bc07490 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/bootstrap.py @@ -0,0 +1,92 @@ +import os +import sys +import shutil +from glob import glob +from subprocess import check_call + + +def bootstrap_charm_deps(): + """ + Set up the base charm dependencies so that the reactive system can run. + """ + venv = os.path.abspath('../.venv') + vbin = os.path.join(venv, 'bin') + vpip = os.path.join(vbin, 'pip') + vpy = os.path.join(vbin, 'python') + if os.path.exists('wheelhouse/.bootstrapped'): + from charms import layer + cfg = layer.options('basic') + if cfg.get('use_venv') and '.venv' not in sys.executable: + # activate the venv + os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) + reload_interpreter(vpy) + return + # bootstrap wheelhouse + if os.path.exists('wheelhouse'): + apt_install(['python3-pip', 'python3-yaml']) + from charms import layer + cfg = layer.options('basic') + # include packages defined in layer.yaml + apt_install(cfg.get('packages', [])) + # if we're using a venv, set it up + if cfg.get('use_venv'): + apt_install(['python-virtualenv']) + cmd = ['virtualenv', '--python=python3', venv] + if cfg.get('include_system_packages'): + cmd.append('--system-site-packages') + check_call(cmd) + os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) + pip = vpip + else: + pip = 'pip3' + # save a copy of system pip to prevent `pip3 install -U pip` from changing it + if os.path.exists('/usr/bin/pip'): + shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') + # need newer pip, to fix spurious Double Requirement error https://github.com/pypa/pip/issues/56 + check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip']) + # install the rest of the wheelhouse deps + check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + glob('wheelhouse/*')) + if not cfg.get('use_venv'): + # restore system pip to prevent `pip3 install -U pip` from changing it + if os.path.exists('/usr/bin/pip.save'): + shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') + os.remove('/usr/bin/pip.save') + # flag us as having already bootstrapped so we don't do it again + open('wheelhouse/.bootstrapped', 'w').close() + # Ensure that the newly bootstrapped libs are available. + # Note: this only seems to be an issue with namespace packages. + # Non-namespace-package libs (e.g., charmhelpers) are available + # without having to reload the interpreter. :/ + reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0]) + + +def reload_interpreter(python): + """ + Reload the python interpreter to ensure that all deps are available. + + Newly installed modules in namespace packages sometimes seemt to + not be picked up by Python 3. + """ + os.execle(python, python, sys.argv[0], os.environ) + + +def apt_install(packages): + """ + Install apt packages. + + This ensures a consistent set of options that are often missed but + should really be set. + """ + if isinstance(packages, (str, bytes)): + packages = [packages] + + env = os.environ.copy() + + if 'DEBIAN_FRONTEND' not in env: + env['DEBIAN_FRONTEND'] = 'noninteractive' + + cmd = ['apt-get', + '--option=Dpkg::Options::=--force-confold', + '--assume-yes', + 'install'] + check_call(cmd + packages, env=env) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/layer.py b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/layer.py new file mode 100644 index 0000000..45552fd --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/layer.py @@ -0,0 +1,22 @@ + +import os +import yaml + + +class LayerOptions(dict): + def __init__(self, layer_file, section=None): + with open(layer_file) as f: + layer = yaml.safe_load(f.read()) + opts = layer.get('options', {}) + if section and section in opts: + super(LayerOptions, self).__init__(opts.get(section)) + else: + super(LayerOptions, self).__init__(opts) + + +def options(section=None, layer_file=None): + if not layer_file: + base_dir = os.environ.get('CHARM_DIR', os.getcwd()) + layer_file = os.path.join(base_dir, 'layer.yaml') + + return LayerOptions(layer_file, section) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/router.py b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/router.py new file mode 100644 index 0000000..54ff7fb --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/lib/charms/router.py @@ -0,0 +1,80 @@ + +import paramiko +import subprocess + +from charmhelpers.core.hookenv import config + + +class NetNS(object): + def __init__(self, name): + pass + + @classmethod + def create(cls, name): + # @TODO: Need to check if namespace exists already + try: + ip('netns', 'add', name) + except Exception as e: + raise Exception('could not create net namespace: %s' % e) + + return cls(name) + + def up(self, iface, cidr): + self.do('ip', 'link', 'set', 'dev', iface, 'up') + self.do('ip', 'address', 'add', cidr, 'dev', iface) + + def add_iface(self, iface): + ip('link', 'set', 'dev', iface, 'netns', self.name) + + def do(self, *cmd): + ip(*['netns', 'exec', self.name] + cmd) + + +def ip(*args): + return _run(['ip'] + list(args)) + + +def _run(cmd, env=None): + if isinstance(cmd, str): + cmd = cmd.split() if ' ' in cmd else [cmd] + + cfg = config() + if all(k in cfg for k in ['pass', 'vpe-router', 'user']): + router = cfg['vpe-router'] + user = cfg['user'] + passwd = cfg['pass'] + + if router and user and passwd: + return ssh(cmd, router, user, passwd) + + p = subprocess.Popen(cmd, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + retcode = p.poll() + if retcode > 0: + raise subprocess.CalledProcessError(returncode=retcode, + cmd=cmd, + output=stderr.decode("utf-8").strip()) + return (''.join(stdout), ''.join(stderr)) + + +def ssh(cmd, host, user, password=None): + ''' Suddenly this project needs to SSH to something. So we replicate what + _run was doing with subprocess using the Paramiko library. This is + temporary until this charm /is/ the VPE Router ''' + + cmds = ' '.join(cmd) + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + client.connect(host, port=22, username=user, password=password) + + stdin, stdout, stderr = client.exec_command(cmds) + retcode = stdout.channel.recv_exit_status() + client.close() # @TODO re-use connections + if retcode > 0: + output = stderr.read().strip() + raise subprocess.CalledProcessError(returncode=retcode, cmd=cmd, + output=output) + return (''.join(stdout), ''.join(stderr)) diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/metadata.yaml b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/metadata.yaml new file mode 100644 index 0000000..82789ad --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/metadata.yaml @@ -0,0 +1,11 @@ +name: vpe-router +summary: setup a virtualized PE Router with GRE tunnels +description: | + this charm, when deployed and configured, will provide a secure virtualized + provider edge router. +peers: + loadbalance: + interface: vpe-router +maintainers: +- Marco Ceppi +- Adam Israel diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/reactive/__init__.py b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/reactive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/reactive/vpe_router.py b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/reactive/vpe_router.py new file mode 100644 index 0000000..c62983d --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/reactive/vpe_router.py @@ -0,0 +1,639 @@ + +from charmhelpers.core.hookenv import ( + config, + status_set, + action_get, + action_fail, + log, +) + +from charms.reactive import ( + hook, + when, + when_not, + helpers, + set_state, + remove_state, +) + +from charms import router +import subprocess + +cfg = config() + + +@hook('config-changed') +def validate_config(): + try: + """ + If the ssh credentials are available, we'll act as a proxy charm. + Otherwise, we execute against the unit we're deployed on to. + """ + if all(k in cfg for k in ['pass', 'vpe-router', 'user']): + routerip = cfg['vpe-router'] + user = cfg['user'] + passwd = cfg['pass'] + + if routerip and user and passwd: + # Assumption: this will be a root user + out, err = router.ssh(['whoami'], routerip, + user, passwd) + if out.strip() != user: + raise Exception('invalid credentials') + + # Set the router's hostname + try: + if user == 'root' and 'hostname' in cfg: + hostname = cfg['hostname'] + out, err = router.ssh(['hostname', hostname], + routerip, + user, passwd) + out, err = router.ssh(['sed', + '-i', + '"s/hostname.*$/hostname %s/"' + % hostname, + '/usr/admin/global/hostname.sh' + ], + routerip, + user, passwd) + + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + raise + + set_state('vpe.configured') + status_set('active', 'ready!') + + except Exception as e: + log(repr(e)) + remove_state('vpe.configured') + status_set('blocked', 'validation failed: %s' % e) + + +@when_not('vpe.configured') +def not_ready_add(): + actions = [ + 'vpe.add-corporation', + 'vpe.connect-domains', + 'vpe.delete-domain-connections', + 'vpe.remove-corporation', + 'vpe.configure-interface', + 'vpe.configure-ospf', + ] + + if helpers.any_states(*actions): + action_fail('VPE is not configured') + + status_set('blocked', 'vpe is not configured') + + +def start_ospfd(): + # We may want to make this configurable via config setting + ospfd = '/usr/local/bin/ospfd' + + try: + (stdout, stderr) = router._run(['touch', + '/usr/admin/global/ospfd.conf']) + (stdout, stderr) = router._run([ospfd, '-d', '-f', + '/usr/admin/global/ospfd.conf']) + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + + +def configure_ospf(domain, cidr, area, subnet_cidr, subnet_area, enable=True): + """Configure the OSPF service""" + + # Check to see if the OSPF daemon is running, and start it if not + try: + (stdout, stderr) = router._run(['pgrep', 'ospfd']) + except subprocess.CalledProcessError as e: + # If pgrep fails, the process wasn't found. + start_ospfd() + log('Command failed (ospfd not running): %s (%s)' % + (' '.join(e.cmd), str(e.output))) + + upordown = '' + if not enable: + upordown = 'no' + try: + vrfctl = '/usr/local/bin/vrfctl' + vtysh = '/usr/local/bin/vtysh' + + (stdout, stderr) = router._run([vrfctl, 'list']) + + domain_id = 0 + for line in stdout.split('\n'): + if domain in line: + domain_id = int(line[3:5]) + + if domain_id > 0: + router._run([vtysh, + '-c', + '"configure terminal"', + '-c', + '"router ospf %d vr %d"' % (domain_id, domain_id), + '-c', + '"%s network %s area %s"' % (upordown, cidr, area), + '-c', + '"%s network %s area %s"' % (upordown, + subnet_cidr, + subnet_area), + ]) + + else: + log("Invalid domain id") + except subprocess.CalledProcessError as e: + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + finally: + remove_state('vpe.configure-interface') + status_set('active', 'ready!') + + +@when('vpe.configured') +@when('vpe.configure-interface') +def configure_interface(): + """ + Configure an ethernet interface + """ + iface_name = action_get('iface-name') + cidr = action_get('cidr') + + # cidr is optional + if cidr: + try: + # Add may fail, but change seems to add or update + router.ip('address', 'change', cidr, 'dev', iface_name) + except subprocess.CalledProcessError as e: + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + return + finally: + remove_state('vpe.configure-interface') + status_set('active', 'ready!') + + try: + router.ip('link', 'set', 'dev', iface_name, 'up') + except subprocess.CalledProcessError as e: + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + finally: + remove_state('vpe.configure-interface') + status_set('active', 'ready!') + + +@when('vpe.configured') +@when('vpe.add-corporation') +def add_corporation(): + ''' + Create and Activate the network corporation + ''' + domain_name = action_get('domain-name') + iface_name = action_get('iface-name') + # HACK: python's list, used deeper, throws an exception on ints in a tuple + vlan_id = str(action_get('vlan-id')) + cidr = action_get('cidr') + area = action_get('area') + subnet_cidr = action_get('subnet-cidr') + subnet_area = action_get('subnet-area') + + iface_vlanid = '%s.%s' % (iface_name, vlan_id) + + status_set('maintenance', 'adding corporation {}'.format(domain_name)) + + """ + Attempt to run all commands to add the network corporation. If any step + fails, abort and call `delete_corporation()` to undo. + """ + try: + """ + $ ip link add link eth3 name eth3.103 type vlan id 103 + """ + router.ip('link', + 'add', + 'link', + iface_name, + 'name', + iface_vlanid, + 'type', + 'vlan', + 'id', + vlan_id) + + """ + $ ip netns add domain + """ + router.ip('netns', + 'add', + domain_name) + + """ + $ ip link set dev eth3.103 netns corpB + """ + router.ip('link', + 'set', + 'dev', + iface_vlanid, + 'netns', + domain_name) + + """ + $ ifconfig eth3 up + """ + router._run(['ifconfig', iface_name, 'up']) + + """ + $ ip netns exec corpB ip link set dev eth3.103 up + """ + router.ip('netns', + 'exec', + domain_name, + 'ip', + 'link', + 'set', + 'dev', + iface_vlanid, + 'up') + + """ + $ ip netns exec corpB ip address add 10.0.1.1/24 dev eth3.103 + """ + mask = cidr.split("/")[1] + ip = '%s/%s' % (area, mask) + router.ip('netns', + 'exec', + domain_name, + 'ip', + 'address', + 'add', + ip, + 'dev', + iface_vlanid) + + configure_ospf(domain_name, cidr, area, subnet_cidr, subnet_area, True) + + except subprocess.CalledProcessError as e: + delete_corporation() + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + finally: + remove_state('vpe.add-corporation') + status_set('active', 'ready!') + + +@when('vpe.configured') +@when('vpe.delete-corporation') +def delete_corporation(): + + domain_name = action_get('domain-name') + cidr = action_get('cidr') + area = action_get('area') + subnet_cidr = action_get('subnet-cidr') + subnet_area = action_get('subnet-area') + + status_set('maintenance', 'deleting corporation {}'.format(domain_name)) + + try: + """ + Remove all tunnels defined for this domain + + $ ip netns exec domain_name ip tun show + | grep gre + | grep -v "remote any" + | cut -d":" -f1 + """ + p = router.ip( + 'netns', + 'exec', + domain_name, + 'ip', + 'tun', + 'show', + '|', + 'grep', + 'gre', + '|', + 'grep', + '-v', + '"remote any"', + '|', + 'cut -d":" -f1' + ) + + # `p` should be a tuple of (stdout, stderr) + tunnels = p[0].split('\n') + + for tunnel in tunnels: + try: + """ + $ ip netns exec domain_name ip link set $tunnel_name down + """ + router.ip( + 'netns', + 'exec', + domain_name, + 'ip', + 'link', + 'set', + tunnel, + 'down' + ) + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + pass + + try: + """ + $ ip netns exec domain_name ip tunnel del $tunnel_name + """ + router.ip( + 'netns', + 'exec', + domain_name, + 'ip', + 'tunnel', + 'del', + tunnel + ) + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + pass + + """ + Remove all interfaces associated to the domain + + $ ip netns exec domain_name ifconfig | grep mtu | cut -d":" -f1 + """ + p = router.ip( + 'netns', + 'exec', + domain_name, + 'ifconfig', + '|', + 'grep mtu', + '|', + 'cut -d":" -f1' + ) + + ifaces = p[0].split('\n') + for iface in ifaces: + + try: + """ + $ ip netns exec domain_name ip link set $iface down + """ + router.ip( + 'netns', + 'exec', + domain_name, + 'ip', + 'link', + 'set', + iface, + 'down' + ) + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + + try: + """ + $ ifconfig eth3 down + """ + router._run(['ifconfig', iface, 'down']) + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + pass + + try: + """ + $ ip link del dev $iface + """ + router.ip( + 'link', + 'del', + 'dev', + iface + ) + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + pass + + try: + """ + Remove the domain + + $ ip netns del domain_name + """ + router.ip( + 'netns', + 'del', + domain_name + ) + except subprocess.CalledProcessError as e: + log('Command failed: %s (%s)' % (' '.join(e.cmd), str(e.output))) + pass + + try: + configure_ospf(domain_name, + cidr, + area, + subnet_cidr, + subnet_area, + False) + except subprocess.CalledProcessError as e: + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + + except: + # Do nothing + log('delete-corporation failed.') + pass + + finally: + remove_state('vpe.delete-corporation') + status_set('active', 'ready!') + + +@when('vpe.configured') +@when('vpe.connect-domains') +def connect_domains(): + + params = [ + 'domain-name', + 'iface-name', + 'tunnel-name', + 'local-ip', + 'remote-ip', + 'tunnel-key', + 'internal-local-ip', + 'internal-remote-ip', + 'tunnel-type', + ] + + config = {} + for p in params: + config[p] = action_get(p) + + status_set('maintenance', 'connecting domains') + + try: + """ + $ ip tunnel add tunnel_name mode gre local local_ip remote remote_ip + dev iface_name key tunnel_key csum + """ + router.ip( + 'tunnel', + 'add', + config['tunnel-name'], + 'mode', + config['tunnel-type'], + 'local', + config['local-ip'], + 'remote', + config['remote-ip'], + 'dev', + config['iface-name'], + 'key', + config['tunnel-key'], + 'csum' + ) + + except subprocess.CalledProcessError as e: + log('Command failed (retrying with ip tunnel change): %s (%s)' % + (' '.join(e.cmd), str(e.output))) + try: + """ + If the tunnel already exists (like gre0) and can't be deleted, + modify it instead of trying to add it. + """ + router.ip( + 'tunnel', + 'change', + config['tunnel-name'], + 'mode', + config['tunnel-type'], + 'local', + config['local-ip'], + 'remote', + config['remote-ip'], + 'dev', + config['iface-name'], + 'key', + config['tunnel-key'], + 'csum' + ) + except subprocess.CalledProcessError as e: + delete_domain_connection() + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + finally: + remove_state('vpe.connect-domains') + status_set('active', 'ready!') + + try: + """ + $ ip link set dev tunnel_name netns domain_name + """ + router.ip( + 'link', + 'set', + 'dev', + config['tunnel-name'], + 'netns', + config['domain-name'] + ) + + """ + $ ip netns exec domain_name ip link set dev tunnel_name up + """ + router.ip( + 'netns', + 'exec', + config['domain-name'], + 'ip', + 'link', + 'set', + 'dev', + config['tunnel-name'], + 'up' + ) + + """ + $ ip netns exec domain_name ip address add internal_local_ip peer + internal_remote_ip dev tunnel_name + """ + router.ip( + 'netns', + 'exec', + config['domain-name'], + 'ip', + 'address', + 'add', + config['internal-local-ip'], + 'peer', + config['internal-remote-ip'], + 'dev', + config['tunnel-name'] + ) + except subprocess.CalledProcessError as e: + delete_domain_connection() + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + finally: + remove_state('vpe.connect-domains') + status_set('active', 'ready!') + + +@when('vpe.configured') +@when('vpe.delete-domain-connection') +def delete_domain_connection(): + ''' Remove the tunnel to another router where the domain is present ''' + domain = action_get('domain-name') + tunnel_name = action_get('tunnel-name') + + status_set('maintenance', 'deleting domain connection: {}'.format(domain)) + + try: + + try: + """ + $ ip netns exec domain_name ip link set tunnel_name down + """ + router.ip('netns', + 'exec', + domain, + 'ip', + 'link', + 'set', + tunnel_name, + 'down') + except subprocess.CalledProcessError as e: + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + + try: + """ + $ ip netns exec domain_name ip tunnel del tunnel_name + """ + router.ip('netns', + 'exec', + domain, + 'ip', + 'tunnel', + 'del', + tunnel_name) + except subprocess.CalledProcessError as e: + action_fail('Command failed: %s (%s)' % + (' '.join(e.cmd), str(e.output))) + except: + pass + finally: + remove_state('vpe.delete-domain-connection') + status_set('active', 'ready!') diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/requirements.txt b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/requirements.txt new file mode 100644 index 0000000..28ecaca --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/requirements.txt @@ -0,0 +1,2 @@ +flake8 +pytest diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/tox.ini b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/tox.ini new file mode 100644 index 0000000..cc7cf78 --- /dev/null +++ b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/tox.ini @@ -0,0 +1,8 @@ +[tox] +skipsdist=True +envlist = py34 + +[testenv] +commands = py.test -v +deps = + -r{toxinidir}/requirements.txt diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/Jinja2-2.8.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/Jinja2-2.8.tar.gz new file mode 100644 index 0000000..9c38426 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/Jinja2-2.8.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/MarkupSafe-0.23.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/MarkupSafe-0.23.tar.gz new file mode 100644 index 0000000..6b19006 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/MarkupSafe-0.23.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/PyYAML-3.11.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/PyYAML-3.11.tar.gz new file mode 100644 index 0000000..2a5d431 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/PyYAML-3.11.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/Tempita-0.5.2.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/Tempita-0.5.2.tar.gz new file mode 100644 index 0000000..755befc Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/Tempita-0.5.2.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charmhelpers-0.6.1.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charmhelpers-0.6.1.tar.gz new file mode 100644 index 0000000..5255319 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charmhelpers-0.6.1.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charmhelpers-0.6.2.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charmhelpers-0.6.2.tar.gz new file mode 100644 index 0000000..9192919 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charmhelpers-0.6.2.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charms.reactive-0.3.8.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charms.reactive-0.3.8.tar.gz new file mode 100644 index 0000000..c7ed6ed Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/charms.reactive-0.3.8.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/ecdsa-0.13.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/ecdsa-0.13.tar.gz new file mode 100644 index 0000000..3709762 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/ecdsa-0.13.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/netaddr-0.7.18.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/netaddr-0.7.18.tar.gz new file mode 100644 index 0000000..0df6b47 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/netaddr-0.7.18.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/paramiko-1.16.0.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/paramiko-1.16.0.tar.gz new file mode 100644 index 0000000..88850e6 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/paramiko-1.16.0.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pip-7.1.2.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pip-7.1.2.tar.gz new file mode 100644 index 0000000..56ead41 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pip-7.1.2.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pyaml-15.8.2.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pyaml-15.8.2.tar.gz new file mode 100644 index 0000000..3c49aaf Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pyaml-15.8.2.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pycrypto-2.6.1.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pycrypto-2.6.1.tar.gz new file mode 100644 index 0000000..e6bf62c Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/pycrypto-2.6.1.tar.gz differ diff --git a/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/six-1.10.0.tar.gz b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/six-1.10.0.tar.gz new file mode 100644 index 0000000..ac8eec5 Binary files /dev/null and b/src/vnfd/6wind_vnf/charms/trusty/vpe-router/wheelhouse/six-1.10.0.tar.gz differ diff --git a/src/vnfd/6wind_vnf/icons/6wind_2x.png b/src/vnfd/6wind_vnf/icons/6wind_2x.png new file mode 100644 index 0000000..64e5054 Binary files /dev/null and b/src/vnfd/6wind_vnf/icons/6wind_2x.png differ diff --git a/src/vnfd/gw_corpa_pe1_vnf/gw-corpa-pe1__vnfd.yaml b/src/vnfd/gw_corpa_pe1_vnf/gw-corpa-pe1__vnfd.yaml new file mode 100644 index 0000000..416bcc4 --- /dev/null +++ b/src/vnfd/gw_corpa_pe1_vnf/gw-corpa-pe1__vnfd.yaml @@ -0,0 +1,55 @@ +vnfd:vnfd-catalog: + vnfd: + - id: gw_corpA_PE1 + name: gw_corpA_PE1 + short-name: gw_corpA_PE1 + description: gw_corpA_PE1 + connection-point: + - name: eth0 + type: VPORT + - name: eth1 + type: VPORT + - name: xe0 + type: VPORT + mgmt-interface: + vdu-id: gw_corpA_PE1-VM + vdu: + - id: gw_corpA_PE1-VM + name: gw_corpA_PE1-VM + description: gw_corpA_PE1-VM + image: /mnt/powervault/virtualization/vnfs/demos/mwc2016/gw_corpA_PE1.qcow2 + mgmt-vpci: 0000:00:0a.0 + vm-flavor: + vcpu-count: '2' + memory-mb: '4096' + storage-gb: '10' + external-interface: + - name: eth0 + virtual-interface: + bandwidth: '0' + type: VIRTIO + vpci: 0000:00:0a.0 + vnfd-connection-point-ref: eth0 + - name: eth1 + virtual-interface: + bandwidth: '0' + type: OM-MGMT + vpci: 0000:00:0b.0 + vnfd-connection-point-ref: eth1 + - name: xe0 + virtual-interface: + bandwidth: '10000000000' + type: PCI-PASSTHROUGH + vpci: '0000:00:10.0' + vnfd-connection-point-ref: xe0 + guest-epa: + cpu-pinning-policy: DEDICATED + cpu-thread-pinning-policy: PREFER + mempage-size: LARGE + numa-node-policy: + mem-policy: STRICT + node: + - id: '0' + paired-threads: + num-paired-threads: '1' + node-cnt: '1' diff --git a/src/vnfd/gw_corpa_pe2_vnf/gw-corpa-pe2__vnfd.yaml b/src/vnfd/gw_corpa_pe2_vnf/gw-corpa-pe2__vnfd.yaml new file mode 100644 index 0000000..cf43bf2 --- /dev/null +++ b/src/vnfd/gw_corpa_pe2_vnf/gw-corpa-pe2__vnfd.yaml @@ -0,0 +1,55 @@ +vnfd:vnfd-catalog: + vnfd: + - id: gw_corpA_PE2 + name: gw_corpA_PE2 + short-name: gw_corpA_PE2 + description: gw_corpA_PE2 + mgmt-interface: + vdu-id: gw_corpA_PE2-VM + connection-point: + - name: eth0 + type: VPORT + - name: eth1 + type: VPORT + - name: xe0 + type: VPORT + vdu: + - id: gw_corpA_PE2-VM + name: gw_corpA_PE2-VM + description: gw_corpA_PE2-VM + image: /mnt/powervault/virtualization/vnfs/demos/mwc2016/gw_corpA_PE2.qcow2 + mgmt-vpci: 0000:00:0a.0 + vm-flavor: + memory-mb: '4096' + storage-gb: '10' + vcpu-count: '2' + external-interface: + - name: eth0 + virtual-interface: + bandwidth: '0' + type: VIRTIO + vpci: 0000:00:0a.0 + vnfd-connection-point-ref: eth0 + - name: eth1 + virtual-interface: + bandwidth: '0' + type: OM-MGMT + vpci: 0000:00:0b.0 + vnfd-connection-point-ref: eth1 + - name: xe0 + virtual-interface: + bandwidth: '10000000000' + type: PCI-PASSTHROUGH + vpci: '0000:00:10.0' + vnfd-connection-point-ref: xe0 + guest-epa: + cpu-pinning-policy: DEDICATED + cpu-thread-pinning-policy: PREFER + mempage-size: LARGE + numa-node-policy: + mem-policy: STRICT + node: + - id: '0' + paired-threads: + num-paired-threads: '1' + node-cnt: '1' diff --git a/src/vnfd/ims_allin1_2p_vnf/IMS-ALLIN1__vnfd.yaml b/src/vnfd/ims_allin1_2p_vnf/IMS-ALLIN1__vnfd.yaml new file mode 100644 index 0000000..b36941a --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/IMS-ALLIN1__vnfd.yaml @@ -0,0 +1,85 @@ +vnfd:vnfd-catalog: + vnfd: + - id: IMS-ALLIN1_2p + name: IMS-ALLIN1_2p + short-name: IMS-ALLIN1_2p + description: IMS-ALLIN1_2p + logo: metaswitch_2x.png + mgmt-interface: + vdu-id: IMS-ALLIN1_2p-VM + vnf-configuration: + config-attributes: + config-delay: '0' + config-priority: '1' + config-primitive: + - name: config + parameter: + - name: home_domain + data-type: STRING + mandatory: 'true' + default-value: ims.com + - name: password + data-type: string + mandatory: 'true' + name: password + default-value: cw-aio + - name: create-update-user + parameter: + - name: number + data-type: STRING + mandatory: 'true' + - name: password + data-type: STRING + mandatory: 'true' + - name: delete-user + parameter: + - name: number + data-type: STRING + mandatory: 'true' + initial-config-primitive: + - name: config + parameter: + - name: proxied_ip + value: + seq: '1' + juju: + charm: clearwater-aio-proxy + connection-point: + - name: eth0 + type: VPORT + - name: eth1 + type: VPORT + vdu: + - id: IMS-ALLIN1_2p-VM + name: IMS-ALLIN1_2p-VM + description: IMS-ALLIN1_2p-VM + image: /mnt/powervault/virtualization/vnfs/demos/mwc2016/allin1.qcow2 + vm-flavor: + memory-mb: '4096' + storage-gb: '10' + vcpu-count: '2' + mgmt-vpci: 0000:00:0a.0 + external-interface: + - name: eth0 + virtual-interface: + bandwidth: '0' + type: VIRTIO + vpci: 0000:00:0a.0 + vnfd-connection-point-ref: eth0 + - name: eth1 + virtual-interface: + bandwidth: '0' + type: OM-MGMT + vpci: 0000:00:0b.0 + vnfd-connection-point-ref: eth1 + guest-epa: + cpu-pinning-policy: DEDICATED + cpu-thread-pinning-policy: PREFER + mempage-size: LARGE + numa-node-policy: + mem-policy: STRICT + node: + - id: '0' + paired-threads: + num-paired-threads: '1' + node-cnt: '1' diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/README.md b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/README.md new file mode 100644 index 0000000..c67a763 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/README.md @@ -0,0 +1,53 @@ +# Overview + +This is a [Juju charm](https://jujucharms.com/about), which allows configuration of the [Project Clearwater](http://projectclearwater.org) IMS core's [all-in-one](http://clearwater.readthedocs.org/en/stable/All_in_one_Images/index.html) node. + +This is a proxy charm, meaning that you must spin up the all-in-one VM first, and then point this charm at it to manage it. + +Since the all-in-one node does not support scaling up, neither does this charm. + +# Deployment + +## Initial deployment + +The all-in-one VM image should be downloaded from [http://repo.cw-ngv.com/juju-clearwater-2/cw-aio.ova](http://repo.cw-ngv.com/juju-clearwater-2/cw-aio.ova) and deployed onto your virtualization platform. (You could alternatively try the latest all-in-one VM image from [http://vm-images.cw-ngv.com/](http://vm-images.cw-ngv.com/), but this may not have been tested with this charm - the juju-clearwater-2 version above is known to work.) + +The proxy charm should then be deployed, pointing at the all-in-one VM. + +# Using the All-in-One Node + +Once installed, the all-in-one node will listen for SIP traffic on port 5060 (both TCP and UDP). You can use a standard SIP client (e.g. Blink, Boghe or X-Lite) to register against the all-in-one VM's public IP and make calls. + +Our ["Making your first call" documentation](http://clearwater.readthedocs.org/en/latest/Making_your_first_call/index.html) has more information on this process. + +# Configuration + +- `proxied_ip`: The IP address of the All-in-One node to manage +- `password`: The login password of the All-in-One node to manage (default is + very likely correct) +- `home_domain`: The home domain for this service +- `base_number`: The first number to be allocated in the number range +- `number_count`: The count of numbers to allocate + +# Actions + +This proxy charm exposes two actions. + +- `create-update-user`: Creates a user, or updates if they already exist + - `number`: The number to provision + - `password`: The number's password + +- `delete-user`: Deletes a user + - `number`: The number to delete + +For example, `juju action do clearwater-aio-proxy/0 create-update-user number=\"1234567890\" password=secret` creates a user. (Note that the escaped double-quotes are required to avoid juju parsing the number as an integer rather than a string.) + +Note that the numbers specified in `create-update-user` and `delete-user` actions need not be in the number range specified in the configuration above. + +# Contact and Upstream Project Information + +Project Clearwater is an open-source IMS core, developed by [Metaswitch Networks](http://www.metaswitch.com) and released under the [GNU GPLv3](http://www.projectclearwater.org/download/license/). You can find more information about it on [our website](http://www.projectclearwater.org/) or [our documentation site](https://clearwater.readthedocs.org). + +Clearwater source code and issue list can be found at https://github.com/Metaswitch/. + +If you have problems when using Project Clearwater, read [our troubleshooting documentation](http://clearwater.readthedocs.org/en/latest/Troubleshooting_and_Recovery/index.html) for help, or see [our support page](http://clearwater.readthedocs.org/en/latest/Support/index.html) to find out how to ask mailing list questions or raise issues. diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions.yaml b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions.yaml new file mode 100644 index 0000000..424ae89 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions.yaml @@ -0,0 +1,19 @@ +create-update-user: + description: Create a user, or update a user if they already exist. + params: + number: + description: The number to provision + type: string + password: + description: The number's password + type: string + required: [number, password] + additionalProperties: false +delete-user: + description: Delete a user. If the user does not exist, this is still considered success. + params: + number: + description: The number to provision + type: string + required: [number] + additionalProperties: false diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/create-update-user b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/create-update-user new file mode 100755 index 0000000..1f388fe --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/create-update-user @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +# Get the configuration and action parameters. +proxied_ip=$(config-get proxied_ip) +login_password=$(config-get password) +home_domain=$(config-get home_domain) +number=$(action-get number) +password=$(action-get password) + +if [ -z "$proxied_ip" ] || [ -z "$login_password" ] || [ -z "$home_domain" ] ; then + echo Proxy not yet configured! + exit 1 +fi + +# If the user doesn't exist, try to create them. Otherwise, try to update them. +if ! sshpass -p$login_password ssh -o StrictHostKeyChecking=no ubuntu@$proxied_ip "echo $login_password | sudo -S /usr/share/clearwater/bin/display_user $number $home_domain" ; then + echo "Subscriber doesn't exist - creating" + sshpass -p$login_password ssh -o StrictHostKeyChecking=no ubuntu@$proxied_ip "echo $login_password | sudo -S /usr/share/clearwater/bin/create_user $number $home_domain $password" +else + echo "Subscriber exists - updating" + sshpass -p$login_password ssh -o StrictHostKeyChecking=no ubuntu@$proxied_ip "echo $login_password | sudo -S /usr/share/clearwater/bin/update_user $number $home_domain --password $password" +fi diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/create-user b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/create-user new file mode 100755 index 0000000..feeb646 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/create-user @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +# Get the configuration and action parameters. +proxied_ip=$(config-get proxied_ip) +login_password=$(config-get password) +home_domain=$(config-get home_domain) +number=$(action-get number) +password=$(action-get password) + +if [ -z "$proxied_ip" ] || [ -z "$login_password" ] || [ -z "$home_domain" ] ; then + echo Proxy not yet configured! + exit 1 +fi + +# Create the user. +sshpass -p$login_password ssh -o StrictHostKeyChecking=no ubuntu@$proxied_ip "echo $login_password | sudo -S /usr/share/clearwater/bin/create_user $number $home_domain $password" diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/delete-user b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/delete-user new file mode 100755 index 0000000..7ecf519 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/actions/delete-user @@ -0,0 +1,16 @@ +#!/bin/bash +set -e + +# Get the configuration and action parameters. +proxied_ip=$(config-get proxied_ip) +login_password=$(config-get password) +home_domain=$(config-get home_domain) +number=$(action-get number) + +if [ -z "$proxied_ip" ] || [ -z "$login_password" ] || [ -z "$home_domain" ] ; then + echo Proxy not yet configured! + exit 1 +fi + +# Delete the user. +sshpass -p$login_password ssh -o StrictHostKeyChecking=no ubuntu@$proxied_ip "echo $login_password | sudo -S /usr/share/clearwater/bin/delete_user -y $number $home_domain" diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/config.yaml b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/config.yaml new file mode 100644 index 0000000..3be783f --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/config.yaml @@ -0,0 +1,20 @@ +options: + proxied_ip: + description: The IP address of the All-in-One node to manage + type: string + password: + default: cw-aio + description: The login password of the All-in-One node to manage (default is very likely correct) + type: string + home_domain: + default: example.com + description: The home domain for this service + type: string + base_number: + default: "1230000000" + description: The first number to be allocated in the number range + type: string + number_count: + default: 1000 + description: The count of numbers to allocate + type: int diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/copyright b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/copyright new file mode 100644 index 0000000..39f1ec8 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/copyright @@ -0,0 +1,31 @@ +Project Clearwater - IMS in the Cloud +Copyright (C) 2016 Metaswitch Networks Ltd + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version, along with the "Special Exception" for use of +the program along with SSL, set forth below. This program is distributed +in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more +details. You should have received a copy of the GNU General Public +License along with this program. If not, see +. + +The author can be reached by email at clearwater@metaswitch.com or by +post at Metaswitch Networks Ltd, 100 Church St, Enfield EN2 6BQ, UK + +Special Exception +Metaswitch Networks Ltd grants you permission to copy, modify, +propagate, and distribute a work formed by combining OpenSSL with The +Software, or a work derivative of such a combination, even if such +copying, modification, propagation, or distribution would otherwise +violate the terms of the GPL. You must comply with the GPL in all +respects for all of the code used other than OpenSSL. +"OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL +Project and licensed under the OpenSSL Licenses, or a work based on such +software and licensed under the OpenSSL Licenses. +"OpenSSL Licenses" means the OpenSSL License and Original SSLeay License +under which the OpenSSL Project distributes the OpenSSL toolkit software, +as those licenses appear in the file LICENSE-OPENSSL. diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/config-changed b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/config-changed new file mode 100755 index 0000000..4382edd --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/config-changed @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +# Get the configuration. +proxied_ip=$(config-get proxied_ip) +login_password=$(config-get password) +home_domain=$(config-get home_domain) +base_number=$(config-get base_number) +number_count=$(config-get number_count) + +# If the node is configured, provision it and its numbers. +if [ -n "$proxied_ip" ] && [ -n "$home_domain" ] && [ -n "$login_password" ] ; then + # Copy the reconfigure-aio script on, and run it. + status-set maintenance "configuring" + sshpass -p$login_password scp -o StrictHostKeyChecking=no $CHARM_DIR/lib/reconfigure-aio ubuntu@$proxied_ip:/tmp/reconfigure-aio.$$ + sshpass -p$login_password ssh -o StrictHostKeyChecking=no ubuntu@$proxied_ip "echo $login_password | sudo -S bash -c 'bash /tmp/reconfigure-aio.$$ $home_domain $base_number $number_count ; rm -f /tmp/reconfigure-aio.$$'" + status-set active "configured" +else + status-set blocked "waiting for configuration" +fi diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/install b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/install new file mode 100755 index 0000000..4378d03 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/install @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +status-set maintenance "installing" + +# Install sshpass as we'll need it shortly +apt-get update +apt-get -q -y --force-yes install sshpass + +status-set maintenance "installed" diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/start b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/start new file mode 100755 index 0000000..8824f74 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/start @@ -0,0 +1,7 @@ +#!/bin/bash +# Here put anything that is needed to start the service. +# Note that currently this is run directly after install +# i.e. 'service apache2 start' +set -e + +# Nothing to do diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/stop b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/stop new file mode 100755 index 0000000..f6ecbe3 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/stop @@ -0,0 +1,10 @@ +#!/bin/bash +# This will be run when the service is being torn down, allowing you to disable +# it in various ways.. +# For example, if your web app uses a text file to signal to the load balancer +# that it is live... you could remove it and sleep for a bit to allow the load +# balancer to stop sending traffic. +# rm /srv/webroot/server-live.txt && sleep 30 +set -e + +# Nothing to do diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/upgrade-charm b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/upgrade-charm new file mode 100755 index 0000000..fdb1f86 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/hooks/upgrade-charm @@ -0,0 +1,8 @@ +#!/bin/bash +# This hook is executed each time a charm is upgraded after the new charm +# contents have been unpacked +# Best practice suggests you execute the hooks/install to ensure all updates are processed - +# hooks/config_change is triggered automatically +set -e + +$CHARM_DIR/hooks/install diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/icon.svg b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/icon.svg new file mode 100644 index 0000000..f9ac92c --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/icon.svg @@ -0,0 +1,407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/lib/reconfigure-aio b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/lib/reconfigure-aio new file mode 100755 index 0000000..a7cad90 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/lib/reconfigure-aio @@ -0,0 +1,55 @@ +#!/bin/bash +# Reconfigures an all-in-one image to use a new home domain and number range. +# Usage: reconfigure-aio [ ] + +# Get command-line arguments. +home_domain=$1 +base_number=$2 +number_count=$3 + +if [ -z "$home_domain" ] ; then + echo "Usage: reconfigure-aio [ ]" +fi + +# Remove all old numbers from the database, unless they're currently assigned. +# We do this even if the home domain hasn't changed, because the number range might have done (and +# it's hard to tell if that's happened, and cheap/low-impact to just do the reprovisioning). +old_home_domain=$(. /etc/clearwater/config ; echo $home_domain) +echo "DELETE FROM ellis.numbers WHERE number LIKE '%@$old_home_domain' AND owner_id IS NULL ;" | mysql + +# Update /etc/clearwater/shared_config, if the home domain has changed. +if [ "$home_domain" != "$old_home_domain" ] ; then + function escape { echo $1 | sed -e 's/\//\\\//g' ; } + sed -e 's/^home_domain=.*$/home_domain='$(escape $home_domain)'/g' \ + /tmp/shared_config.$$ + mv /tmp/shared_config.$$ /etc/clearwater/shared_config + + # Restart clearwater-infrastructure to propagate changes to other configuration files. + service clearwater-infrastructure restart +fi + +# Create new numbers in the new domain, if we've been asked to. +if [ -n "$base_number" ] && [ -n "$number_count" ] ; then + /usr/share/clearwater/ellis/env/bin/python /usr/share/clearwater/ellis/src/metaswitch/ellis/tools/create_numbers.py --start $base_number --count $number_count +fi + +# Restart all the components, if the home domain has changed. +if [ "$home_domain" != "$old_home_domain" ] ; then + # Work around https://github.com/Metaswitch/sprout/issues/1296. + service bono stop + + # Restart all the processes. + for X in /usr/share/clearwater/infrastructure/scripts/restart/* ; do $X ; done + + # Kick monit to wake up and sleep for 10 seconds to make sure it has an accurate view of the system. + monit + sleep 10 + + # Now wait until all the processes are back up and running (or at least "Uptime failed", which + # means the process is running, just hasn't been running for very long). + while monit summary | grep _process | egrep -v "(Running|Uptime failed)" ; do + echo Some processes still not running - waiting... + sleep 2 + done + echo All processes running - configuration complete! +fi diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/metadata.yaml b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/metadata.yaml new file mode 100644 index 0000000..4f1ad13 --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/metadata.yaml @@ -0,0 +1,10 @@ +name: clearwater-aio-proxy +summary: All-in-One proxy charm for Project Clearwater +maintainer: Project Clearwater Maintainers +description: All-in-One proxy charm for Project Clearwater +tags: + - misc +subordinate: false +provides: + ue: + interface: 3GPP-Gm diff --git a/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/revision b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/revision new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/src/vnfd/ims_allin1_2p_vnf/charms/clearwater-aio-proxy/revision @@ -0,0 +1 @@ +1 diff --git a/src/vnfd/ims_allin1_2p_vnf/icons/metaswitch_2x.png b/src/vnfd/ims_allin1_2p_vnf/icons/metaswitch_2x.png new file mode 100644 index 0000000..a899bc8 Binary files /dev/null and b/src/vnfd/ims_allin1_2p_vnf/icons/metaswitch_2x.png differ diff --git a/src/vnfd/tidgen_mwc16_vnf/icons/tef.png b/src/vnfd/tidgen_mwc16_vnf/icons/tef.png new file mode 100644 index 0000000..fcaaf88 Binary files /dev/null and b/src/vnfd/tidgen_mwc16_vnf/icons/tef.png differ diff --git a/src/vnfd/tidgen_mwc16_vnf/mwc16gen1__vnfd.yaml b/src/vnfd/tidgen_mwc16_vnf/mwc16gen1__vnfd.yaml new file mode 100644 index 0000000..204c8c9 --- /dev/null +++ b/src/vnfd/tidgen_mwc16_vnf/mwc16gen1__vnfd.yaml @@ -0,0 +1,87 @@ +vnfd:vnfd-catalog: + vnfd: + - id: mwc16gen + name: mwc16gen + short-name: mwc16gen + description: tidgen 4x10Gbps 28GB 11cores + logo: tef.png + mgmt-interface: + vdu-id: mwc16gen1-VM + connection-point: + - name: eth0 + type: VPORT + - name: eth1 + type: VPORT + - name: xe0 + type: VPORT + - name: xe1 + type: VPORT + - name: xe2 + type: VPORT + - name: xe3 + type: VPORT + vdu: + - id: mwc16gen1-VM + name: mwc16gen1-VM + description: tidgen with 4x10Gbps 28GB + image: /mnt/powervault/virtualization/vnfs/demos/mwc2016/tidgen_mwc16.qcow2 + vm-flavor: + memory-mb: '28672' + mgmt-vpci: 0000:00:0a.0 + external-interface: + - name: xe0 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:10.0' + vnfd-connection-point-ref: xe0 + - name: xe1 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:11.0' + vnfd-connection-point-ref: xe1 + - name: xe2 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:12.0' + vnfd-connection-point-ref: xe2 + - name: xe3 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '0000:00:13.0' + vnfd-connection-point-ref: xe3 + - name: eth0 + virtual-interface: + bandwidth: '1000000' + type: VIRTIO + vpci: 0000:00:0a.0 + vnfd-connection-point-ref: eth0 + - name: eth1 + virtual-interface: + bandwidth: '1000000' + type: OM-MGMT + vpci: 0000:00:0b.0 + vnfd-connection-point-ref: eth1 + guest-epa: + cpu-pinning-policy: DEDICATED + cpu-thread-pinning-policy: PREFER + mempage-size: LARGE + numa-node-policy: + mem-policy: STRICT + node: + - id: '0' + paired-threads: + num-paired-threads: '11' + node-cnt: '1' + host-epa: + om-cpu-feature: + - 64b + - iommu + - lps + - tlbps + - hwsv + - dioc + - ht + om-cpu-model-string: Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz + hypervisor-epa: + type: REQUIRE_KVM + version: 10002|12001|2.6.32-358.el6.x86_64