commit 3436c083612df6ea6cd798e9eb28a529ce2e0c0d Author: Michael Prokop Date: Wed Mar 13 14:47:57 2013 +0100 Initial checkin diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..5ed0f947 --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ +# for syntax checks +BASH_SCRIPTS = scripts/* functions/* etc/ngcp-config/ngcpcfg.cfg helper/build_config sbin/ngcpcfg +PERL_SCRIPTS = helper/sort-yml \ + helper/sync-db \ + helper/tt2-wrapper \ + helper/validate-yml helper/fileformat_version \ + sbin/ngcp-network \ + sbin/ngcp-sync-constants + +all: docs + +docs: html pdf epub man + +html: + asciidoc docs/ngcpcfg.txt + +pdf: + a2x --icons -a toc -a toclevels=3 -a docinfo -f pdf docs/ngcpcfg.txt + +epub: + a2x --icons -a toc -a toclevels=3 -a docinfo -f epub docs/ngcpcfg.txt + +man: + asciidoc -d manpage -b docbook docs/ngcpcfg.txt + sed -i 's///' docs/ngcpcfg.xml + xsltproc --nonet /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl docs/ngcpcfg.xml + pod2man --section=8 sbin/ngcp-network > ngcp-network.8 + pod2man --section=8 sbin/ngcp-sync-constants > ngcp-sync-constants.8 + + +clean: + rm -f docs/ngcpcfg.xml docs/ngcpcfg.epub docs/ngcpcfg.html docs/ngcpcfg.pdf + rm -f ngcpcfg.8 ngcp-network.8 ngcp-sync-constants.8 + +dist-clean: + rm -f docs/ngcpcfg.html docs/ngcpcfg.pdf + rm -f docs/ngcpcfg.epub ngcpcfg.8 + +# check for syntax errors +syntaxcheck: shellcheck perlcheck + +shellcheck: + @echo -n "Checking for shell syntax errors"; \ + for SCRIPT in $(BASH_SCRIPTS); do \ + test -r $${SCRIPT} || continue ; \ + bash -n $${SCRIPT} || exit ; \ + echo -n "."; \ + done; \ + echo " done."; \ + +perlcheck: + @echo "Checking for perl syntax errors:"; \ + for SCRIPT in $(PERL_SCRIPTS); do \ + test -r $${SCRIPT} || continue ; \ + perl -CSD -w -c $${SCRIPT} || exit ; \ + done; \ + echo "-> perl check done."; \ + +# EOF diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..b0bc7a15 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,357 @@ +ngcp-ngcpcfg (0.16.0) unstable; urgency=low + + * New release splitting of ha/carrier features. + + -- Michael Prokop Wed, 13 Mar 2013 14:21:25 +0100 + +ngcp-ngcpcfg (0.15.3) unstable; urgency=low + + * ngcp-sync-constants takes MySQL credentials from /etc/mysql/sipwise.cnf + + -- Kirill Solomko Wed, 15 Jan 2013 16:32:11 +0100 + +ngcp-ngcpcfg (0.15.2) unstable; urgency=low + + * Fix sync for asterisk ODBC credentials. + + -- Andrew Pogrebennyk Wed, 02 Jan 2013 11:27:23 +0100 + +ngcp-ngcpcfg (0.15.1) unstable; urgency=low + + * Make sure to sync both sems and sems_prepaid passwords. + + -- Andrew Pogrebennyk Fri, 21 Dec 2012 21:47:51 +0100 + +ngcp-ngcpcfg (0.15.0) unstable; urgency=low + + * Implement ngcp-network command-line tool. + * Implement ngcp-ngcpcfg-carrier package. + * Add helper functions for network.yml transition. + * support configuration directory /etc/ngcp-config/ngcpcfg.d + * provide carton/cpanfile integration for running testsuite on Jenkins + * Check the fax_gateways defined or not + * Add support to netmask for the fax_gateways + + -- Andrew Pogrebennyk Fri, 21 Dec 2012 11:27:15 +0100 + +ngcp-ngcpcfg (0.14.1) unstable; urgency=low + + [ mwang ] + * sync-db: Check the fax_gateways defined or not + + -- Jon Bonilla Wed, 22 Aug 2012 12:50:01 +0200 + +ngcp-ngcpcfg (0.14.0) unstable; urgency=low + + [ Michael Prokop ] + * Adjust configuration file handling to properly work with UTF-8 input + * Run etckeeper when invoking "ngcpcfg commit" + * Adjust detection of new configuration files that are not yet tracked + * Bump Standards-Version to 3.9.3 + * etckeeper: check for etckeeper binary and initialised .git repo + * commit: do not exit on errors during sync-db + * services option: run inside a function so --dry-run option works + correct + * wrap and sort items in Debian packaging files + + [ Andreas Granig ] + * On commit, also sync various options with db using helper/sync-db + + [ Min Wang ] + * helper/sync-db related changes: + - Add sync fax_gateways for kamailio dispatcher table + - Add reloading dispatcher once fax_gateways changes + - Print out the reload dispatcher return status + + -- Michael Prokop Fri, 10 Aug 2012 18:33:19 +0200 + +ngcp-ngcpcfg (0.13.1) unstable; urgency=low + + * Do not rely on TTL but instead use timeout when checking other host(s) + * pull: no need to run fetch check in subshell + * pull: avoid diverging branches if changes take place on both sides. + Thanks to Andrew Pogrebennyk for catching the bug + providing + instructions to reproduce it + * pull: exit with return code of pull command + + -- Michael Prokop Mon, 14 May 2012 15:27:05 +0200 + +ngcp-ngcpcfg (0.13.0) unstable; urgency=low + + [ Michael Prokop ] + * Install ngcpcfg manpage in main ngcp-ngcpcfg package + + [ Andrew Pogrebennyk ] + * Add yml upgrade scripts for 2.5 + * Add testfiles for 2.5 ce and pro + * Add yml update helper scripts + + -- Michael Prokop Tue, 03 Apr 2012 16:24:30 +0200 + +ngcp-ngcpcfg (0.12.4) unstable; urgency=low + + [ Michael Prokop ] + * perlcheck: invoke perl with -CSD option + + -- Michael Prokop Thu, 26 Jan 2012 17:38:19 +0100 + +ngcp-ngcpcfg (0.12.3) unstable; urgency=low + + [ Richard Fuchs ] + * Fix handling of utf8 files + + [ Michael Prokop ] + * Config builder: do not output error message, provide + debugging instructions instead + + -- Michael Prokop Thu, 26 Jan 2012 16:51:51 +0100 + +ngcp-ngcpcfg (0.12.2) unstable; urgency=low + + * provide force-reload in ngcpcfg-status init script + * diff command: drop --addremove option and make it the + default behaviour + + -- Michael Prokop Thu, 26 Jan 2012 12:36:56 +0100 + +ngcp-ngcpcfg (0.12.1) unstable; urgency=low + + * upgrade scripts: + - update internal file format information + - bump fileversion of cdrexport to 003 + + -- Michael Prokop Fri, 02 Dec 2011 22:23:58 +0100 + +ngcp-ngcpcfg (0.12.0) unstable; urgency=low + + [ Michael Prokop ] + * Add simple sort-file script for easier comparison of upgrade script output + * Provide upgrade scripts for recent sip:provider releases + * Update configuration files according to recent development + * Add fileformat_version helper script for usage inside upgrade scripts + * Error out if encoding of a central configuration file isn't ASCII nor UTF-8 + * tt2-wrapper: explicitely set utf8 mode for stdout + * Run xsltproc with --nonet option + * Add config.yml/constants.yml testfiles for new sip:provider releases + + [ Jon Bonilla ] + * Adapt upgrade scripts to recent development + * Adapt testfile to recent development + + -- Michael Prokop Tue, 29 Nov 2011 17:49:45 +0100 + +ngcp-ngcpcfg (0.11.1) unstable; urgency=low + + * Bugfixes: + - ngcpcfg: fix usage instructions regarding --debug switch + + * Debian packaging: + - rework debian/rules to use generic rule to build packages + - use team as entry in Maintainer field of debian/control + + * Testsuite improvements: + - test tt2 processing + precedence of files + - validate ngcpcfg without any arguments, with --version + and with --help + + * High Availability Setup: + - pull: add further debug statements + - push: use 'ngcpcfg apply' in default action and support + --noapply for disabling the behaviour + - push: use ngcpcfg pull instead of native git commands + - push: if ssh login does not work report it with specific + error message + + -- Michael Prokop Thu, 22 Sep 2011 01:34:04 +0200 + +ngcp-ngcpcfg (0.11.0) unstable; urgency=low + + * Bugfixes: + - Do not strip $CONFIG_POOL variable from provided file/directory + arguments when generating file list + - Get rid of files *.tt2.sp{1,2} where a *.customtt.tt2.sp{1,2} + exists as well + + * New features: + - Build option: support generation of modified files only when + using --modified-only option + - Diff: support --addremove option to list new/removed files + - Provide version information through -v, --version + version options + - Push: be more verbose when operation fails + - Services: support --dry-run as alternative to 'test' option, + error out on unknown options + - Support --debug option to run actions in debug mode + - Support new option "diff" to show pending modifications in + configuration pool + + * Improvements: + - Do not remove temporary filelist files in debug mode + - Extend package description of ngcp-ngcpcfg-ha + - Redesign code for generating the file list + - Update ngcpcfg manpage (document new options, clarify + precedence of configuration files,...) + + * High Availability Setup: + - Do not add host to host list if build operation was successfull + - Fix typo in warning message (registerted<->registered) + - Support --nobuild option to skip build process when pushing + changes + - When pushing changes then execute 'build' on all pushed hosts + + -- Michael Prokop Mon, 29 Aug 2011 13:42:23 +0200 + +ngcp-ngcpcfg (0.10.0) unstable; urgency=low + + * Add further logic and user information for validating YAML syntax. + * Make sure customtt.tt2 template files are preferred over + non-customtt.tt2 files in non-HA setups. + * encrypt/decrypt: get rid of ngcpcfg-share on glusterfs and + local cache on encrypt and try to restore it iff possible or otherwis + * Fix unset variable if calling ngcpcfg without any options + and having ngcpcfg-locker installed. + * Usage text: correctly indent optional features. + * Add build-arch/build-indep targets to debian/rules to make lintian happy. + * Drop essential package tar from Depends of ngcp-ngcpcfg-locker. + * Drop essential package bsdutils from Depends. + + -- Michael Prokop Sat, 20 Aug 2011 00:20:34 +0200 + +ngcp-ngcpcfg (0.9.0) unstable; urgency=low + + * Before generating new configuration files from templates + test known .yml files for valid syntax. + * New package ngcp-ngcpcfg-locker: support encrypting/decrypting + ngcpcfg configuration files. + * Initial version of an upgrade script to support safe upgrades. + + -- Michael Prokop Mon, 04 Jul 2011 15:49:36 +0200 + +ngcp-ngcpcfg (0.8.0) unstable; urgency=low + + [ Michael Prokop ] + * Adjust package description. + * Upgrade script: + - Add clir and block-override VSC. + - Add www_admin.peer with preference_features flag. + - Add preference_features switch for domains in www-admin. + * Fix gitignore configuration of ngcpcfg. + * Bump Standards-Version to 3.9.2. + * HA features: + - Store node name in /etc/ngcp_ha_node. + - Use glusterfs share as default remote target and make setup + consistent between involved nodes. + + [ Andreas Granig ] + * Aligned to x.y.z versioning scheme. + + -- Michael Prokop Sat, 11 Jun 2011 01:44:46 +0200 + +ngcp-ngcpcfg (0.7.0) unstable; urgency=low + + [ Michael Prokop ] + * Bugfix: + - Make sure we switch to $NGCPCTL_MAIN before marking host as + initialised. + * Features: + - Support shared setup (via ngcp-ngcpcfg-ha). + - Support .sp1 and .sp2 files for shared setup (ngcp-ngcpcfg-ha). + - Provide debugging option through environment variable DEBUG. + - Send status messages to syslog. + - Check for Debian package versions of templates on all nodes and do + NOT push in case of different package versions (applies only + to files that are going to be pushed and not to all templates, + ngcp-ngcpcfg-ha only). + - Provide upgrade package ngcp-ngcpcfg-upgrade to apply schema + changes when upgrading from 2.1 to 2.2. + - Support {pre,post}build scripts within template directory. + - Initial testsuite to check for regressions. + - Provide validate-yml script to validate configuration file. + - Provide sort-yml script to sort configuration file for easier + comparison with other configuration files. + - Provide warning message if a service script did not return + with exit code 0. + - Support {pre,post}build scripts within template directory. + * Changes: + - Refactor code to minimise user interface script and provide + options (build/commit/...) through separate scripts. + - Drop HELPER configuration variable from ngcpcfg.cfg. + - Slightly improve /etc/ngcp-config/ngcpcfg.cfg (description and + sorting of variables. + * Debian packaging: + - Drop shlibs:Depends from Depends, we do not have any libraries. + - Fix typo in long description. + - Add debian/source/format (1.0 format). + - Provide syntaxchecks for bash and perl scripts as Q/A mechanism + in build process. + - Add perl packages to Build-Depends. + - Drop unnecessary libtemplate-plugin-yaml-perl from Depends. + - Run syntax checks for code while building, therefore add + libhash-merge-perl, libtemplate-perl and libyaml-perl to + Build-Depends. + + [ Richard Fuchs ] + * Add libyaml-tiny-perl to Build-Depends. + + [ Jon Bonilla ] + * Add support to ngcpcfg.{pre|post}build directory generic files. + + -- Andreas Granig Fri, 29 Apr 2011 12:10:13 +0200 + +ngcp-ngcpcfg (0.6) unstable; urgency=low + + [ Jon Bonilla ] + * Add constants.yaml file handling so the user can configure + just configurable options. + + [ Andreas Granig ] + * Added "apply" as a short-cut for build, then services, then commit. + + [ Michael Prokop ] + * Iff a directory does not exist yet create it with permissions 755. + * Update stderr printf handling. + * Integrate etckeeper commit in "apply" shortcut. + * Update copyright (GPL-3+). + * Work around a git index issue with generated files for use with + the service command. + + -- Michael Prokop Tue, 30 Nov 2010 11:52:19 +0100 + +ngcp-ngcpcfg (0.5) unstable; urgency=low + + * Support .prebuild and .postbuild scripts inside template pool + which are executre before/after generation of output file. + * Support building of specific files/directories only so it's + possible to execute e.g. 'ngcpcfg build /etc/apache2' to + skip generation of any files besides the ones inside /etc/apache2. + + -- Michael Prokop Tue, 16 Nov 2010 18:27:28 +0100 + +ngcp-ngcpcfg (0.4) unstable; urgency=low + + * Support additional config.local.yml config file. + * Support .customtt.tt2 files for local configuration. + * Provide service files through same directory as templates. + * Unify service executions. + * Make generated files r/o. + * Inform if config has changed and is not "build" yet. + * Show which files have been modified. + + -- Michael Prokop Wed, 10 Nov 2010 13:04:56 +0100 + +ngcp-ngcpcfg (0.3) unstable; urgency=low + + * Unify directory names, configuration files,... + + -- Michael Prokop Wed, 20 Oct 2010 14:03:01 +0200 + +ngcp-ngcpcfg (0.2) unstable; urgency=low + + * Support non-shared setup. + + -- Michael Prokop Tue, 12 Oct 2010 18:02:10 +0200 + +ngcp-ngcpcfg (0.1) unstable; urgency=low + + * Initial release. + + -- Michael Prokop Thu, 26 Aug 2010 17:19:32 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000..7ed6ff82 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..577bb224 --- /dev/null +++ b/debian/control @@ -0,0 +1,60 @@ +Source: ngcp-ngcpcfg +Section: admin +Priority: extra +Maintainer: Sipwise Development Team +Build-Depends: asciidoc, + debhelper (>= 5), + docbook-xsl, + libdata-validate-ip-perl, + libdbd-mysql-perl, + libhash-merge-perl, + libio-interface-perl, + liblist-moreutils-perl, + libnet-netmask-perl, + libregexp-ipv6-perl, + libtemplate-perl, + libyaml-perl, + libyaml-tiny-perl, + xsltproc +Standards-Version: 3.9.4 +Homepage: http://sipwise.com/ + +Package: ngcp-ngcpcfg +Architecture: all +Depends: etckeeper, + file, + git-core, + libdata-validate-ip-perl, + libdbd-mysql-perl, + libhash-merge-perl, + libio-interface-perl, + liblist-moreutils-perl, + libnet-netmask-perl, + libregexp-ipv6-perl, + libtemplate-perl, + libyaml-perl, + libyaml-tiny-perl, + ${misc:Depends}, + ${perl:Depends} +Description: central and templated based Configuration Management System for NGCP platforms + ngcp-ngcpcfg is a Configuration Management System providing central + configuration and template based handling of configuration + files, featuring handling of local configuration changes and + updates as well as synchronisation between servers. + +Package: ngcp-ngcpcfg-locker +Architecture: all +Depends: gnupg, + ngcp-ngcpcfg, + ${misc:Depends} +Description: Encrypt and decrypt feature for ngcp-ngcpcfg + This package provides the encrypt and decrypt options for + usage via ngcpcfg. + +Package: ngcp-ngcpcfg-testsuite +Architecture: all +Depends: ngcp-ngcpcfg, + ${misc:Depends} +Description: testsuite for ngcpcfg + This package provides a testsuite to automatically test ngcpcfg + features and detect any possible breakages. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 00000000..a8f3e510 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,23 @@ +Upstream Author: The Sipwise Team - http://sipwise.com +Copyright: 2007-2013, Sipwise GmbH, Austria +License: GPL-3+ + 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. + . + 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 package; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301 USA + . + On Debian systems, the full text of the GNU General Public + License version 3 can be found in the file + `/usr/share/common-licenses/GPL-3'. diff --git a/debian/ngcp-ngcpcfg-locker.install b/debian/ngcp-ngcpcfg-locker.install new file mode 100644 index 00000000..1741c91a --- /dev/null +++ b/debian/ngcp-ngcpcfg-locker.install @@ -0,0 +1,2 @@ +scripts/decrypt usr/share/ngcp-ngcpcfg/scripts/ +scripts/encrypt usr/share/ngcp-ngcpcfg/scripts/ diff --git a/debian/ngcp-ngcpcfg-testsuite.install b/debian/ngcp-ngcpcfg-testsuite.install new file mode 100644 index 00000000..e06aee9e --- /dev/null +++ b/debian/ngcp-ngcpcfg-testsuite.install @@ -0,0 +1 @@ +testsuite/* usr/share/ngcp-ngcpcfg/testsuite/ diff --git a/debian/ngcp-ngcpcfg.install b/debian/ngcp-ngcpcfg.install new file mode 100644 index 00000000..819eab56 --- /dev/null +++ b/debian/ngcp-ngcpcfg.install @@ -0,0 +1,19 @@ +etc/ngcp-config/ngcpcfg.cfg etc/ngcp-config/ +functions/main usr/share/ngcp-ngcpcfg/functions/ +helper/build_config usr/share/ngcp-ngcpcfg/helper/ +helper/fileformat_version usr/share/ngcp-ngcpcfg/helper/ +helper/sort-yml usr/share/ngcp-ngcpcfg/helper/ +helper/sync-db usr/share/ngcp-ngcpcfg/helper/ +helper/tt2-wrapper usr/share/ngcp-ngcpcfg/helper/ +helper/validate-yml usr/share/ngcp-ngcpcfg/helper/ +lib/* usr/lib/ngcp-ngcpcfg/ +sbin/ngcp-network usr/sbin/ +sbin/ngcpcfg usr/sbin/ +sbin/ngcp-sync-constants usr/sbin/ +scripts/build usr/share/ngcp-ngcpcfg/scripts/ +scripts/commit usr/share/ngcp-ngcpcfg/scripts/ +scripts/diff usr/share/ngcp-ngcpcfg/scripts/ +scripts/etckeeper usr/share/ngcp-ngcpcfg/scripts/ +scripts/initialise usr/share/ngcp-ngcpcfg/scripts/ +scripts/services usr/share/ngcp-ngcpcfg/scripts/ +scripts/status usr/share/ngcp-ngcpcfg/scripts/ diff --git a/debian/ngcp-ngcpcfg.manpages b/debian/ngcp-ngcpcfg.manpages new file mode 100644 index 00000000..1378aeb6 --- /dev/null +++ b/debian/ngcp-ngcpcfg.manpages @@ -0,0 +1,3 @@ +ngcpcfg.8 +ngcp-network.8 +ngcp-sync-constants.8 diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 00000000..60a5b36c --- /dev/null +++ b/debian/postinst @@ -0,0 +1,31 @@ +#!/bin/sh +# postinst script for ngcp-ngcpcfg + +set -e + +case "$1" in + configure) + if ! [ -d /etc/.git ] ; then + cd /etc + etckeeper init + git rm --cached -r ngcp-config || true + grep -q '^ngcp-config$' .gitignore || echo 'ngcp-config' >> .gitignore + git commit -a -m "initial commit" + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..49a8cf35 --- /dev/null +++ b/debian/rules @@ -0,0 +1,68 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +# export DH_VERBOSE=1 + +b=$(CURDIR)/debian/build +VERSION:=$(shell dpkg-parsechangelog | awk '/Version: / { print $$2 }') + +build: build-stamp + +build-stamp: + dh_testdir + $(MAKE) man + # catch any syntax errors *before* building the .deb + $(MAKE) syntaxcheck + touch $@ + +clean: + dh_testdir + dh_testroot + rm -rf build-stamp $(b) + dh_auto_clean + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + +%: + @echo "--- Building: $@" + dh_installdirs -p$@ -P$(b)/$@ + dh_link -p$@ -P$(b)/$@ + dh_installdocs -p$@ -P$(b)/$@ + dh_installman -p$@ -P$(b)/$@ + dh_installchangelogs -p$@ -P$(b)/$@ + dh_install -p$@ -P$(b)/$@ + test -r $(b)/$@/usr/sbin/ngcp-network && \ + sed -i -e "s/VERSION = 'UNRELEASED'/VERSION = '$(VERSION)'/" \ + $(b)/$@/usr/sbin/ngcp-network || true + dh_link -p$@ -P$(b)/$@ + dh_strip -p$@ -P$(b)/$@ + dh_compress --exclude=examples/etc/ --exclude=packages/ -p$@ -P$(b)/$@ + dh_fixperms -p$@ -P$(b)/$@ + dh_makeshlibs -p$@ -P$(b)/$@ -V + dh_installdeb -p$@ -P$(b)/$@ + dh_shlibdeps -p$@ -P$(b)/$@ + dh_installdebconf -p$@ -P$(b)/$@ + dh_gencontrol -p$@ -P$(b)/$@ + dh_md5sums -p$@ -P$(b)/$@ + dh_builddeb -p$@ -P$(b)/$@ + +binary-all: build install + +binary-indep: build install \ + ngcp-ngcpcfg \ + ngcp-ngcpcfg-locker \ + ngcp-ngcpcfg-testsuite + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 00000000..d3827e75 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +1.0 diff --git a/docs/ngcpcfg.txt b/docs/ngcpcfg.txt new file mode 100644 index 00000000..9fe9c47a --- /dev/null +++ b/docs/ngcpcfg.txt @@ -0,0 +1,467 @@ +ngcpcfg(8) +========== + +Name +---- +ngcpcfg - central and template based Configuration Management + +Synopsis +-------- +ngcpcfg [ options ] + +Introduction +------------ + +ngcpcfg is a Configuration Management System, developed for the Sipwise Next +Generation Platform. It provides a central mechanism for handling configuration +changes, updates and synchronisation between servers through a main +configuration which is simple and easy to read and modify. + +tl;dr? - ngcpcfg for the impatient +---------------------------------- + +The main system configuration is done in the file _/etc/ngcp-config/config.yml_. +After modifying the file execute 'ngcpcfg apply' to build the accordingly +configurations file. + +Taxonomy +-------- + +*central yml files*:: *.yml files inside _/etc/ngcp-config/_ + +*High Availability setup*:: ngcpcfg running in a cluster setup (e.g. +sip:providerPRO or sip:carrier), depends on Debian package ngcp-ngcpcfg-ha. + +*local repository*:: the directory _/etc/ngcp-config/_, being a Git repository + +*remote systems*:: the other nodes inside a High Availability setup, as defined +in _/etc/ngcp-config/systems.cfg_ + +*shared repository*:: Git repository shared amongst nodes inside a High +Availability setup, as defined by the GLUSTERFS setting in +/etc/ngcp-config/ngcpcfg.d/shared_storage.cfg (requires ngcp-ngcpcfg-ha) and +being _/mnt/glusterfs_ by default + +*templates*:: template toolkit files with .tt2 suffix, found in +_/etc/ngcp-config/templates/etc/_ + +[[configfiles]] +Configuration files +------------------- + +Main configuration files +~~~~~~~~~~~~~~~~~~~~~~~~ + +* _/etc/ngcp-config/config.yml_: central configuration file, to be configured with +$EDITOR, webfrontend,... + +* _/etc/ngcp-config/config.$HOSTNAME.yml_: host specific configuration file, +depending on the hostname (:= $HOSTNAME) of the system. + +* _/etc/ngcp-config/config.local.yml_: local configuration, not being host +specific. + +* _/etc/ngcp-config/constants.yml_: configuration file that has precedence over +any other .yml file _/etc/ngcp-config/_, defining important constant settings. +This file is *not* supposed to be modified by the user (without having a very +good reason). + +* _/etc/ngcp-config/ngcpcfg.cfg_: main configuration file for ngcpcfg itself, +provides global variables used inside ngcpcfg and its helper scripts. This file +is *not* supposed to be modified by the user (without having a very good +reason). + +* _/etc/ngcp-config/ngcpcfg.d/_: configuration directory for ngcpcfg itself. +Files with suffix '.cfg' inside this directory are sourced after +/etc/ngcp-config/ngcpcfg.cfg has been read. Files inside this directory are +*not* supposed to be modified by the user (without having a very good reason). + +[IMPORTANT] +Configuration file priority: constants.yml takes precedence over +config.local.yml, over config.$HOSTNAME.yml, over config.yml. + +High Availability setup specific configuration files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* _/etc/ngcp_ha_node_: defines node name in the High Availability setup, usually +being _sp1_ for the first node and _sp2_ for the second node. + +* _/etc/ngcp-config/systems.cfg_: configuration file that specifies to which +hosts changes should be pushed to. + +Supported template files +~~~~~~~~~~~~~~~~~~~~~~~~ + +Example for generating a configuration file named_/etc/foobar/baz_: + +* _/etc/ngcp-config/templates/etc/foobar/baz.tt2_: main and default template +file, used by template-handler for generating /etc/foobar/baz. Configuration +file is usually provided by a Debian package. + +* _/etc/ngcp-config/templates/etc/foobar/baz.customtt.tt2_: system specific +template file, but configuration usually isn't provided by a Debian package and +can be modified independent from any Debian package mechanism. + +* _/etc/ngcp-config/templates/etc/foobar/baz.tt2.$HA_NODE_: node specific +template file. $HA_NODE is determined using the content of /etc/ngcp_ha_node +(usually being _sp1_ for the first node and _sp2_ for the second node on the +Sipwise Next Generation Platform). Wheras _*customtt.tt2_ files are used on all +nodes in a High Availability setup the _*.tt2.$HA_NODE*_ file is specific for +the single node only. A common usage case is master vs. slave configuration of a +service. Configuration file is usually provided by a Debian package. Note: +Feature is available in the High Availability setup only. + +* _/etc/ngcp-config/templates/etc/foobar/baz.customtt.tt2.$HA_NODE_: node +specific template file. Regarding $HA_NODE the same as for _baz.tt2.$HA_NODE_ +applies (see previous bullet), but the configuration file usually isn't provided +by a Debian package but can be modified independent from any Debian package +mechanism. Note: Feature is available in the High Availability setup only. + +[IMPORTANT] +Configuration file priority: *.customtt.tt2.$HA_NODE takes precedence over +*.customtt.tt2, over *.tt2.$HA_NODE, over *.tt2. + +Support action related files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Example for handling a configuration file named _/etc/foobar/baz_: + +* _/etc/ngcp-config/templates/etc/foobar/baz.services_: service file, defining +actions that have to be executed when file /etc/foobar/baz was modified. + +* _/etc/ngcp-config/templates/etc/foobar/ngcpcfg.services_: service file, +defining actions that have to be executed whenever *any* file inside /etc/foobar +was modified. + +* _/etc/ngcp-config/templates/etc/foobar/baz.postbuild_: script which is +executed *after* a configuration file (/etc/foobar/baz) has been generated. The +environment variable "$output_file" containing the file name of the generated +configuration file (/etc/foobar/baz) is available within the postbuild script. A +common usage case for postbuild files is adjusting file permissions. + +* _/etc/ngcp-config/templates/etc/foobar/baz.prebuild_: script which is +executed *before* a configuration file (/etc/foobar/baz) is generated. The +environment variable "$output_file" containing the file name of the generated +configuration file (/etc/foobar/baz) is available within the postbuild script. + +Syntax and layout of configuration files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* _/etc/ngcp-config/ngcpcfg.cfg_: plain shell syntax style "key=value" entries + +* _/etc/ngcp-config/systems.cfg_: one hostname or IP address per line + +* _central yml files_ (*.yml): YAML configuration syntax (see http://yaml.org/) + +* _template files_ (*.tt2): whatever the according software (Kamailio, +MySQL,...) needs. Any variables that should be replaced by the configuration +management system (based on the main configuration files *.yml inside +/etc/ngcp-config/) have to be written in the YAML syntax (see http://yaml.org/) + +* _service files_ (*.services) and _build files_ ({post,pre}build): file will be +executed under "bash $FILE", so you can use commands e.g. like +"/etc/init.d/foobar restart" and any further shell scripting syntax inside the +services files + +Options +------- + + **--debug** []:: + +Run actions (see next section) in verbose mode, useful for debugging. + + **--help**:: + +Display usage information and exit. + + **--version**:: + +Display ngcpcfg version and exit. + +Actions +------- + + **apply**:: + +Executes the _build_, _services_ and _commit_ commands in a batch (assuming each +command worked as expected). This option serves as a shortcut for the most +commonly executed commands. + + **build** [--modified-only] [|]:: + +Generate configuration files, based on values defined the central yml files and +based on the templates in the configuration tree (/etc/ngcp-config/templates by +default). The *--modified-only* option checks for _modified_ and _uncommitted_ +configuration files (central yml files and templates) and builds the relevant +files only. If a central yml file is modified it builds all configuration files. +If changes are in template files only just the according template files are +considered for rebuild. If a file (e.g. _/etc/rsyslog.conf_) or directory (e.g. +_/etc/mysql/_) is provided as argument to the _build_ option only the specified +file / files inside the directory will be generated. If the argument doesn't +start with the /etc prefix the argument is considered as pattern, matching all +files/directories which include the specified pattern. The pattern can include +shell globbing patterns - so argument 'mo..t' will match the files +/etc/ngcp-monitoring-tools/collective-check.conf, /etc/monit/monitrc, +/etc/ha.d/resource.d/monit-services and /etc/default/monit iff they are present. +You can combine __ and __ and use multiple +arguments. + + **commit** []:: + +Commit all modified files in _/etc/ngcp-config_ and record changes in _/etc_ by +executing the etckeeper(8) command through +'/usr/share/ngcp-ngcpcfg/scripts/etckeeper'. To check whether there are any +pending changes to be committed execute 'ngcpcfg status'. + + **decrypt**:: + +Decrypt /etc/ngcp-config-crypted.tgz.gpg and restore configuration files, +doing the reverse operation of the _encrypt_ option. +Note: This feature is only available if the ngcp-ngcpcfg-locker package is +installed. + + **diff** []:: + +Show changes between ngcpcfg's Git repository and the working tree inside +_/etc/ngcp-config_. You can specify options for the underlying 'git diff' +command, e.g. 'ngcpcfg diff HEAD^' will show all changes that have been recorded +with the last _commit_ operation (see manpage git-rev-parse(1) for details how +to specify revisions). If the tool doesn't report anything it means that there +are neither any uncommited changes nor any new or removed files (files which are +not yet (un)registered to the repository). + + **encrypt**:: + +Encrypt /etc/ngcp-config and all resulting configuration files with a user +defined password and save the result as /etc/ngcp-config-crypted.tgz.gpg. +Note: This feature is only available if the ngcp-ngcpcfg-locker package is +installed. + + **help**:: + +Display usage information and exit. + + **initialise**:: + +If ngcpcfg was installed but isn't configured yet the 'initialise' option sets +up ngcpcfg accordingly. Follow the instructions from the <> section. + + **init-mgmt** :: + +Set up specified '' for usage as shared repository whereas '' +usually corresponds to the IP/hostname of the central management system in a +sip:carrier environment. Further details about the details of the setup +are available in the <> section. +Note: This feature is only available if the ngcp-ngcpcfg-carrier package is installed. + + **pull**:: + +Retrieve modifications from shared repository. +Note: This option is available in the High Availability setup only. + + **push** [--nobuild] [--noapply] [--shared-only] []:: + +Push modifications to shared repository and remote systems. After changes have been +pushed to the node the _apply_ operation will be executed on each remote system +to rebuild the configuration files (unless the '--noapply' operation has been +specified, then only the _build_ operation will be executed). To skip building +any configuration files at all the '--nobuild' option can be specified (implying +to also skip the _apply_ operation). If the '--shared-only' option is set then +the any pending commits will be pushed only to the shared repository only, any +foreign hosts will be skipped then. If hostname(s) or IP address(es) is given +as argument then the changes will be pushed to the shared repository and to the +given hosts only. If no host has has been specified then the hosts specified in +_/etc/ngcp-config/systems.cfg_ are used. +Note: This option is available in the High Availability setup only. + + **services** [--dry-run]:: + +Execute any defined service actions for modified configuration files. If the +_--dry-run_ option is present the services won't be executed but you'll be +noticed which service files would be executed if being invoked without the +_--dry-run__ option. + + **status**:: + +Display the current state of the configuration system, like modified +configuration files and pending actions. + + +Usage examples +-------------- + +The main workflow *without* High Availability setup is: + + ngcpcfg status # check for pending operations + $EDITOR /etc/ngcp-config/config.yml # adjust/extend configuration + ngcpcfg apply "summary of changes" # build configs, run services + commit changes + +If you do not want to execute the _apply_ shortcut command but instead run the +single actions, then you can execute: + + ngcpcfg status # check for pending operations + $EDITOR /etc/ngcp-config/config.yml # adjust/extend configuration + ngcpcfg build # generate/update configuration files + ngcpcfg services # restart services for modified configs + ngcpcfg commit "summary of changes" # register changes + +The main workflow *with* High Availability setup is: + + ngcpcfg status # check for pending operations + ngcpcfg pull # retrieve possibly pending updates + $EDITOR /etc/ngcp-config/config.yml # adjust/extend configuration + ngcpcfg build # generate/update configuration files + ngcpcfg services # restart services for modified configs + ngcpcfg commit "summary of changes" # register changes + ngcpcfg push # upload changes to shared storage + remote systems + ngcpcfg status # check for possibly outstanding issues + +[[faq]] +Frequently asked questions +-------------------------- + +What does ngcpcfg mean? +~~~~~~~~~~~~~~~~~~~~~~~ + +'ngcp' is the http://www.sipwise.com/[Sipwise Next Generation Communication +Platform] and 'cfg' stands for the ConFiGuration management system of the +ngcp system. + +Does ngcpcfg require a shared storage? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Only if you are using ngcpcfg in a High Availability setup and using the 'push' +functionality (which is highly recommended in a HA Setup). + +How does the configuration management system work? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ngcpcfg takes the central yml files (/etc/ngcp-config/*.yml) as input for +configuration, searches for supported templates files (*.tt2) in the template +pool (_/etc/ngcp-config/templates/etc/_) and builds the resulting configuration +files in _/etc_. + +[[errorhandling]] +Error handling - what does this error message mean? +--------------------------------------------------- + +Error message: + + Error: Failed hosts: hostname (down) + +Background: The system "hostname" is not reachable via ICMP, which means it's +not reachable for the ngcpcfg system. + +How to fix: Make sure the system is up and running and reachable via ssh from +the system where you are running the "pull" action. + +Error message: + + error: Your local changes to 'config.yml' would be overwritten by merge. Aborting. + +Background: the file 'config.yml' (in this example) on the system where you are +pushing to contains local modifications which are uncommitted yet. To avoid data +damage the system can't resolve this situation automatically. + +How to fix: + +1) Throw away the changes on the remote system (where you are pushing to, +important: your changes on the system where you are executing the commands will +be gone!): + + cd /etc/ngcp-config ; git checkout -- config.yml + +To get rid of *any* modified files (included added/removed files) the following +commands will do the job (use with care and only execute it if you're aware of +the consequences): + + cd /etc/ngcp-config + git checkout -- . + git clean -xdf + +2) Resolve the merge conflict: + + cd /etc/ngcp-config + git pull + $EDITOR config.yml # resolve conflict + ngcpcfg commit 'merge conflict on system sp2' + ngcpcfg pull # just to make sure + ngcpcfg push # push changes back to the other systems + +[[init-mgmt]] +init-mgmt setup +--------------- + +This section documents the 'ngcpcfg init-mgmt' feature in more detail. +The feature is provided through the ngcp-ngcpcfg-carrier package. + +The script deprecates the shared repository /mnt/glusterfs/ngcpcfg-share of the +local system by renaming it to /mnt/glusterfs/ngcpcfg-share.unused. It then +registers the two according nodes (the local system plus the second node which +makes the PRO pair) in /etc/ngcp-config/systems.cfg. It sets the 'mgmgt_server' +variable in /etc/ngcp-config/carrier.cfg to ''. Then it makes sure that +the automated login via SSH between the two according nodes as well as the + works. It clones the local shared repository to the ''. Finally +it updates the remote origin for the shared repository on both nodes to point to +':/mnt/glusterfs/ngcpcfg_' whereas '' corresponds to the +system's hostname without the trailing 'a' and 'b' suffix. + +[NOTE] +To check which remote repository is in use execute +'cd /etc/ngcp-config; git remote show origin | grep URL'. + +[[setup_instructions]] +Setup instructions +------------------ + +Requirements for High Availability setup: + +* glusterfs is supposed to be set up working and deployed at /mnt/glusterfs + (as defined via /etc/ngcp-config/ngcpcfg.cfg) + +* hosts are expected to be set up for automatic ssh login (ssh-keygen && ssh-copy-id) + +* node names are expected to be sp1 and sp2 so the .tt2.sp1 and .tt2.sp2 +template files are supported + +Configuration on sp1 (expected to be master node in initial setup): + + ngcpcfg initialise + ngcpcfg build + printf "sp1\nsp2\n" > /etc/ngcp-config/systems.cfg + ngcpcfg commit "provide systems.cfg" + +Configuration on sp2 (and any further existing system): + + ngcpcfg initialise + ngcpcfg build + +Please note that you do not have to run this steps if you are using the Sipwise +Next Generation Platform since the installation steps are fully automated. + +Involved frameworks +------------------- + +* git: Distributed Version Control system +* tt2: Template Toolkit +* make: Utility for Directing compilation +* yaml: Generic data serialization language + +Limitations +----------- + +ngcpcfg was designed specifically for the Sipwise Next Generation Platform, +though with being as generic as possible in mind. The system is supposed to be +useful for configuration management on other systems/platforms and it's possible +to adapt it for different needs through a variety of configuration parameters, +though keep in mind that ngcpcfg was implemented for some very specific use +cases. + +Author +------ +Michael Prokop + +///////////////////////////// +// vim: ft=asciidoc tw=80 ai +///////////////////////////// diff --git a/etc/ngcp-config/ngcpcfg.cfg b/etc/ngcp-config/ngcpcfg.cfg new file mode 100644 index 00000000..2f3e6cca --- /dev/null +++ b/etc/ngcp-config/ngcpcfg.cfg @@ -0,0 +1,22 @@ +# Filename: /etc/ngcp-config/ngcpcfg.cfg +# Purpose: main configuration file for ngcpcfg tools +# Note: do not modify unless you have a really good reason to do so + +# directory name where ngcpcfg is managed through git +NGCPCTL_MAIN='/etc/ngcp-config' +NGCPCTL_CONFIG="${NGCPCTL_MAIN}/config.yml" +HOST_CONFIG="${NGCPCTL_MAIN}/config.$(hostname).yml" +LOCAL_CONFIG="${NGCPCTL_MAIN}/config.local.yml" +CONSTANTS_CONFIG="${NGCPCTL_MAIN}/constants.yml" +NETWORK_CONFIG="${NGCPCTL_MAIN}/network.yml" + +# configuration files that should be managed +CONFIG_POOL='/etc' + +# location of templates +TEMPLATE_POOL="${NGCPCTL_MAIN}/templates/${CONFIG_POOL}" + +# location of service definitions +SERVICES_POOL="${NGCPCTL_MAIN}/templates/${CONFIG_POOL}" + +## END OF FILE ################################################################# diff --git a/functions/main b/functions/main new file mode 100644 index 00000000..7e64bbe6 --- /dev/null +++ b/functions/main @@ -0,0 +1,192 @@ +# Filename: /usr/share/ngcp-ngcpcfg/functions/main +# Purpose: helper functions for ngcpcfg +################################################################################ + +## logging functions {{{ +log_info() { + logger -t ngcpcfg -- "$*" + echo "$*" +} + +# info without ending newline +log_info_n() { + logger -t ngcpcfg -- "$*" + printf -- "$*" +} + +log_warn() { + logger -t ngcpcfg -- "Warning: $*" + echo "Warning: $*" +} + +log_error() { + logger -t ngcpcfg -- "Error: $*" + echo "Error: $*" >&2 +} + +log_debug() { + if [ -n "${DEBUG:-}" ] ; then + logger -t ngcpcfg -- "Debug: $*" + echo ; echo "DEBUG: $*" ; echo # newlines to avoid messup with cmdline output + fi +} +## }}} + +## important variables we depend on to operate successfully {{{ +# support test suite which requires system independent configuration +if [ -r ngcpcfg-testsuite.cfg ] ; then + . ngcpcfg-testsuite.cfg +else + if [ -r /etc/ngcp-config/ngcpcfg.cfg ] ; then + log_debug "sourcing configuration file /etc/ngcp-config/ngcpcfg.cfg" + . /etc/ngcp-config/ngcpcfg.cfg + + if [ -d /etc/ngcp-config/ngcpcfg.d ] ; then + for file in /etc/ngcp-config/ngcpcfg.d/*.cfg ; do + if test -r $file ; then + log_debug "sourcing configuration file $file" + . $file + fi + done + fi + elif [ -r /etc/ngcp-config-crypted.tgz.gpg ] ; then + log_error "Configuration pool locked. Please contact your distributor. Exiting." + exit 1 + else + log_error "Could not read configuration file /etc/ngcp-config/ngcpcfg.cfg. Exiting." + exit 1 + fi +fi + +if ! [ -r "$NGCPCTL_CONFIG" ] ; then + log_error "Configuration file ${NGCPCTL_CONFIG} does not exist (unconfigured?) - exiting." + exit 1 +fi + +if ! [ -r "$CONSTANTS_CONFIG" ] ; then + log_error "Constants file ${CONSTANTS_CONFIG} does not exist (unconfigured?) - exiting." + exit 1 +fi + +if ! [ -n "${NETWORK_CONFIG:-}" ] ; then + log_warn "NETWORK_CONFIG is not configured in $NGCPCTL_CONFIG - continuing anyway." +elif ! [ -r "$NETWORK_CONFIG" ] ; then + log_error "Constants file ${NETWORK_CONFIG} does not exist (unconfigured?) - exiting." + exit 1 +fi + +if ! [ -d "$TEMPLATE_POOL" ] ; then + log_error "No template directory (${TEMPLATE_POOL}) found - exiting." + exit 1 +fi +## }}} + +## environment variables {{{ +export PN="ngcpcfg" +export HNAME="$(hostname)" + +# avoid warnings by perl script complaining about locales +export LANG=C +export LC_ALL=C + +# make sure it's available in all helper scripts +[ -n "${DEBUG:-}" ] && export DEBUG + +# export for access via build_config etc +export CONFIG_POOL +export HOST_CONFIG +export LOCAL_CONFIG +export NGCPCTL_CONFIG +export CONSTANTS_CONFIG +export NETWORK_CONFIG +## }}} + +## HA / carrier features {{{ +if [ -r /usr/share/ngcp-ngcpcfg/functions/ha_features ] ; then + . /usr/share/ngcp-ngcpcfg/functions/ha_features + set_ha_file # set $HA_FILE for usage in generate_template_list +fi + +if [ -r /usr/share/ngcp-ngcpcfg/functions/carrier_features ] ; then + . /usr/share/ngcp-ngcpcfg/functions/carrier_features +fi +## }}} + +## functions {{{ +generate_template_list() { + [ -n "$TEMPLATE_POOL" ] || return 1 + + local filelist_prepared=$(mktemp) + local filelist_final=$(mktemp) + + # iterate over all files + for file in $(find "$TEMPLATE_POOL" -name \*.tt2 -o -name \*.tt2"${HA_FILE:-}") ; do + # *NO* arguments provided via cmdline + if [ -z "${1:-}" ] ; then + echo "$file" >> "${filelist_prepared}" + else + # arguments (file list/pattern) provided via cmdline + for arg in $* ; do + if echo $file | grep -q "${arg}" ; then + echo "$file" >> "${filelist_prepared}" + fi + done + fi + done + + # remove all filenames where a preferred filename exists + # foo.customtt.tt2.spX > foo.customtt.tt2 > foo.tt2.spX > foo.tt2 + for line in $(cat ${filelist_prepared}); do + + # reduce filenames from "foo.*" to "foo" + if [ -n "${HA_FILE:-}" ] ; then + normalized_filename="${line%${HA_FILE}}" + else + normalized_filename="${line}" + fi + normalized_filename="${normalized_filename%.customtt.tt2}" + normalized_filename="${normalized_filename%.tt2}" + + # foo.customtt.tt2.sp{1,2} + if [ -n "${HA_FILE:-}" ] ; then + if grep -q "^${normalized_filename}.customtt.tt2${HA_FILE:-}" "${filelist_prepared}" ; then + echo "${normalized_filename}.customtt.tt2${HA_FILE:-}" >> "${filelist_final}" + continue + fi + fi + + # foo.customtt.tt2 + if grep -q "^${normalized_filename}.customtt.tt2$" "${filelist_prepared}" ; then + echo "${normalized_filename}.customtt.tt2" >> "${filelist_final}" + continue + fi + + # foo.tt2.sp{1,2} + if [ -n "${HA_FILE:-}" ] ; then + if grep -q "^${normalized_filename}.tt2${HA_FILE}" "${filelist_prepared}" ; then + echo "${normalized_filename}.tt2${HA_FILE}" >> "${filelist_final}" + continue + fi + fi + + # file should be used, so list it + echo "$line" >> "${filelist_final}" + done + + # output file list, make sure we provide the file names just once + sort -u ${filelist_final} + + if [ -n "${DEBUG:-}" ] ; then + # send to stderr since stdout is used from outside + log_debug "Not removing temporary filelist files since we are in debug mode:" >&2 + log_debug " filelist_prepared = ${filelist_prepared}" >&2 + log_debug " filelist_final = ${filelist_final}" >&2 + else + rm -f "${filelist_prepared}" "${filelist_final}" + fi + + unset filelist_prepared filelist_final +} +## }}} + +## END OF FILE ################################################################# diff --git a/helper/build_config b/helper/build_config new file mode 100755 index 00000000..8c98ba85 --- /dev/null +++ b/helper/build_config @@ -0,0 +1,102 @@ +#!/bin/bash +# Filename: /usr/share/ngcp-ngcpcfg/helper/build_config +# Purpose: builds output configuration file based on tt2 template file +# using /usr/share/ngcp-ngcpcfg/helper/tt2-wrapper +################################################################################ + +set -e +set -u + +if [ "${#:-}" -ne 1 ] ; then + echo "Usage: /usr/share/ngcp-ngcpcfg/helper/build_config " >&2 + exit 1 +fi + +if [ -z "${CONFIG_POOL:-}" ] ; then + echo "Error: $CONFIG_POOL is not set." >&2 + exit 1 +fi + +# support for testsuite +if [ -z "${FUNCTIONS:-}" ] ; then + FUNCTIONS='/usr/share/ngcp-ngcpcfg/functions/' +fi + +if [ -z "${HELPER:-}" ] ; then + HELPER='/usr/share/ngcp-ngcpcfg/helper/' +fi + +. ${FUNCTIONS}/main + +# main script + +input_file="$1" # like /etc/ngcp-config/templates/etc/mysql/my.cnf.tt2 +# calculate output file {{{ + x=${input_file##${NGCPCTL_MAIN}/templates/} # drop leading /etc/ngcp-config + y=${x%%.tt2} # drop trailing suffix '.tt2' + [ -n "${HA_FILE:-}" ] && y=${y%%.tt2${HA_FILE}} # drop trailing suffix '.tt2.sp{1,2}' + y=${y%%.customtt} # drop trailing suffix '.customtt' +# }}} +export output_file="${y}" # like /etc/mysql/my.cnf, export variable + # for usage within {pre,post}build scripts + +# pre-execution script in template store: +if [ -r "${NGCPCTL_MAIN}/templates/${output_file}.prebuild" ] ; then + log_info "Executing prebuild for ${output_file}" + bash "${NGCPCTL_MAIN}/templates/${output_file}.prebuild" +elif [ -r "${NGCPCTL_MAIN}/templates/$(dirname ${output_file})/ngcpcfg.prebuild" ] ; then + log_info "Executing prebuild for ${output_file}" + bash "${NGCPCTL_MAIN}/templates/$(dirname ${output_file})/ngcpcfg.prebuild" +fi + +# if output directory does not exist yet, create it +if ! [ -d "$(dirname ${output_file})" ] ; then + umask 0022 # directory permissions should be '755' + mkdir -p "$(dirname ${output_file})" +fi + +# assume safe defaults +umask 0077 + +# read host specific configuration file only if it exists +[ -r "${HOST_CONFIG:-}" ] && host_conf="$HOST_CONFIG" + +# read local config only if it exists +[ -r "${LOCAL_CONFIG:-}" ] && local_conf="$LOCAL_CONFIG" + +TT_WRAPPER="${HELPER}/tt2-wrapper" + +log_debug "Output file ${output_file} based on ${input_file}" +log_debug "Executing: $TT_WRAPPER ${input_file} ${host_conf:-} ${local_conf:-} $NGCPCTL_CONFIG ${NETWORK_CONFIG:-} $CONSTANTS_CONFIG > ${output_file}" +if "$TT_WRAPPER" "${input_file}" ${host_conf:-} ${local_conf:-} "$NGCPCTL_CONFIG" "${NETWORK_CONFIG:-}" "$CONSTANTS_CONFIG" > "${output_file}" 2>/dev/null ; then + log_info "Generating ${output_file}: OK" + RC=0 +else + log_error "Generating ${output_file} based on ${input_file}: FAILED" + RC=1 + + log_info "NOTE: Check those files for valid syntax and encoding:" + for f in "${input_file}" ${host_conf:-} ${local_conf:-} "$NGCPCTL_CONFIG" "${NETWORK_CONFIG:-}" "$CONSTANTS_CONFIG" ; do + [ -r "$f" ] && log_info "$f" + done + log_info "Running /usr/share/ngcp-ngcpcfg/helper/tt2-wrapper should provide more details." + +fi + +# set permissions for generated config based on the ones of the template +chmod --reference="${input_file}" "${output_file}" +# finally drop all write permissions +chmod a-w "${output_file}" + +# post-execution script in template store: +if [ -r "${NGCPCTL_MAIN}/templates/${output_file}.postbuild" ] ; then + log_info "Executing postbuild for ${output_file}" + bash "${NGCPCTL_MAIN}/templates/${output_file}.postbuild" +elif [ -r "${NGCPCTL_MAIN}/templates/$(dirname ${output_file})/ngcpcfg.postbuild" ] ; then + log_info "Executing postbuild for ${output_file}" + bash "${NGCPCTL_MAIN}/templates/$(dirname ${output_file})/ngcpcfg.postbuild" +fi + +exit $RC + +## END OF FILE ################################################################# diff --git a/helper/fileformat_version b/helper/fileformat_version new file mode 100755 index 00000000..7909518d --- /dev/null +++ b/helper/fileformat_version @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# Purpose: report internal->fileformat setting of /etc/ngcp-config/config.yml +################################################################################ + +use strict; +use warnings; + +use YAML::Tiny; +my $yaml = YAML::Tiny->new; +$yaml = YAML::Tiny->read("/etc/ngcp-config/config.yml"); +my $fileformat = $yaml->[0]->{internal}->{fileformat}; + +if (defined $fileformat) { + print "$fileformat\n"; + exit 0; +} else { + print "undefined\n"; + exit 1; +} + +## END OF FILE ################################################################# diff --git a/helper/sort-yml b/helper/sort-yml new file mode 100755 index 00000000..9888ac93 --- /dev/null +++ b/helper/sort-yml @@ -0,0 +1,18 @@ +#!/usr/bin/perl -CSD +# Purpose: sort yaml configuration file +################################################################################ + +use strict; +use warnings; +use YAML::Tiny; + +my $yaml = YAML::Tiny->new; +my $inputfile = shift or die 'You did not specify an input file name'; +my $outputfile = shift or die 'You did not specify an ouput file name'; + +$yaml = YAML::Tiny->read($inputfile); + +open(my $fh, '>', "$outputfile") or die "Could not open $outputfile for writing"; +print $fh $yaml->write_string() or die "Could not write YAML to $outputfile"; + +## END OF FILE ################################################################# diff --git a/helper/sync-db b/helper/sync-db new file mode 100755 index 00000000..6b740143 --- /dev/null +++ b/helper/sync-db @@ -0,0 +1,188 @@ +#!/usr/bin/perl -CSD +# Purpose: template toolkit helper script +################################################################################ + +use strict; +use warnings; + +use YAML qw/LoadFile/; +use Template; +use Hash::Merge qw(merge); +use DBI; + +sub sync_extra_sockets; + +my $tt = Template->new({ ABSOLUTE => 1, RELATIVE => 1 }); + +my $config = {}; +foreach my $file (@ARGV) { + $config = merge($config, LoadFile($file) || die $!); +} + +open my $SWFH, '<', '/etc/mysql/sipwise.cnf'; +my $dbpass = join ' ', <$SWFH>; +close $SWFH; +$dbpass =~ s/^.*SIPWISE_DB_PASSWORD=\'([^\']+)\'.*$/$1/; +chomp $dbpass; +my $dbuser = 'sipwise'; +my $dbname = $config->{ossbss}->{provisioning}->{database}->{name}; + +unless(defined $dbname) { + print "Could not determine provisioning db name\n"; + exit 1; +} +unless(defined $dbuser) { + print "Could not determine provisioning db user\n"; + exit 1; +} +unless(defined $dbpass) { + print "Could not determine provisioning db password\n"; + exit 1; +} + +my $dbh = DBI->connect('DBI:mysql:'.$dbname, $dbuser, $dbpass); +unless(defined $dbh) { + print "Could not connect to database '$dbname': $DBI::errstr\n"; + exit 1; +} + +exit 1 unless(sync_extra_sockets()); + +$dbh->disconnect; + +# this part sync with kamailio db +$dbuser = $config->{kamailio}->{proxy}->{dbrwuser}; +$dbname = $config->{kamailio}->{proxy}->{dbname}; +$dbpass = $config->{kamailio}->{proxy}->{dbrwpw}; +unless(defined $dbname) { + print "Could not determine kamailio db name\n"; + exit 1; +} +unless(defined $dbuser) { + print "Could not determine kamailio db user\n"; + exit 1; +} +unless(defined $dbpass) { + print "Could not determine kamailio db password\n"; + exit 1; +} + +$dbh = DBI->connect('DBI:mysql:'.$dbname, $dbuser, $dbpass); +unless(defined $dbh) { + print "Could not connect to database '$dbname': $DBI::errstr\n"; + exit 1; +} + +exit 1 unless(sync_faxserver_uris()); +$dbh->disconnect; + +exit 0; + +## kamailio.lb.extra_sockets handling: ############################## +sub sync_extra_sockets { + my $pref_name = 'outbound_socket'; + my $sth = $dbh->prepare("select id from voip_preferences where attribute=?"); + $sth->execute($pref_name); + my $res = $sth->fetchrow_hashref(); + unless(defined $res) { + print "Could not find preference '$pref_name' in db\n"; + return; + } + my $pref_id = $res->{id}; + $sth->finish; + + my $enum_insert_sth = $dbh->prepare("insert into voip_preferences_enum (preference_id, label, value, usr_pref, dom_pref, peer_pref) values(?, ?, ?, 1, 1, 1)"); + my $enum_update_sth = $dbh->prepare("update voip_preferences_enum set value=? where id=?"); + my $enum_delete_sth = $dbh->prepare("delete from voip_preferences_enum where id=?"); + $sth = $dbh->prepare("select id, label, value from voip_preferences_enum where preference_id=?"); + $sth->execute($pref_id); + + my $sockets = $config->{kamailio}->{lb}->{extra_sockets}; + my $dbsockets = $sth->fetchall_hashref('label'); + $sth->finish; + + foreach my $row (keys %{$dbsockets}) { + $row = $dbsockets->{$row}; + next if($row->{label} eq 'default'); + if(!exists $sockets->{$row->{label}}) { + print "socket $row->{label} does not exist anymore in config, delete from db\n"; + $enum_delete_sth->execute($row->{id}); + } elsif($sockets->{$row->{label}} ne $row->{value}) { + print "update $row->{label}=$row->{value} to $row->{label}=$sockets->{$row->{label}} in db\n"; + $enum_update_sth->execute($sockets->{$row->{label}}, $row->{id}); + delete $sockets->{$row->{label}}; + } else { + print "socket $row->{label}=$row->{value} is sync between config and db\n"; + delete $sockets->{$row->{label}}; + } + } + foreach my $sock(keys %{$sockets}) { + print "insert new socket $sock=$sockets->{$sock} from config into db\n"; + $enum_insert_sth->execute($pref_id, $sock, $sockets->{$sock}); + } + + $enum_insert_sth->finish; + $enum_update_sth->finish; + $enum_delete_sth->finish; + return 1; +} + +## faxserver.hw_fax_gateways handling: ############################## +sub sync_faxserver_uris { + my $fax_set_id = 4; + my $reload_dispatcher = 0; + my $dispatcher_insert_sth = $dbh->prepare("insert into dispatcher (setid, destination, flags, priority, description) values(4, ?, 0, 0, 'Fax2Mail Servers')"); + my $dispatcher_delete_sth = $dbh->prepare("delete from dispatcher where id=?"); + + my $sth = $dbh->prepare("select id,destination from dispatcher where setid=?"); + $sth->execute($fax_set_id); + my $db_fax_gateways = $sth->fetchall_hashref('destination'); + $sth->finish; + + # array reference + my $yaml_fax_gateways = $config->{faxserver}->{fax_gateways}; + unless(defined $yaml_fax_gateways) { + return 1; + } + my @tmp_array = @{$yaml_fax_gateways}; + # turn the array into hash reference + my $fax_gateways = {}; + foreach my $gw (@tmp_array) { + # e.g: sip:127.0.0.1:5070/255.255.255.0, remove /255.255.255.0 + my @new_gw_parts = split('/', $gw); + my $gw_sip_parts = $new_gw_parts[0]; + $fax_gateways->{$gw_sip_parts} = 1 ; + } + + foreach my $row (keys %{$db_fax_gateways}) { + $row = $db_fax_gateways->{$row}; + if(!exists $fax_gateways->{$row->{destination}}) { + print "fax gateway $row->{destination} does not exist anymore in config, delete from db\n"; + $dispatcher_delete_sth->execute($row->{id}); + $reload_dispatcher ||= 1; + } else { + print "fax gateway $row->{destination} sync between config and db\n"; + delete $fax_gateways->{$row->{destination}}; + } + } + foreach my $fax_gw(keys %{$fax_gateways}) { + print "insert new fax gateway $fax_gw from config into db\n"; + $dispatcher_insert_sth->execute($fax_gw); + $reload_dispatcher ||= 1; + } + + $dispatcher_insert_sth->finish; + $dispatcher_delete_sth->finish; + + if($reload_dispatcher){ + print "fax gateway changes, need to reload kamailio dispatcher ... \n"; + my $result = `ngcp-kamctl proxy dispatcher reload`; + print "reload kamailio dispatcher done, status was: $? \n"; + } + + return 1; + +} + + +## END OF FILE ################################################################# diff --git a/helper/tt2-wrapper b/helper/tt2-wrapper new file mode 100755 index 00000000..99849a17 --- /dev/null +++ b/helper/tt2-wrapper @@ -0,0 +1,26 @@ +#!/usr/bin/perl -CSD +# Purpose: template toolkit helper script +################################################################################ + +use strict; +use warnings; + +use YAML qw/LoadFile/; +use Template; +use Hash::Merge qw(merge); + +my $tt = Template->new({ ABSOLUTE => 1, RELATIVE => 1, EVAL_PERL => 1 }); +my $template = shift(@ARGV); + +my $config = {}; +foreach my $file (@ARGV) { + $config = merge($config, LoadFile($file) || die $!); +} + +# NOTE: we can't rely on "$tt->process($template, $config)" because the config +# file is UTF8 encoded but is then being read as a binary 8bit file and written +# as UTF8 file again, causing wide characters getting encoded twice +open my $fh, '<', $template or die "Unable to open file for reading: $!"; +$tt->process($fh, $config) or die $tt->error; + +## END OF FILE ################################################################# diff --git a/helper/validate-yml b/helper/validate-yml new file mode 100755 index 00000000..bab00325 --- /dev/null +++ b/helper/validate-yml @@ -0,0 +1,17 @@ +#!/usr/bin/perl -CSD +# Purpose: validate yml file format +################################################################################ + +use strict; +use warnings; +use YAML::Tiny; +use File::Temp qw/tempfile/; + +my $yaml = YAML::Tiny->new; +my $inputfile = shift or die 'You did not specify an input file name'; +my $outputfile = new File::Temp( UNLINK => 1 ); +$yaml = YAML::Tiny->read($inputfile); +open(my $fh, '>', "$outputfile") or die "Could not open $outputfile for writing"; +print $fh $yaml->write_string() or die "Could not write YAML to $outputfile"; + +## END OF FILE ################################################################# diff --git a/lib/get_all_adv_ips b/lib/get_all_adv_ips new file mode 100644 index 00000000..3f60088b --- /dev/null +++ b/lib/get_all_adv_ips @@ -0,0 +1,30 @@ +[% + # Return an array of advertised IPs from the interface of a given + # type for all nodes which act as a given role + # + # @param argv.role The role of the node to process + # One of: proxy, lb, mgmt + # @param argv.type The interface type of a node to extract + # the advertised ips from + # One of: web_int, web_ext, sip_int, sip_ext, ... + # @return out The array of advertised ips +-%] +[% blktmp.processed_hosts = {} -%] +[% out = [] -%] +[% FOREACH host IN hosts.keys -%] +[% IF hosts.$host.role.grep('^' _ argv.role _ '$').size() && !blktmp.processed_hosts.$host.defined -%] +[% FOREACH iface IN hosts.$host.interfaces -%] +[% FOREACH realiface IN hosts.$host.keys -%] +[% IF realiface == iface -%] +[% IF hosts.$host.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% peer = hosts.$host.peer -%] +[% blktmp.processed_hosts.$peer = 1 -%] +[% FOREACH ip IN hosts.$host.$realiface.advertised_ip -%] +[% out.push(ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_adv_ips_for_host b/lib/get_all_adv_ips_for_host new file mode 100644 index 00000000..a4329604 --- /dev/null +++ b/lib/get_all_adv_ips_for_host @@ -0,0 +1,21 @@ +[% + # Return an array of advertised IPs for a given host + # + # @param argv.host The host to get all shared IPs for. + # @param argv.type The interface type or empty string for all types. + # @return out The array of advertised ips. +-%] +[% IF !hosts.${argv.host}.defined -%][% argv.host = 'self' -%][% END -%] +[% out = [] -%] +[% IF !argv.type.length -%][% argv.type = '.*' -%][% END -%] +[% FOREACH iface IN hosts.${argv.host}.interfaces -%] +[% FOREACH realiface IN hosts.${argv.host}.keys -%] +[% IF realiface == iface -%] +[% IF hosts.${argv.host}.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% FOREACH ip IN hosts.${argv.host}.$realiface.advertised_ip -%] +[% out.push(ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_hosts_ips b/lib/get_all_hosts_ips new file mode 100644 index 00000000..368f1429 --- /dev/null +++ b/lib/get_all_hosts_ips @@ -0,0 +1,31 @@ +[% + # Return an array of hashes with name and ip within a given type + # for all nodes. + # + # @param argv.role The role of the node to process + # One of: proxy, lb, mgmt + # @param argv.type The interface type of a node to extract + # the ip from + # One of: web_int, web_ext, sip_int, sip_ext, ... + # @return out The array of hashes with fields "name" and "ip", e.g.: + # [ + # {name=>'sp1', ip='192.168.255.251'}, + # {name=>'sp2', ip='192.168.255.252'} + # ] +-%] +[% out = [] -%] +[% FOREACH host IN hosts.keys -%] +[% IF hosts.$host.role.grep('^' _ argv.role _ '$').size() -%] +[% FOREACH iface IN hosts.$host.interfaces -%] +[% FOREACH realiface IN hosts.$host.keys -%] +[% IF realiface == iface -%] +[% IF hosts.$host.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% IF !out.grep('^' _ hosts.$host.$realiface.ip _ '$').size() -%] +[% out.push({ name = host, ip = hosts.$host.$realiface.ip }) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_ips b/lib/get_all_ips new file mode 100644 index 00000000..afa64bf2 --- /dev/null +++ b/lib/get_all_ips @@ -0,0 +1,27 @@ +[% + # Return an array of IPs from the interface of a given + # type for all nodes which act as a given role + # + # @param argv.role The role of the node to process + # One of: proxy, lb, mgmt + # @param argv.type The interface type of a node to extract + # the ip from + # One of: web_int, web_ext, sip_int, sip_ext, ... + # @return out The array of ips +-%] +[% out = [] -%] +[% FOREACH host IN hosts.keys -%] +[% IF hosts.$host.role.grep('^' _ argv.role _ '$').size() -%] +[% FOREACH iface IN hosts.$host.interfaces -%] +[% FOREACH realiface IN hosts.$host.keys -%] +[% IF realiface == iface -%] +[% IF hosts.$host.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% IF !out.grep('^' _ hosts.$host.$realiface.ip _ '$').size() -%] +[% out.push(hosts.$host.$realiface.ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_ips_for_host b/lib/get_all_ips_for_host new file mode 100644 index 00000000..b8bd474d --- /dev/null +++ b/lib/get_all_ips_for_host @@ -0,0 +1,19 @@ +[% + # Return an array of IPs for a given host. + # + # @param argv.host The host to get all IPs for. + # @param argv.type The interface type or empty string for all types. + # @return out The array of ips. +-%] +[% IF !hosts.${argv.host}.defined -%][% argv.host = 'self' -%][% END -%] +[% out = [] -%] +[% IF !argv.type.length -%][% argv.type = '.*' -%][% END -%] +[% FOREACH iface IN hosts.${argv.host}.interfaces -%] +[% FOREACH realiface IN hosts.${argv.host}.keys -%] +[% IF realiface == iface -%] +[% IF hosts.${argv.host}.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% out.push(hosts.${argv.host}.$realiface.ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_shared_ips b/lib/get_all_shared_ips new file mode 100644 index 00000000..01c3ea81 --- /dev/null +++ b/lib/get_all_shared_ips @@ -0,0 +1,30 @@ +[% + # Return an array of shared IPs from the interface of a given + # type for all nodes which act as a given role + # + # @param argv.role The role of the node to process + # One of: proxy, lb, mgmt + # @param argv.type The interface type of a node to extract + # the shared ip from + # One of: web_int, web_ext, sip_int, sip_ext, ... + # @return out The array of shared ips +-%] +[% blktmp.processed_hosts = {} -%] +[% out = [] -%] +[% FOREACH host IN hosts.keys -%] +[% IF hosts.$host.role.grep('^' _ argv.role _ '$').size() && !blktmp.processed_hosts.$host.defined -%] +[% FOREACH iface IN hosts.$host.interfaces -%] +[% FOREACH realiface IN hosts.$host.keys -%] +[% IF realiface == iface -%] +[% IF hosts.$host.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% peer = hosts.$host.peer -%] +[% blktmp.processed_hosts.$peer = 1 -%] +[% FOREACH ip IN hosts.$host.$realiface.shared_ip -%] +[% out.push(ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_shared_ips_for_host b/lib/get_all_shared_ips_for_host new file mode 100644 index 00000000..4a1faae4 --- /dev/null +++ b/lib/get_all_shared_ips_for_host @@ -0,0 +1,21 @@ +[% + # Return an array of shared IPs for a given host + # + # @param argv.host The host to get all shared IPs for. + # @param argv.type The interface type or empty string for all types. + # @return out The array of shared ips. +-%] +[% IF !hosts.${argv.host}.defined -%][% argv.host = 'self' -%][% END -%] +[% out = [] -%] +[% IF !argv.type.length -%][% argv.type = '.*' -%][% END -%] +[% FOREACH iface IN hosts.${argv.host}.interfaces -%] +[% FOREACH realiface IN hosts.${argv.host}.keys -%] +[% IF realiface == iface -%] +[% IF hosts.${argv.host}.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% FOREACH ip IN hosts.${argv.host}.$realiface.shared_ip -%] +[% out.push(ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_shared_v6ips b/lib/get_all_shared_v6ips new file mode 100644 index 00000000..06513f17 --- /dev/null +++ b/lib/get_all_shared_v6ips @@ -0,0 +1,30 @@ +[% + # Return an array of shared IPv6 addresses from the interface of a given + # type for all nodes which act as a given role + # + # @param argv.role The role of the node to process + # One of: proxy, lb, mgmt + # @param argv.type The interface type of a node to extract + # the shared ip from + # One of: web_int, web_ext, sip_int, sip_ext, ... + # @return out The array of shared IPv6 addresses +-%] +[% blktmp.processed_hosts = {} -%] +[% out = [] -%] +[% FOREACH host IN hosts.keys -%] +[% IF hosts.$host.role.grep('^' _ argv.role _ '$').size() && !blktmp.processed_hosts.$host.defined -%] +[% FOREACH iface IN hosts.$host.interfaces -%] +[% FOREACH realiface IN hosts.$host.keys -%] +[% IF realiface == iface -%] +[% IF hosts.$host.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% peer = hosts.$host.peer -%] +[% blktmp.processed_hosts.$peer = 1 -%] +[% FOREACH ip IN hosts.$host.$realiface.shared_v6ip -%] +[% out.push(ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_shared_v6ips_for_host b/lib/get_all_shared_v6ips_for_host new file mode 100644 index 00000000..431acd51 --- /dev/null +++ b/lib/get_all_shared_v6ips_for_host @@ -0,0 +1,21 @@ +[% + # Return an array of shared IPs for a given host + # + # @param argv.host The host to get all shared IPs for. + # @param argv.type The interface type or empty string for all types. + # @return out The array of shared ips +-%] +[% IF !hosts.${argv.host}.defined -%][% argv.host = 'self' -%][% END -%] +[% out = [] -%] +[% IF !argv.type.length -%][% argv.type = '.*' -%][% END -%] +[% FOREACH iface IN hosts.${argv.host}.interfaces -%] +[% FOREACH realiface IN hosts.${argv.host}.keys -%] +[% IF realiface == iface -%] +[% IF hosts.${argv.host}.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% FOREACH ip IN hosts.${argv.host}.$realiface.shared_v6ip -%] +[% out.push(ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_v6ips b/lib/get_all_v6ips new file mode 100644 index 00000000..2ff31187 --- /dev/null +++ b/lib/get_all_v6ips @@ -0,0 +1,25 @@ +[% + # Return an array of IPv6 addresses from the interface of a given + # type for all nodes which act as a given role + # + # @param argv.role The role of the node to process + # One of: proxy, lb, mgmt + # @param argv.type The interface type of a node to extract + # the ip from + # One of: web_int, web_ext, sip_int, sip_ext, ... + # @return out The array of ips +-%] +[% out = [] -%] +[% FOREACH host IN hosts.keys -%] +[% IF hosts.$host.role.grep('^' _ argv.role _ '$').size() -%] +[% FOREACH iface IN hosts.$host.interfaces -%] +[% FOREACH realiface IN hosts.$host.keys -%] +[% IF realiface == iface -%] +[% IF hosts.$host.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% out.push(hosts.$host.$realiface.v6ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_all_v6ips_for_host b/lib/get_all_v6ips_for_host new file mode 100644 index 00000000..afef6f6a --- /dev/null +++ b/lib/get_all_v6ips_for_host @@ -0,0 +1,22 @@ +[% + # Return an array of IPs for a given host. The order is iface1_sharedip_1, + # iface1_sharedip_n, iface1_ip, iface2_sharedip_1, ... + # + # @param argv.host The host to get all IPs for. + # @param argv.type The interface type or empty string for all types. + # @return out The array of ips. +-%] +[% IF !hosts.${argv.host}.defined -%][% argv.host = 'self' -%][% END -%] +[% out = [] -%] +[% IF !argv.type.length -%][% argv.type = '.*' -%][% END -%] +[% FOREACH iface IN hosts.${argv.host}.interfaces -%] +[% FOREACH realiface IN hosts.${argv.host}.keys -%] +[% IF realiface == iface -%] +[% IF hosts.${argv.host}.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% IF hosts.${argv.host}.$realiface.v6ip.defined -%] +[% out.push(hosts.${argv.host}.$realiface.v6ip) -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] diff --git a/lib/get_firstname b/lib/get_firstname new file mode 100644 index 00000000..b3903175 --- /dev/null +++ b/lib/get_firstname @@ -0,0 +1,15 @@ +[% + # Returns the (alphabetically) first hostname of a node pair for + # a given host. + # + # @param argv.host The hostname to get the first name for. + # @return out The alphabetically first name. +-%] +[% IF !hosts.${argv.host}.defined -%] +[% argv.host = 'self' -%] +[% END -%] +[% blktmp.hosts = [argv.host] -%] +[% IF hosts.${argv.host}.peer.defined -%] +[% blktmp.hosts.push(hosts.${argv.host}.peer) -%] +[% END -%] +[% out = blktmp.hosts.sort.0 -%] diff --git a/lib/get_hostname b/lib/get_hostname new file mode 100644 index 00000000..36b49d4a --- /dev/null +++ b/lib/get_hostname @@ -0,0 +1,15 @@ +[% + # Returns the hostname of the node calling this function. + # + # @return out The hostname of the node calling this function. +-%] +[% PERL -%] +# don't trust hostname(1) as this might differ from the hostname of +# the system which runs the installer, instead rely on /etc/hostname +open my $hh, '<', '/etc/hostname' or die "Error opening /etc/hostname"; +my $hostname = <$hh>; +close $hh; +chomp $hostname; +die "Fatal error retrieving hostname [$hostname]" unless length $hostname; +$stash->set(out => $hostname); +[% END -%] diff --git a/lib/get_iface b/lib/get_iface new file mode 100644 index 00000000..9d410006 --- /dev/null +++ b/lib/get_iface @@ -0,0 +1,23 @@ +[% + # Returns the interface name with a certain type for a given host + # + # @param argv.host The hostname to get the interface from. + # @param argv.type The interface type + # One of: web_int, web_ext, sip_int, sip_ext, ... + # @return out The interface name +-%] +[% IF !hosts.${argv.host}.defined -%] +[% argv.host = 'self' -%] +[% END -%] +[% out = '' -%] +[% FOREACH iface IN hosts.${argv.host}.interfaces -%] +[% FOREACH realiface IN hosts.${argv.host}.keys -%] +[% IF realiface == iface -%] +[% IF hosts.${argv.host}.$realiface.type.grep('^' _ argv.type _ '$').size() -%] +[% out = realiface -%] +[% RETURN -%] +[% END -%] +[% END -%] +[% END -%] +[% END -%] + diff --git a/lib/get_peername b/lib/get_peername new file mode 100644 index 00000000..055b4aa1 --- /dev/null +++ b/lib/get_peername @@ -0,0 +1,10 @@ +[% + # Returns the peer name of a given host + # + # @param argv.host The hostname to get the peername for. + # @return out The peername. +-%] +[% IF !hosts.${argv.host}.defined -%] +[% argv.host = 'self' -%] +[% END -%] +[% out = hosts.${argv.host}.peer -%] diff --git a/lib/has_role b/lib/has_role new file mode 100644 index 00000000..7a1f1db5 --- /dev/null +++ b/lib/has_role @@ -0,0 +1,15 @@ +[% + # Checks whether a given host has a given role + # + # @param argv.host The hostname to check the role for + # @param argv.role The role of the node to process + # One of: proxy, lb, mgmt + # @return out 0 if false, 1 if true +-%] +[% IF !hosts.${argv.host}.defined -%] +[% argv.host = 'self' -%] +[% END -%] +[% out = 0 -%] +[% IF hosts.${argv.host}.role.grep('^' _ argv.role _ '$').size() -%] +[% out = 1 -%] +[% END -%] diff --git a/sbin/ngcp-network b/sbin/ngcp-network new file mode 100755 index 00000000..55948c0a --- /dev/null +++ b/sbin/ngcp-network @@ -0,0 +1,781 @@ +#!/usr/bin/perl -CSD + +use warnings; +use strict; + +use Carp; +use Data::Validate::IP; +use English qw( -no_match_vars ); +use Getopt::Long; +use IO::Interface; +use IO::Socket; +use IPC::Open3; +use List::MoreUtils qw{ any }; +use Net::Netmask; +use Pod::Usage; +use Regexp::IPv6 qw($IPv6_re); +use Socket; +use Sys::Hostname; +use YAML::Tiny; + +our $VERSION = 'UNRELEASED'; + +# defaults / option handling {{{ +open my $hh, '<', '/etc/hostname' + or croak "Error opening /etc/hostname: $ERRNO"; +my $host = <$hh>; +close $hh or croak 'Error closing file handling for /etc/hostname'; +chomp $host; +length $host or croak "Fatal error retrieving hostname [$host]"; + +my $advertised_ip; +my $bond_miimon; +my $bond_mode; +my $bond_slaves; +my $broadcast; +my @dns_nameservers; +my $gateway; +my $help; +my $hwaddr; +my $inputfile = '/etc/ngcp-config/network.yml'; +my $internal_iface; +my $ip; +my $ip_v6; +my $man; +my $move_from; +my $move_to; +my $netmask; +my $outputfile = $inputfile; +my $peer; +my @remove_host; +my @remove_interface; +my @roles; +my @set_interface; +my $shared_ip; +my $shared_ip_v6; +my @type; +my $verbose; +my $version; +my $vlan_raw_device; + +GetOptions( + 'advertised-ip=s' => \$advertised_ip, + 'bond-miimon=s' => \$bond_miimon, + 'bond-mode=s' => \$bond_mode, + 'bond-slaves=s' => \$bond_slaves, + 'broadcast=s' => \$broadcast, + 'dns=s' => \@dns_nameservers, + 'gateway=s' => \$gateway, + 'help' => \$help, + 'host=s' => \$host, + 'hwaddr=s' => \$hwaddr, + 'input-file=s' => \$inputfile, + 'internal-iface=s' => \$internal_iface, + 'ip=s' => \$ip, + 'ipv6=s' => \$ip_v6, + 'man' => \$man, + 'move-from=s' => \$move_from, + 'move-to=s' => \$move_to, + 'netmask=s' => \$netmask, + 'output-file=s' => \$outputfile, + 'peer=s' => \$peer, + 'remove-host=s' => \@remove_host, + 'remove-interface=s' => \@remove_interface, + 'role=s' => \@roles, + 'set-interface=s' => \@set_interface, + 'shared-ip=s' => \$shared_ip, + 'shared-ipv6=s' => \$shared_ip_v6, + 'type=s' => \@type, + 'verbose' => \$verbose, + 'version' => \$version, + 'vlan-raw-device=s' => \$vlan_raw_device, +) or pod2usage(2); + +if ($help) { + pod2usage(0); +} + +if ($version) { + print "$PROGRAM_NAME, v$version\n"; + exit 0; +} + +if ($man) { + pod2usage( -exitstatus => 0, -verbose => 2 ); +} + +# validate input +if ($ip) { + logger("validating specified IP address $ip"); + if ( is_ipv4($ip) || $ip =~ /^(auto|none|delete)$/msx ) { + logger('valid IPv4 address'); + } + else { + croak "Specified IP $ip is not a valid IPv4 address"; + } +} + +if ($ip_v6) { + logger("validating specified IP address $ip_v6"); + if ( is_ipv6($ip_v6) || $ip_v6 =~ /^(auto|none|delete)$/msx ) { + logger('valid IPv6 address'); + } + else { + croak "Specified IP $ip_v6 is not a valid IPv6 address"; + } +} + +foreach my $opt ( + $bond_miimon, $bond_mode, $broadcast, @dns_nameservers, + $gateway, @set_interface, $host, $hwaddr, + $internal_iface, $ip, $ip_v6, $move_from, + $move_to, $netmask, $peer, @remove_host, + @remove_interface, @roles, @type, $vlan_raw_device + ) +{ + if ( defined $opt && $opt =~ /\s/msx ) { + logger("invalid option argument $opt"); + croak "Command line option does not accept whitespace in '$opt'."; + } +} + +# }}} + +my $yaml = YAML::Tiny->new; +logger("reading input file $inputfile"); +$yaml = YAML::Tiny->read($inputfile) + or croak "File $inputfile could not be read"; + +if ( -e "$outputfile" && !-w "$outputfile" ) { + croak "Could not open $outputfile for writing (are you user root?)"; +} + +my $spce; +if ( defined $yaml->[0]->{hosts}->{self} ) { + logger('host "self" identified and set, assuming spce system'); + $host = 'self'; + $spce = 1; +} + +# helper functions {{{ +sub logger { + if ($verbose) { + print "@_\n" or croak 'Error sending log output'; + } + return; +} + +sub get_interface_details { + my $interface = shift or croak "Usage: $PROGRAM_NAME "; + my $setting = shift or croak "Usage: $PROGRAM_NAME "; + + my $s = IO::Socket::INET->new( Proto => 'tcp' ); + return $s->$setting($interface); +} + +sub get_ip6_addr { + + my $interface = shift or croak 'Usage: runcmd '; + my $cmd = 'ip'; + my @args = ( '-6', 'addr', 'show', 'dev', $interface, 'scope', 'global' ); + + logger("get_ip6_addr for device $interface"); + logger("$cmd @args"); + + my $childpid = open3 'HIS_WRITE', 'HIS_OUT', 'HIS_ERR', $cmd, @args; + my @stderr = ; + my @stdout = ; + + logger("stdout: @stdout"); + logger("stderr: @stderr"); + + close HIS_OUT or croak 'Failed to close stdout'; + close HIS_ERR or croak 'Failed to close stderr'; + + waitpid $childpid, 0; + if ($CHILD_ERROR) { + croak "Problem with execution [return code $CHILD_ERROR]:\n@stderr"; + } + + foreach my $line (@stdout) { + if ( $line =~ /^\s*inet6\s+($IPv6_re)\/\d+.*scope.*global.*$/msx ) { + return $1; + } + } + + return; +} + +sub set_interface { + my $iface = shift; + + logger("set_interface: interface = $iface"); + logger("set_interface: host = $host"); + + if ( defined $ip && $ip =~ /^auto$/msx ) { + logger("get_interface_details( $iface, 'if_addr' );"); + $ip = get_interface_details( $iface, 'if_addr' ); + } + + if ( defined $ip_v6 && $ip_v6 =~ /^auto$/msx ) { + logger("get_interface_details( $iface, 'if_addr' );"); + $ip_v6 = get_ip6_addr($iface); + } + + if ( defined $netmask && $netmask =~ /^auto$/msx ) { + logger("get_interface_details( $iface, 'if_netmask' );"); + $netmask = get_interface_details( $iface, 'if_netmask' ); + } + + if ( defined $hwaddr && $hwaddr =~ /^auto$/msx ) { + logger("get_interface_details( $iface, 'if_hwaddr' );"); + $hwaddr = get_interface_details( $iface, 'if_hwaddr' ); + } + + my $settings = { + 'advertised_ip' => $advertised_ip, + 'bond_miimon' => $bond_miimon, + 'bond_mode' => $bond_mode, + 'bond_slaves' => $bond_slaves, + 'broadcast' => $broadcast, + 'gateway' => $gateway, + 'hwaddr' => $hwaddr, + 'ip' => $ip, + 'netmask' => $netmask, + 'shared_ip' => $shared_ip, + 'shared_v6ip' => $shared_ip_v6, + 'v6ip' => $ip_v6, + 'vlan_raw_device' => $vlan_raw_device, + }; + + foreach my $k ( keys %{$settings} ) { + if ( defined $settings->{$k} ) { + if ( $settings->{$k} =~ /^none$/msx ) { + logger("unsetting entry $k"); + undef $yaml->[0]->{hosts}->{$host}->{$iface}->{$k}; + } + elsif ( $settings->{$k} =~ /^delete$/msx ) { + logger("deleting entry $k"); + delete $yaml->[0]->{hosts}->{$host}->{$iface}->{$k}; + } + else { + logger("$k: $settings->{$k}"); + if($k eq 'shared_ip' || $k eq 'shared_v6ip' || $k eq 'advertised_ip') { + push @{ $yaml->[0]->{hosts}->{$host}->{$iface}->{$k} }, $settings->{$k}; + } + else { + $yaml->[0]->{hosts}->{$host}->{$iface}->{$k} = $settings->{$k}; + } + } + } + } + + if (@dns_nameservers) { + foreach my $dns (@dns_nameservers) { + logger("set_iface_config( $iface, 'dns_nameservers', $dns)"); + set_iface_config( $iface, 'dns_nameservers', $dns ); + } + } + + if (@type) { + foreach my $type (@type) { + logger("set_iface_config( $iface, 'type', $type)"); + set_iface_config( $iface, 'type', $type ); + } + } + + # add interface to list of available interfaces + my $ifaces = $yaml->[0]->{hosts}->{$host}->{interfaces}; + if ( !defined $ifaces ) { + logger("no interfaces defined yet, adding interface $iface"); + $yaml->[0]->{hosts}->{$host}->{interfaces}->[0] = "$iface"; + } + else { + logger("interface = $iface"); + + if ( any { /^${iface}$/msx } @{$ifaces} ) { + logger("interface $iface already listed on host $host"); + } + else { + push @{$ifaces}, $iface; + logger("adding $iface to list of interfaces on host $host"); + } + } + + return; +} + +sub remove_interface { + my $rem_iface = shift or croak 'Usage: remove_interface '; + + delete $yaml->[0]->{hosts}->{$host}->{$rem_iface}; + + my $ifaces = $yaml->[0]->{hosts}->{$host}->{interfaces}; + if ( defined $ifaces ) { + logger("removing interface @$ifaces as requested"); + undef @{$ifaces}; + } + return; +} + +sub set_yml_config { + my $setting = shift or croak 'Usage: set_yml_config '; + my $value = shift or croak 'Usage: set_yml_config '; + + $yaml->[0]->{hosts}->{$host}->{$setting} = "$value"; + logger("\$yaml->[0]->{hosts}->{\$host}->{$setting} = $value"); + return; +} + +sub set_iface_config { + my $iface = shift + or croak 'Usage: set_iface_config '; + my $setting = shift + or croak 'Usage: set_iface_config '; + my $value = shift + or croak 'Usage: set_iface_config '; + + if ( any { /^${value}$/msx } + @{ $yaml->[0]->{hosts}->{$host}->{$iface}->{$setting} } ) + { + logger("$value for $setting on $iface already defined in host $host"); + } + else { + push @{ $yaml->[0]->{hosts}->{$host}->{$iface}->{$setting} }, $value; + logger( + "\$yaml->[0]->{hosts}->{\$host}->{$iface}->{$setting} => $value"); + } + + return; +} + +sub set_role { + my $new_role = shift or croak 'Usage: set_role '; + + if ( any { /^${new_role}$/msx } @{ $yaml->[0]->{hosts}->{$host}->{role} } ) + { + logger("role $new_role already defined in host $host"); + } + else { + push @{ $yaml->[0]->{hosts}->{$host}->{role} }, $new_role; + logger("\$yaml->[0]->{hosts}->{\$host}->{role} => $new_role"); + } + + return; +} + +sub move_settings { + my $from = shift; + my $to = shift; + + if ( defined $from && !defined $to ) { + croak '--move-from option must be used with --move-to option together'; + } + + if ( !defined $from && defined $to ) { + croak '--move-to option must be used with --move-from option together'; + } + + # ha, nothing to do for us + if ( !defined $from && !defined $to ) { + return; + } + + logger("from = $from"); + logger("to = $to"); + + if (@roles) { + foreach my $role (@roles) { + logger("role = $role"); + + # get rid of the entry from the old section + my @tmp = + grep { !/^${role}$/msx } + @{ $yaml->[0]->{hosts}->{$from}->{role} }; + $yaml->[0]->{hosts}->{$from}->{role} = []; + push @{ $yaml->[0]->{hosts}->{$from}->{role} }, @tmp; + + # add it to its new place + push @{ $yaml->[0]->{hosts}->{$to}->{role} }, $role; + } + } + + if (@type) { + foreach my $type (@type) { + logger("type = $type"); + + # get rid of the entry from the old section + my @tmp = + grep { !/^${type}$/msx } + @{ $yaml->[0]->{hosts}->{$host}->{$from}->{type} }; + $yaml->[0]->{hosts}->{$host}->{$from}->{type} = []; + push @{ $yaml->[0]->{hosts}->{$host}->{$from}->{type} }, @tmp; + + # add it to its new place, but only if not already defined yet + if ( any { /^${type}$/msx } + @{ $yaml->[0]->{hosts}->{$host}->{$to}->{type} } ) + { + logger("type $type is already defined on host $host, interface $to"); + } + else { + push @{ $yaml->[0]->{hosts}->{$host}->{$to}->{type} }, $type; + } + } + } + + return; +} + +# }}} + +# main execution {{{ +if (@set_interface) { + foreach my $interface (@set_interface) { + logger("set_interface($interface)"); + set_interface($interface); + } +} + +if (@remove_host) { + foreach my $rhost (@remove_host) { + logger("removing host $rhost"); + delete $yaml->[0]->{hosts}->{$rhost}; + } +} + +if (@remove_interface) { + foreach my $riface (@remove_interface) { + logger("remove_interface($riface)"); + remove_interface($riface); + } +} + +if ( @roles && ( !defined $move_from || !defined $move_to ) ) { + foreach my $role (@roles) { + logger("set_role($role)"); + set_role($role); + } +} + +if ( defined $peer && ( !defined $move_from || !defined $move_to ) ) { + logger("set_yml_config('peer', $peer)"); + set_yml_config( 'peer', $peer ); +} + +if ( defined $internal_iface + && ( !defined $move_from || !defined $move_to ) ) +{ + logger("set_yml_config('internal_iface', $internal_iface)"); + set_yml_config( 'internal_iface', $internal_iface ); +} + +move_settings( $move_from, $move_to ); + +open my $fh, '>', "$outputfile" + or croak "Could not open $outputfile for writing"; +logger("writing output file $outputfile"); +print {$fh} $yaml->write_string() + or croak "Could not write YAML to $outputfile"; +close $fh or croak "Couldn't close '$fh': $OS_ERROR"; + +# }}} + +__END__ + +=head1 NAME +ngcp-network - command line interface to ngcp's network configuration settings + +=head1 SYNOPSIS +ngcp-network + +=head1 OPTIONS + +=over 8 + +=item B<--advertised-ip=> + +Set advertised_ip configuration to specified argument. + +=item B<--bond-miimon=> + +Set bond_miimon configuration to specified argument. + +=item B<--bond-mode=> + +Set bond_mode configuration to specified argument. + +=item B<--bond-slaves=> + +Set bond_slaves configuration to specified argument. + +=item B<--broadcast=> + +Set broadcast configuration to specified argument. + +=item B<--dns=> + +Set dns_nameservers configuration to specified argument. +Can be specified multiple times in one single command line. + +=item B<--gateway=> + +Set gateway configuration to specified argument. + +=item B<--help> + +Print the help message and exit. + +=item B<--host=> + +Apply configuration changes for specified host entry instead of using the +hostname of the currently running system. NOTE: If running the sip:provider CE +edition this configuration option can't be changed as the only configured host +is set to and supposed to be 'self' there. + +=item B<--hwaddr=> + +Set hwaddr configuration (MAC address of network device) to specified argument. + +=item B<--input-file=> + +Use specified file as input, defaults to /etc/ngcp-config/network.yml if unset. + +=item B<--internal-iface=> + +Set internal-iface configuration to specified argument. + +=item B<--ip=> + +Set ip configuration (IPv4 address) to specified argument. If set to 'auto' and +the selected interface is available on the running host then the IP address will +be determined based on its current settings. + +=item B<--ipv6=> + +Set ip configuration (IPv6 address) to specified argument. If set to 'auto' and +the selected interface is available on the running host then the IP address will +be determined based on its current settings. + +=item B<--man> + +Prints the manual page and exits. + +=item B<--move-from=> + +Move item from specified level (being host for --role and interface for --type). +The item needs to be chosen via --type or --role. To be used in combination with +--move-to=.... + +=item B<--move-to=> + +Move item to specified level (being host for --role and interface for --type). +The item needs to be chosen via --type or --role. To be used in combination with +--move-to=.... + +=item B<--netmask=> + +Set netmask configuration to specified argument. + +=item B<--output-file=> + +Store resulting file under specified argument. If unset defaults to +/etc/ngcp-config/network.yml. + +=item B<--peer=> + +Set peer configuration (being the corresponding other node in a PRO setup) to +specified argument. + +=item B<--remove-host=> + +Remove the specified host from the configuration file. +Can be specified multiple times (--remove-host=sp1 --remove-host=sp2 ...). + +=item B<--remove-interface=> + +Remove the specified interface from the configuration file. +Can be specified multiple times (--remove-interface=eth5 --remove-interface=eth6 ...). + +=item B<--role=> + +Set role configuration for host to specified argument. +Can be specified multiple times (--role=lb --role=proxy ...). + +=item B<--set-interface=> + +Add specified network interface. Can be combined with options like --hwaddr, +--ip, --netmask,... to set specified arguments as configuration options for the +given network interface. + +NOTE: If multiple --set-interface options are specified in one command line +(e.g. '--set-interface=lo --set-interface=eth0 --set-interface=eth1') then +options like --hwaddr can't be sanely combined with different settings on +multiple interfaces. Instead invoke ngcp-network with the --set-interface option +mulitple times. + +=item B<--shared-ip=> + +Set shared_ip configuration to specified argument. + +=item B<--shared-ipv6=> + +Set shared_v6ip configuration to specified argument. + +=item B<--type=> + +Set type configuration to specified argument. +Can be specified multiple times in one single command line. + +=item B<--verbose> + +Be more verbose about execution. + +=item B<--version> + +Display program version and exit. + +=item B<--vlan-raw-device=> + +Set vlan_raw_device configuration to specified argument. + +=back + +=head1 DESCRIPTION + +B will read the given input file(s) and do something +useful with the contents thereof. + +=head1 USAGE + +Usage examples useful especially on sip:provider CE systems: + + ngcp-network --set-interface=eth0 --ip=192.168.23.42 --netmask=255.25.255.248 + ngcp-network --set-interface=eth1 --ip=auto --netmask=auto + +Usage examples useful especially on sip:provider PRO systems: + + ngcp-network --set-interface=lo --set-interface=eth0 --set-interface=eth1 --ip=auto --netmask=auto + + ngcp-network --peer=sp2 + ngcp-network --host=sp2 --peer=sp1 + + ngcp-network --set-interface=eth1 --ip=auto --netmask=auto + ngcp-network --move-from=lo --move-to=eth1 --type=ha_int + + ngcp-network --set-interface=eth1 --host=sp2 --ip=192.168.255.252 --netmask=255.255.255.248 --type=ha_int + +Usage examples useful especially on sip:carrier systems: + + ngcp-network --host=proxy2 --set-interface=eth0 --ip=192.168.10.42 --netmask=255.255.255.0 + +=head1 REQUIRED ARGUMENTS + +Depending on the setting you are trying to apply there are different sets of +required arguments. Some examples: + + --move-from=INTERFACE1 --move-to=INTERFACE2 --type=XXX + +Move specified type setting XXX from INTERFACE1 section to INTERFACE2. + + --move-from=HOST1 --move-to=HOST2 --role=XXX + +Move specified role setting XXX from HOST1 to HOST2. + + --set-interface=INTERFACE --ip=[1.2.3.4|auto|none|delete] --netmask=[255.25.255.0|auto|none|delete] ... + +Configure IP, netmask,... on specified INTERFACE. + +=head1 DIAGNOSTICS + +=head2 Unknown option: ... + +The specified command line option is not support. + +=head2 File ... could not be read ... + +The specified input file doesn't exist or can't be read. + +=head2 Error opening /etc/hostname: ... + +The file /etc/hostname couldn't be read, either because the file doesn't exist +or having wrong file permissions. + +=head2 Fatal error retrieving hostname [...] + +No valid hostname could not be retrieved from /etc/hostname. + +=head2 Specified IP ... is not a valid IPv4 address + +The specified IP address is not considered a valid IPv4 address. + +=head2 Specified IP ... is not a valid IPv6 address + +The specified IP address is not considered a valid IPv6 address. + +=head2 Command line option does not accept whitespace in ... + +An argument to a command line option contains whitespace(s), +which isn't supported to avoid problems with the YAML file format. + +=head2 Could not open [...] for writing (are you user root?) + +The configuration file couldn't be stored, usually caused by user executing the +program not having write permissions on the file. + +=head2 Error sending log output + +Using the --verbose option the more detailed information couldn't be printed to +the console. + +=head2 --move-from option must be used with --move-to option together + +The --move-from option was specified in the command line but the --move-to +option is missing. + +=head2 --move-to option must be used with --move-from option together + +The --move-to option was specified in the command line but the --move-from +option is missing. + +=head2 Could not open ... for writing + +The file can't be written, usually caused because the user doesn't have write +permissions on the file. + +=head2 Could not write YAML to ... + +There is an error in storing the configuration in the YAML format. + +=head1 EXIT STATUS + +Exit code 0 means that everything should have went fine. +Exit code 2 means something with the command line options or retrieving default settings went wrong. +Exit code 9 means the specified argument to a command line option is not valid. + +=head1 CONFIGURATION + +There's no configuration file for the ngcp-network script itself supported at the moment. +The main configuration file ngcp-network operates on is /etc/ngcp-config/network.yml. + +=head1 DEPENDENCIES + +ngcp-network relies on a bunch of Perl modules, all of them specified as dependencies +through the ngcp-ngcpcfg Debian package. + +=head1 INCOMPATIBILITIES + +No known at this time. + +=head1 BUGS AND LIMITATIONS + +Please report problems you notice to the Sipwise Development Team . + +=head1 AUTHOR + +Michael Prokop + +=head1 LICENSE AND COPYRIGHT + +GPL-3+, Sipwise GmbH, Austria + +=cut diff --git a/sbin/ngcp-sync-constants b/sbin/ngcp-sync-constants new file mode 100755 index 00000000..5b96459f --- /dev/null +++ b/sbin/ngcp-sync-constants @@ -0,0 +1,308 @@ +#!/usr/bin/perl -w +#---------------------------------------------------------------------- +# Syncronizes passwords from constants.yml with MySQL +#---------------------------------------------------------------------- +use strict; +use DBI; +use YAML::Tiny; +use Getopt::Long; +use Data::Dumper; +#---------------------------------------------------------------------- +use constant CONSTANTS_YML => "/etc/ngcp-config/constants.yml"; +use constant MYSQL_CREDENTIALS => "/etc/mysql/sipwise.cnf"; +use constant MYSQL_DATA => { + voisniff => { dbuser => 'dbpassword' }, + cleanuptools => { dbuser => 'dbpassword' }, + rsyslog => { dbuser => 'dbpassword' }, + sems => { dbuser => 'dbpassword', + prepaid_dbuser => 'prepaid_dbpassword' }, + rateomat => { accountingdb => { user => 'pass' } }, + faxserver => { hylafax => { db_user => 'db_pass' } }, + cdrexport => { dbuser => 'dbpassword' }, + checktools => { dbuser => 'dbpassword' }, + mysql => { repuser => 'reppassword' }, + kamailio => { proxy => { dbrwuser => 'dbrwpw', + dbrouser => 'dbropw' } }, + mediator => { dbuser => 'dbpassword' }, + asterisk => { odbc => { dbuser => 'dbpassword' } }, + ossbss => { provisioning => { + billingdb => { user => 'pass' } } }, + }; +use constant COPY_PASSWORDS => [ # pairs of from/to + { rateomat => { accountingdb => { user => 'pass' }}}, + { rateomat => { billingdb => { user => 'pass' }}}, + + { kamailio => { proxy => { dbrwuser => 'dbrwpw' }}}, + { kamailio => { lb => { dbrwuser => 'dbrwpw' }}}, + + { kamailio => { proxy => { dbrouser => 'dbropw' }}}, + { kamailio => { lb => { dbrouser => 'dbropw' }}}, + + { ossbss => { provisioning => { billingdb => { user => 'pass' }}}}, + { ossbss => { provisioning => { database => { user => 'pass' }}}}, + + { ossbss => { provisioning => { billingdb => { user => 'pass' }}}}, + { ossbss => { provisioning => { openserdb => { user => 'pass' }}}}, + + { ossbss => { provisioning => { billingdb => { user => 'pass' }}}}, + { reminder => { dbuser => 'dbpassword' }} + + ]; + +sub Usage { + print < \&Usage, + "i|init-passwords" => \$init_passwords, + "t|test" => \$test_mode, + "v|verbose" => \$debug); +#---------------------------------------------------------------------- +sub pwgen { + my @list = ("a".."z",0..9,"A".."Z"); + my @randoms; + for (1..$password_length) { + push @randoms, $list[int(rand($#list))]; + } + return join "", @randoms; +} + +sub get_mysql_credentials { + open(my $fh, "<", MYSQL_CREDENTIALS) + or die "Can't open ".MYSQL_CREDENTIALS.": ".$!; + ($mysql_pass = <$fh>) =~ s/^.+='(.+?)'\s*$/$1/; + close $fh; +} + +sub connect_db { + get_mysql_credentials(); + $dbh = DBI->connect("DBI:mysql:database=$mysql_db; + host=$mysql_host; + port=$mysql_port", + $mysql_user, $mysql_pass, + { PrintError => 0, AutoCommit => 0 }) + or die "Can't connect to MySQL database $mysql_db: ". $DBI::errstr; +} + +sub sync_mysql_data { + + my $sth_sel = $dbh->prepare(<prepare(<[0]->{$key}; + print $key." => " if $debug; + my $opts = { init_passwords => $key eq "mysql" ? 0 : $init_passwords }; + my $data = get_user_pass(MYSQL_DATA->{$key}, $opts); + foreach my $pair (@$data) { + my $user = $pair->{'user'}; + my $pass = $pair->{'pass'}; + next unless ($user && $pass); + $sth_sel->execute($user); + (my $count) = $sth_sel->fetchrow_array(); + if ($count) { + print " ---> updating $user => $pass\n" if $debug; + $sth_upd->execute($pass, $user) unless $test_mode; + } + } + } + + $sth_sel->finish; + $sth_upd->finish; + + return if $test_mode; + + $dbh->do('FLUSH PRIVILEGES') + or die "Can't flush MySQL privileges: ". $DBI::errstr; +} + +sub get_user_pass { + my $h_ref = shift || "No data passed to fetch_mysql_data"; + my $opts = shift; + + my @data; + foreach my $ref (keys %$h_ref) { + if (ref($ref) eq 'HASH') { + return get_user_pass($ref, $opts); + } elsif (ref($h_ref->{$ref}) eq 'HASH') { + print $ref." => " if $debug; + $yml_ref = $yml_ref->{$ref}; + return get_user_pass($h_ref->{$ref}, $opts); + } else { + print " ".$ref." -- ".$h_ref->{$ref} if $debug; + $opts->{'init_passwords'} and $yml_ref->{$h_ref->{$ref}} = pwgen(); + my %pair; + $pair{'user'} = $yml_ref->{$ref}; + $pair{'pass'} = $yml_ref->{$h_ref->{$ref}}; + $pair{'user_key'} = $ref; + $pair{'pass_key'} = $h_ref->{$ref}; + push @data, \%pair; + } + } + print "\n" if $debug; + return \@data; +} + +sub copy_passwords { + print "Copying internal passwords\n" if $debug; + my $saved_init_passwords = $init_passwords; + $init_passwords = 0; + my $pairs_count = $#{+COPY_PASSWORDS}; + for (my $idx=0;$idx<$pairs_count+1;$idx++) { + next if $idx % 2; + die "Incorrect from/to pair" if $idx+1 >= $pairs_count+1; + $yml_ref = $yml->[0]; + print "from => " if $debug; + my $from_data = get_user_pass(COPY_PASSWORDS->[$idx]); + die "No 'from' user/pass data available" if $#$from_data == -1; + $yml_ref = $yml->[0]; + print "to => " if $debug; + my $to_data = get_user_pass(COPY_PASSWORDS->[$idx+1]); + die "No 'from' user/pass data available" if $#$to_data == -1; + my $user = $from_data->[0]{'user'}; + my $pass = $from_data->[0]{'pass'}; + my $user_key = $to_data->[0]{'user_key'}; + my $pass_key = $to_data->[0]{'pass_key'}; + if ($user && $pass && $user_key && $pass_key) { + print " ---> updating $user => $pass\n" if $debug; + $yml_ref->{$user_key} = $user; + $yml_ref->{$pass_key} = $pass; + } + } + $init_passwords = $saved_init_passwords; +} + +sub main { + $yml = new YAML::Tiny; + $yml = YAML::Tiny->read(CONSTANTS_YML) + or die "Can't read constants file: $!\n"; + + if ($init_passwords and not $test_mode and not -w CONSTANTS_YML) { + die CONSTANTS_YML . " is not writable"; + } + + print "[TEST MODE]\n" if $test_mode; + + connect_db(); + eval { + $dbh->begin_work; + print "Syncing ".CONSTANTS_YML." -> MySQL ... "; + print "\n" if $debug; + sync_mysql_data(); + }; + if ($@) { + $dbh->rollback; + die "\nError during syncronization: " . $@; + } else { + $test_mode ? $dbh->rollback : $dbh->commit; + } + $dbh->disconnect if $dbh; + + print "Done.\n"; + + return unless $init_passwords; + + copy_passwords(); + + return if $test_mode; + + print "Writing new passwords into ".CONSTANTS_YML." ... "; + $yml->write(CONSTANTS_YML); + print "Done\n"; + +# print Data::Dumper->Dumpxs([$yml]),"\n"; +} +#---------------------------------------------------------------------- +main(); + +exit 0; + +__END__ + +=pod + +=head1 NAME + +ngcp-sync-constants - syncronizes passwords from constants.yml with MySQL + +=head1 SYNOPSIS + +ngcp-sync-constants [ options ... ] + +=over 8 + +=item B<--init-passwords> +New passwords are generated (passwords for "mysql" is not generated to avoid replication problems) + +=item B<--test> +No real updates, only for checks + +=item B<--verbose> +Verbose mode where all changes are written to STDOUT + +=back + +=head1 DESCRIPTION + +B reads constants.yml file, parses it and syncronizes all required passwords with MySQL + +=head1 EXIT STATUS + +=over 8 + +=item B +Everything is ok + +=item B +Something is wrong, an error message raises + +=back + +=head1 INCOMPATIBILITIES + +No known at this time. + +=head1 BUGS AND LIMITATIONS + +Please report problems you notice to the Sipwise Development Team . + +=head1 AUTHOR + +Kirill Solomko + +=head1 LICENSE AND COPYRIGHT + +GPL-3+, Sipwise GmbH, Austria + +=cut diff --git a/sbin/ngcpcfg b/sbin/ngcpcfg new file mode 100755 index 00000000..d2b5efaf --- /dev/null +++ b/sbin/ngcpcfg @@ -0,0 +1,147 @@ +#!/bin/bash +# Purpose: user interface for configuration management system +################################################################################ + +set -e +set -u + +# support for testsuite +if [ -z "${FUNCTIONS:-}" ] ; then + FUNCTIONS='/usr/share/ngcp-ngcpcfg/functions/' +fi + +if [ -z "${SCRIPTS:-}" ] ; then + SCRIPTS='/usr/share/ngcp-ngcpcfg/scripts/' +fi + +if ! [ -r "${FUNCTIONS}/main" ] ; then + printf "Error: ${FUNCTIONS}/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +if [[ "${1:-}" == "decrypt" ]] ; then + # do NOT source ${FUNCTIONS}/main but just provide + # the part we need for executing ngcpcfg itself + log_debug() { + if [ -n "${DEBUG:-}" ] ; then + logger -t ngcpcfg -- "Debug: $*" + echo ; echo "DEBUG: $*" ; echo # newlines to avoid messup with cmdline output + fi + } +else + . ${FUNCTIONS}/main +fi + +# helper functions +initialise() { + log_debug "${SCRIPTS}/initialise $*" + "${SCRIPTS}"/initialise $* +} + +build() { + log_debug "${SCRIPTS}/build $*" + "${SCRIPTS}"/build $* +} + +services() { + log_debug "${SCRIPTS}/services $*" + "${SCRIPTS}"/services $* +} + +encrypt() { + log_debug "${SCRIPTS}/encrypt $*" + "${SCRIPTS}"/encrypt $* +} + +decrypt() { + log_debug "${SCRIPTS}/decrypt $*" + "${SCRIPTS}"/decrypt $* +} + +diff() { + log_debug "${SCRIPTS}/diff $*" + "${SCRIPTS}"/diff $* +} + +etckeeper() { + log_debug "${SCRIPTS}/etckeeper $*" + "${SCRIPTS}"/etckeeper $* +} + +commit() { + log_debug "${SCRIPTS}/commit $*" + "${SCRIPTS}"/commit $* +} + +status() { + log_debug "${SCRIPTS}/status $*" + "${SCRIPTS}"/status $* +} + +usage() { + # make sure to output errors on stderr + [ "${1:-}" = "1" ] && exec >&2 + + printf "%s - Configuration Management System\n\n" "$PN" + printf "Usage: + + $PN build [opts] - generate/update configuration files + $PN commit [msg] - commit and record changes (without pushing) + $PN diff [opts] - display pending configuration changes + $PN help - display this help screen and exit + $PN version - display program version and exit + $PN initialise - initialise setup (to be executed only once on setup) +" + + # display only if ngcp-ngcpcfg-ha is available + if [ -r /usr/share/ngcp-ngcpcfg/functions/ha_features ] ; then + printf " $PN push [opts] - push modifications to other systems (shared storage setup only)\n" + printf " $PN pull - retrieve modifications from shared storage (shared storage setup only)\n" +# printf " $PN upgrade - upgrade all systems [WIP - do not use unless you know what you are doing!]\n" + fi + + # display only if ngcp-ngcpcfg-locker is available + if [ -r /usr/share/ngcp-ngcpcfg/scripts/encrypt ] ; then + printf " $PN encrypt - encrypt /etc/ngcp-config and all resulting configuration files\n" + printf " $PN decrypt - decrypt /etc/ngcp-config-crypted.tgz.gpg and restore config files\n" + fi + + # display only if ngcp-ngcpcfg-carrier is available + if [ -r /usr/share/ngcp-ngcpcfg/scripts/init-mgmt ] ; then + printf " $PN init-mgmt - set up mgmgt server for carrier environment (to be executed only once)\n" + fi + + printf " $PN services [opts] - execute service handlers for modified configuration files + $PN apply - a short-cut for build-services-commit-etckeeper + $PN status - display status of configuration file\n\n" + + printf "For further usage information and options visit the ngcpcfg manpage.\n" +} + +version() { + versinfo=$(dpkg --list ngcp-ngcpcfg 2>/dev/null | awk '/^ii/ {print $3}') + [ -n "${versinfo:-}" ] || versinfo='information not available' + printf "ngcpcfg, version ${versinfo}\n" +} + +case ${1:-} in + build) shift ; build "$*" ;; + commit) shift ; commit "$*" ;; + initialise) initialise;; + push) shift ; push "$*" ;; + pull) shift ; pull "$*" ;; + services) shift ; services "$*" ;; + status) status;; + apply) shift ; build && services && commit "$*" && etckeeper ;; + encrypt) shift ; encrypt "$*" ;; + decrypt) shift ; decrypt "$*" ;; + upgrade) shift ; upgrade "$*" ;; + diff) shift ; diff "$*" ;; + init-mgmt) shift ; init-mgmt "$*" ;; + --debug) export DEBUG=1 ; shift ; $0 $* ;; + -h|--help|help) usage ; exit 0;; + -v|--version|version) version ; exit 0;; + *) usage 1; exit 1;; +esac + +## END OF FILE ################################################################# diff --git a/scripts/build b/scripts/build new file mode 100755 index 00000000..e80d5c3f --- /dev/null +++ b/scripts/build @@ -0,0 +1,129 @@ +#!/bin/bash +# Purpose: search for existing .tt2 (template toolkit) files +# and generate configuration files based on ngcpcfg's config +################################################################################ + +set -e +set -u + +# support testsuite +if [ -z "${FUNCTIONS:-}" ] ; then + FUNCTIONS='/usr/share/ngcp-ngcpcfg/functions/' +fi + +if [ -z "${HELPER:-}" ] ; then + HELPER='/usr/share/ngcp-ngcpcfg/helper/' +fi + +if ! [ -r ${FUNCTIONS}/main ] ; then + printf "Error: ${FUNCTIONS}/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. ${FUNCTIONS}/main + +MODIFIED_ONLY=false +if [ -n "${1:-}" ] ; then + case "$1" in + *--modified-only*) MODIFIED_ONLY=true ; shift ;; + esac +fi + +# make sure encoding is OK +for f in ${NGCPCTL_CONFIG:-} ${HOST_CONFIG:-} ${LOCAL_CONFIG:-} ${NETWORK_CONFIG:-} ${CONSTANTS_CONFIG:-} ; do + if [ -r "$f" ] && ! file "$f" | grep -qe "UTF-8" -qe "ASCII" ; then + log_error "Encoding check of ${f} fails: neither ASCII nor UTF-8." + log_error "Please convert ${f} to UTF-8." + log_info + log_info "NOTE:" + log_info "* Check encoding via:" + log_info " # file ${f}" + log_info "* To convert ISO-8859/latin1 to UTF-8 execute:" + log_info " # iconv -f latin1 -t utf8 < ${f} > ${f}.tmp && mv ${f}.tmp ${f}" + exit 1 + fi +done + +# check for valid YAML syntax +for f in ${NGCPCTL_CONFIG:-} ${HOST_CONFIG:-} ${LOCAL_CONFIG:-} ${NETWORK_CONFIG:-} ${CONSTANTS_CONFIG:-} ; do + if [ -r "$f" ] ; then + # use YAML::Tiny for checking + log_debug "Validating main YAML syntax of ${f}" + if ! "${HELPER}/validate-yml" "${f}" 2>/dev/null ; then + log_error "Invalid file syntax in ${f}:" + "${HELPER}/validate-yml" "${f}" + exit 1 + fi + fi +done + +build_config_files() { + for file in $(generate_template_list $*) ; do + log_debug "${HELPER}/build_config $file" + "${HELPER}/build_config" "${file}" + RC=$(($RC + $?)) + done +} + +# main script +RC=0 + +if ! $MODIFIED_ONLY ; then + build_config_files "$*" +else + log_info "Considering modified files only due to --modified-only option." + + if git status | grep -q 'working directory clean' ; then + log_info "No changes found, nothing to do." + exit 0 + fi + + if git diff-index --name-only HEAD | grep \ + -qe "$(basename $NGCPCTL_CONFIG)" \ + -qe "$(basename $HOST_CONFIG)" \ + -qe "$(basename $LOCAL_CONFIG)" \ + -qe "$(basename $NETWORK_CONFIG)" \ + -qe "$(basename $CONSTANTS_CONFIG)" ; then + log_info "Main configuation files has been changed, running full rebuild." + log_debug "Executing: build_config_files $*" + build_config_files "$*" + fi + + if git diff-index --name-only HEAD | grep -q templates/ || \ + git status --porcelain | awk '/^\?\? / {print $2}' 2>/dev/null | grep -q templates/ ; then + log_debug "Template config changed, identifying files." + for file in $(git diff-index --name-only HEAD) \ + $(git status --porcelain | awk '/^\?\? / {print $2}') ; do + build_file="${file##templates/}" + build_file="${build_file%%.services}" + build_file="${build_file%%.customtt}" + + # drop HA file suffix of all registered nodes + if [ -r /etc/ngcp-config/systems.cfg ] ; then + for host in $(cat /etc/ngcp-config/systems.cfg) ; do + build_file="${build_file%%.${host}}" + done + fi + + build_file="${build_file%%.tt2}" + build_file="/${build_file}" + + # generate file list + case "${file_list:-}" in + # avoid duplicates + *"${build_file}"*) # do nothing + ;; + # append to file list + *) file_list=" ${file_list:-} $build_file" + ;; + esac + done + + log_debug "Executing: build_config_files ${file_list}" + build_config_files ${file_list} + fi +fi + +exit "$RC" + +## END OF FILE ################################################################# diff --git a/scripts/commit b/scripts/commit new file mode 100755 index 00000000..682068aa --- /dev/null +++ b/scripts/commit @@ -0,0 +1,67 @@ +#!/bin/bash +# Purpose: commit pending changes +################################################################################ + +set -e +set -u + +if ! [ -r /usr/share/ngcp-ngcpcfg/functions/main ] ; then + printf "Error: /usr/share/ngcp-ngcpcfg/functions/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. /usr/share/ngcp-ngcpcfg/functions/main + +if [ -z "${HELPER:-}" ] ; then + HELPER='/usr/share/ngcp-ngcpcfg/helper/' +fi + +# main script + +cd "$NGCPCTL_MAIN" + +"${HELPER}/sync-db" "$NGCPCTL_CONFIG" "$CONSTANTS_CONFIG" || true + + +if ! git config user.name >/dev/null ; then + log_debug 'git config user.name ngcp-config' + git config user.name "ngcp-config" +fi +if ! git config user.email >/dev/null ; then + log_debug 'git config user.email "root@$(hostname)"' + git config user.email "root@$(hostname)" +fi + +if ! git config --global user.name >/dev/null ; then + log_debug 'git config --global user.name "git user on $(hostname)"' + git config --global user.name "git user on $(hostname)" +fi +if ! git config --global user.email >/dev/null ; then + log_debug 'git config --global user.email "root@$(hostname)"' + git config --global user.email "root@$(hostname)" +fi + +# commit message +if [ -z "${1:-}" ] ; then + msg="commiting uncommented changes" +else + msg="$*" +fi + +log_debug "git status | grep -q 'working directory clean'" +if git status | grep -q 'working directory clean' ; then + log_info "OK: nothing to commit." +else + log_debug 'git add . ; git commit -a -m "$msg [$(date --rfc-3339=ns)]"' + git add . + git commit -a -m "$msg [$(date --rfc-3339=ns)]" >/dev/null + log_info "OK" +fi + +log_debug "/usr/share/ngcp-ngcpcfg/scripts/etckeeper" +/usr/share/ngcp-ngcpcfg/scripts/etckeeper >/dev/null + +log_info "Synchronizing data from ${CONSTANTS_CONFIG}" +ngcp-sync-constants >/dev/null + +## END OF FILE ################################################################# diff --git a/scripts/decrypt b/scripts/decrypt new file mode 100755 index 00000000..b891c6ae --- /dev/null +++ b/scripts/decrypt @@ -0,0 +1,88 @@ +#!/bin/bash +# Purpose: decrypt ngcp configuration archive +################################################################################ + +set -e +set -u + +# helper functions {{{ +# sadly we can't source ${FUNCTIONS}/main as we are missing a bunch of +# configuration files that are supposed to be available, therefore +# provide the main functions we need for successfull execution of the +# decrypt function +log_info() { + logger -t ngcpcfg -- "$*" + echo "$*" +} + +# info without ending newline +log_info_n() { + logger -t ngcpcfg -- "$*" + printf -- "$*" +} +log_debug() { + if [ -n "${DEBUG:-}" ] ; then + logger -t ngcpcfg -- "Debug: $*" + echo ; echo "DEBUG: $*" ; echo # newlines to avoid messup with cmdline output + fi +} + +log_warn() { + logger -t ngcpcfg -- "Warning: $*" + echo "Warning: $*" +} + +log_error() { + logger -t ngcpcfg -- "Error: $*" + echo "Error: $*" >&2 +} +# }}} + +# main script +if ! type -p gpg &>/dev/null ; then + log_error "gpg binary not found, exiting." + exit 1 +fi + +RC=0 +TARGZ=/etc/ngcp-config-crypted.tgz + +if ! gpg -d "$TARGZ".gpg > "$TARGZ" ; then + log_error "Error while decrypting ${TARGZ}.gpg" + RC=1 +else + cd / # important to extract files at according place + if tar zxf "$TARGZ" ; then + log_info "Successfully restored configuration archive ${TARGZ}.gpg" + log_info "Now you should be able to run 'ngcpcfg apply' again." + else + log_error "Error while restoring ${TARGZ}.gpg" + RC=1 + fi +fi + +if ! [ -d /mnt/glusterfs/shared_config ] ; then + log_warn "Looks like glusterfs is not running, can not install it automatically. + +Please execute the following command on one node +as soon as glusterfs share is mounted again: + + git clone --bare /etc/ngcp-config /mnt/glusterfs/ngcpcfg-share + + +" +else + if [ -d /mnt/glusterfs/ngcpcfg-share ] ; then + log_info "Shared storage exists already, ignoring request to (re)install it." + else + log_info "Copying git repository to shared storage." + git clone --bare /etc/ngcp-config /mnt/glusterfs/ngcpcfg-share + fi +fi + +# don't leave the unencrypted archive behind +rm -f "$TARGZ" + +exit "$RC" + +## END OF FILE ################################################################# diff --git a/scripts/diff b/scripts/diff new file mode 100755 index 00000000..87c9ac00 --- /dev/null +++ b/scripts/diff @@ -0,0 +1,36 @@ +#!/bin/bash +# Purpose: show changes in configuration pool +################################################################################ + +set -e +set -u + +if ! [ -r /usr/share/ngcp-ngcpcfg/functions/main ] ; then + printf "Error: /usr/share/ngcp-ngcpcfg/functions/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. /usr/share/ngcp-ngcpcfg/functions/main + +# main script +log_debug "cd $NGCPCTL_MAIN" +cd "$NGCPCTL_MAIN" + +log_debug "git diff $*" +git diff $* + +# added files +log_debug "git status --porcelain | awk '/^\?\? / {print \$2}'" +if git status --porcelain | awk '/^\?\? / {print $2}' | grep -q . ; then + log_info "* New but not yet registered files inside ${NGCPCTL_MAIN}:" + git status --porcelain | awk '/^\?\? / {print $2}' +fi + +# deleted files +log_debug "git status --porcelain | awk '/^D / {print \$2}'" +if git status --porcelain | awk '/^D / {print $2}' | grep -q . ; then + log_info "* Removed but not yet unregistered files inside ${NGCPCTL_MAIN}:" + git status --porcelain | awk '/^D / {print $2}' +fi + +## END OF FILE ################################################################# diff --git a/scripts/encrypt b/scripts/encrypt new file mode 100755 index 00000000..f43d0ebb --- /dev/null +++ b/scripts/encrypt @@ -0,0 +1,71 @@ +#!/bin/bash +# Purpose: encrypt ngcp configuration files +################################################################################ + +set -e +set -u + +# support testsuite +if [ -z "${FUNCTIONS:-}" ] ; then + FUNCTIONS='/usr/share/ngcp-ngcpcfg/functions/' +fi + +if [ -z "${HELPER:-}" ] ; then + HELPER='/usr/share/ngcp-ngcpcfg/helper/' +fi + +if ! [ -r ${FUNCTIONS}/main ] ; then + printf "Error: ${FUNCTIONS}/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. ${FUNCTIONS}/main + +get_config_file_list() { + for file in $(find "$TEMPLATE_POOL" -name \*.tt2 -o -name \*.tt2"${HA_FILE:-}") ; do + x=${file##${NGCPCTL_MAIN}/templates/} # drop leading /etc/ngcp-config + y=${x%%.tt2} # drop trailing suffix '.tt2' + y=${y%%.tt2.sp1} # drop trailing suffix '.tt2.sp1' + y=${y%%.tt2.sp2} # drop trailing suffix '.tt2.sp2' + y=${y%%.customtt} # drop trailing suffix '.customtt' + # if the file does not exist (e.g. because "ngcpcfg apply" + # hasn't been executed yet for whatever reason, then don't + # report missing files, otherwise tar will complain + if [ -r "/${y}" ] ; then + echo "/${y}" + fi + done +} + +# main script +if ! type -p gpg &>/dev/null ; then + log_error "gpg binary not found, exiting." + exit 1 +fi + +RC=0 +TARGZ=/etc/ngcp-config-crypted.tgz +FILES=$(get_config_file_list) + +tar zcf "$TARGZ" /etc/ngcp-config/ $FILES /etc/.git +if gpg --symmetric "$TARGZ" ; then + log_info "Successfully created crypted ngcpcfg configuration archive ${TARGZ}.gpg" +else + log_error "Error while setting up $TARGZ" + RC=1 +fi + +log_info_n "Now really erase all configuration files managed by ngcpcfg? [y/N] " +a='' ; read a +if [[ "$a" == "y" ]] || [ "$a" == "Y" ]] ; then + rm -rf "$NGCPCTL_MAIN" ; rm -f "$TARGZ" ; rm -f $FILES ; rm -rf /etc/.git + # make sure we don't leavy any stuff on shared storage + rm -rf /var/lib/glusterfs/export/ngcpcfg-share + rm -rf /mnt/glusterfs/ngcpcfg-share/ +else + log_info "Skipping as requested." +fi + +exit "$RC" + +## END OF FILE ################################################################# diff --git a/scripts/etckeeper b/scripts/etckeeper new file mode 100755 index 00000000..51d14e3f --- /dev/null +++ b/scripts/etckeeper @@ -0,0 +1,41 @@ +#!/bin/bash +# Purpose: wrapper around etckeeper for usage inside ngcpcfg +################################################################################ + +set -e +set -u + +if ! [ -r /usr/share/ngcp-ngcpcfg/functions/main ] ; then + printf "Error: /usr/share/ngcp-ngcpcfg/functions/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. /usr/share/ngcp-ngcpcfg/functions/main + +# main script + +log_debug "cd /etc" +cd /etc + +log_debug "type -p etckeeper" +if ! type -p etckeeper &>/dev/null ; then + log_warn "etckeeper is not available, skipping etckeeper execution" + exit 0 +fi + +log_debug "test -d .git" +if ! [ -d .git ] ; then + log_warn "etckeeper has not been initialized yet, skipping etckeeper execution" + exit 0 +fi + +log_info "Checking state of /etc files" +log_debug 'git status | grep -q "working directory clean"' +if git status | grep -q 'working directory clean' ; then + log_info "OK: nothing to commit." +else + log_debug 'etckeeper commit "ngcpcfg apply on $(date)"' + etckeeper commit "ngcpcfg apply on $(date)" +fi + +## END OF FILE ################################################################# diff --git a/scripts/initialise b/scripts/initialise new file mode 100755 index 00000000..ed7875e6 --- /dev/null +++ b/scripts/initialise @@ -0,0 +1,78 @@ +#!/bin/bash +# Purpose: initialise ngcpcfg setup +################################################################################ + +set -e +set -u + +if ! [ -r /usr/share/ngcp-ngcpcfg/functions/main ] ; then + printf "Error: /usr/share/ngcp-ngcpcfg/functions/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. /usr/share/ngcp-ngcpcfg/functions/main + +# main script + +log_debug "cd $NGCPCTL_MAIN" +cd "$NGCPCTL_MAIN" + +if [ -d .git ] ; then + log_info "Git directory in $NGCPCTL_MAIN exists already, skipping creation." +else + log_debug "git init" + git init >/dev/null +fi + +# ignore files we do not consider as valid template files +if ! grep -qs '^# begin section managed by ngcpcfg' .gitignore ; then + echo '# begin section managed by ngcpcfg (do not edit this section by hand) +# new and old versions of conffiles, stored by dpkg +*.dpkg-* +# new and old versions of conffiles, stored by ucf +*.ucf-* + +# old versions of files +*.old + +# editor temp files +*~ +.*.sw? +.sw? +\#*\# +DEADJOE + +# end section managed by ngcpcfg' >> .gitignore +fi + +log_debug "git add ." +git add . + +if ! git status | grep -q 'working directory clean' ; then + log_debug "git commit -a -m \"initial version of ngcpcfg on $HNAME\"" + git commit -a -m "initial version of ngcpcfg on $HNAME" >/dev/null +fi + +if type -p init_ha &>/dev/null ; then + log_debug "init_ha function" + init_ha +fi + +if ! [ -r /etc/.gitignore ] ; then + log_info "etckeeper not present, ignoring request to add ngcp-config to /etc/.gitignore." +else + log_debug 'grep -q ngcp-config /etc/.gitignore' + if ! grep -q ngcp-config /etc/.gitignore ; then + log_info_n "etckeeper seems to be present, adding "$NGCPCTL_MAIN" to ignore list: " + echo "ngcp-config" >> /etc/.gitignore + log_debug 'cd /etc ; git add .gitignore ; git commit -m "add ngcp-config directory to .gitignore"' + cd /etc + git add .gitignore + git commit -m 'add ngcp-config directory to .gitignore' >/dev/null + log_info "OK" + fi +fi + +log_info "Successfully finished setup." + +## END OF FILE ################################################################# diff --git a/scripts/services b/scripts/services new file mode 100755 index 00000000..b84ed3d2 --- /dev/null +++ b/scripts/services @@ -0,0 +1,98 @@ +#!/bin/bash +# Purpose: detect modified files in config tree and execute +# any defined service modifications +################################################################################ + +set -e +set -u + +if ! [ -r /usr/share/ngcp-ngcpcfg/functions/main ] ; then + printf "Error: /usr/share/ngcp-ngcpcfg/functions/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. /usr/share/ngcp-ngcpcfg/functions/main + +# main script + +cd "$CONFIG_POOL" + +DRYRUN='false' +if [[ "${1:-}" == "test" ]] || [[ "${1:-}" == "--dry-run" ]]; then + DRYRUN='true' +elif [[ -n "${1:-}" ]] ; then + log_error "Unsupported option(s) given: $*" + log_info "Did you mean '--dry-run'?" + exit 1 +fi +log_debug "DRYRUN = $DRYRUN" + +TMPFILE="$(mktemp)" + +# unify service calls +unifyer() { + file="$1" + + # make sure services are listed just once to + # avoid re-execution of services (which will + # happen if /etc/foo/ngcpcfg.services exists + # and several file inside /etc/foo are modified) + if ! grep -q "^${file}$" "$TMPFILE" ; then + echo "$file" >> $TMPFILE + fi +} + +# NOTES: +# * 'git ls-files -o' also shows ignored files +# * 'git status --porcelain |grep '^?? ' not available in git 1.5 + +# The 'git diff' command fixes a strange race condition where 'git diff-index' +# lists *all* generated file(s) where the time stamp differs, resulting in a +# list of *all* generated files (which is not what we want). +# After running 'git diff' the following 'git diff-index ...' command lists +# only the modified files as we want and need it. See BTS #211 for bugreport. +git diff &>/dev/null + +for file in $(git diff-index --name-only HEAD) ; do + if [ -r "$file" ] && [ -r "${SERVICES_POOL}/${file}".services ] ; then + log_debug "unifyer ${SERVICES_POOL}/${file}.services" + unifyer "${SERVICES_POOL}/${file}".services + elif [ -r "$file" ] && [ -r $SERVICES_POOL/"$(dirname $file)"/ngcpcfg.services ] ; then + log_debug "unifyer ${SERVICES_POOL}/$(dirname $file)/ngcpcfg.services" + unifyer "${SERVICES_POOL}"/"$(dirname $file)/ngcpcfg.services" + fi +done + +exec_wrapper() { + # normalize path (get rid of "./" and "//") + line="$(echo $1 | sed -e 's/\.\///g ; s/\/\//\//g')" + + if $DRYRUN ; then + log_info "TEST MODE: Would execute action for ${line}" + return 0 + fi + + log_info "Executing action for $line" + if [ -x "$line" ] ; then + log_debug "$line" + if ! "$line" ; then + log_warn "$line returned with error code, continuing anyway." + fi + elif [ -r "$line" ] ; then + log_debug "bash $line" + if ! bash "$line" ; then + log_warn "$line returned with error code, continuing anyway." + fi + else + log_error "Error: $line could not be read." + exit 1 + fi +} + +for line in $(cat $TMPFILE) ; do + exec_wrapper "$line" +done + +rm -f "$TMPFILE" + +## END OF FILE ################################################################# diff --git a/scripts/status b/scripts/status new file mode 100755 index 00000000..c378d527 --- /dev/null +++ b/scripts/status @@ -0,0 +1,69 @@ +#!/bin/bash +# Purpose: display state of ngcpcfg setup and pending/recommended actions +################################################################################ + +set -e +set -u + +if ! [ -r /usr/share/ngcp-ngcpcfg/functions/main ] ; then + printf "Error: /usr/share/ngcp-ngcpcfg/functions/main could not be read. Exiting.\n" >&2 + exit 1 +fi + +. /usr/share/ngcp-ngcpcfg/functions/main + +# main script + +if ! [ -d "${NGCPCTL_MAIN:-}" ] ; then + log_error "Directory ${NGCPCTL_MAIN:-} does not exist yet. Execute 'ngcpcfg initialise'." + exit 1 +fi + +log_debug "cd $NGCPCTL_MAIN" +cd "$NGCPCTL_MAIN" + +log_info "Checking state of ngcpcfg:" +if ! [ -r /etc/ngcp-config/.git/HEAD ] ; then + log_warn "ngcpcfg has not been initialised yet. Execute 'ngcpcfg initialise'." + exit 0 +fi + +log_info "OK: has been initialised already " + +log_info "Checking state of configuration files:" +log_debug "git status | grep -q 'working directory clean'" +if git status | grep -q 'working directory clean' ; then + log_info "OK: nothing to commit." +else + if git diff-index --name-only HEAD | grep -q . ; then + log_info "ACTION_NEEDED: configuration files have been modified:" + log_debug "git diff-index --name-only HEAD | sed \"s;^;${NGCPCTL_MAIN}/;\"" + git diff-index --name-only HEAD | sed "s;^;${NGCPCTL_MAIN}/;" + fi + + if git ls-files --other --exclude-standard | grep -q . ; then + log_info "ACTION_NEEDED: configuration files have been added:" + log_debug "git ls-files --other --exclude-standard | sed \"s;^;${NGCPCTL_MAIN}/;\"" + git ls-files --other --exclude-standard | sed "s;^;${NGCPCTL_MAIN}/;" + fi + + log_info "-> execute 'ngcpcfg build' and 'ngcpcfg commit'" +fi + +if which status_ha &>/dev/null ; then + log_debug "status_ha function" + status_ha +fi + +log_debug "cd /etc" +cd /etc + +log_info "Checking state of /etc files" +log_debug "git status | grep -q 'working directory clean'" +if git status | grep -q 'working directory clean' ; then + log_info "OK: nothing to commit." +else + log_info "ACTION_NEEDED: configuration files changed (execute 'etckeeper commit [message]')." +fi + +## END OF FILE ################################################################# diff --git a/testsuite/Makefile b/testsuite/Makefile new file mode 100644 index 00000000..2085b64b --- /dev/null +++ b/testsuite/Makefile @@ -0,0 +1,16 @@ +all: prepare test + +prepare: + carton install + +test: + mkdir -p ../reports/ + rm -f ../reports/ngcp-network.tap + bash ./ngcp-network > ../reports/ngcp-network.tap + +clean: + rm -rf testfiles + +dist-clean: clean + rm -rf ../reports/ + rm -rf local diff --git a/testsuite/build-check b/testsuite/build-check new file mode 100644 index 00000000..09f76cd7 --- /dev/null +++ b/testsuite/build-check @@ -0,0 +1,89 @@ +#!/bin/bash + +set -e +set -u + +# when invoked under DEBUG_SHELL=1 then provide +# interactive shell before exiting +if [ -n "${DEBUG_SHELL:-}" ] ; then + bailout() { bash; exit 1; } +else + bailout() { exit 1; } +fi + +export testsuite="$(dirname $PWD/$0)" + +TMPDIR="$(mktemp -d)" +echo "Switching to temporary directory $TMPDIR" +cd "$TMPDIR" +cp -a ${testsuite}/* . + +export FUNCTIONS="$testsuite/../functions/" +export HELPER="$testsuite/../helper/" +export SCRIPTS="$testsuite/../scripts/" + +echo -n "Testing ngcpcfg without any arguments: " +if $testsuite/../sbin/ngcpcfg 2>&1 | grep -q "^Usage:$" ; then + echo OK +else + echo "Error with executing ngcpcfg without any arguments" >&2 + bailout +fi + +echo -n "Testing ngcpcfg --help: " +if $testsuite/../sbin/ngcpcfg --help 2>&1 | grep -q "^Usage:$" ; then + echo OK +else + echo "Error with executing ngcpcfg --help" >&2 + bailout +fi + +echo -n "Testing ngcpcfg --version: " +if $testsuite/../sbin/ngcpcfg --version 2>&1 | grep -q "version" ; then + echo OK +else + echo "Error with executing ngcpcfg --version" >&2 + bailout +fi + +echo "Testing ngcpcfg build:" +$testsuite/../sbin/ngcpcfg build > build.log + +oldIFS="$IFS" +IFS=" +" +for line in $(cat build.log) ; do + case "$line" in + Generating\ testsuite/*OK) echo generation OK ;; + Executing\ postbuild\ for\ testsuite/*) echo postbuild OK;; + chgrp\ www-data\ testsuite/ngcp-ossbss/logging.conf) echo postbuild OK;; + DEBUG:*) ;; # support running under "--debug" + *) + echo "Error caught: $line" >&2 + bailout + ;; + esac +done +IFS="$oldIFS" + +# test main tt2 processing +if [[ $(cat testsuite/testtemplate) == "foo" ]] ; then + echo "template test is OK" +else + echo "Error with [ngcp-config/templates/]testsuite/testtemplate.tt2" >&2 + bailout +fi + +# test precedence of files +if [[ $(cat testsuite/precedence/all/test) == "test.custom.tt2" ]] ; then + echo "precedence test is OK" +else + echo "Error with [ngcp-config/templates/]testsuite/precedence/all/*" >&2 + bailout +fi + +rm -rf "$TMPDIR" + +echo "Everything seems to be ok." + +# EOF diff --git a/testsuite/cpanfile b/testsuite/cpanfile new file mode 100644 index 00000000..4f8f9338 --- /dev/null +++ b/testsuite/cpanfile @@ -0,0 +1,13 @@ +requires 'Carp'; +requires 'Data::Validate::IP'; +requires 'Getopt::Long'; +requires 'IO::Interface'; +requires 'IO::Socket'; +requires 'IPC::Open3'; +requires 'List::MoreUtils'; +requires 'Net::Netmask'; +requires 'Pod::Usage'; +requires 'Regexp::IPv6'; +requires 'Socket'; +requires 'Sys::Hostname'; +requires 'YAML::Tiny'; diff --git a/testsuite/network-config/deployment.yml b/testsuite/network-config/deployment.yml new file mode 100644 index 00000000..9aa09f2d --- /dev/null +++ b/testsuite/network-config/deployment.yml @@ -0,0 +1,70 @@ +--- +hosts: + sp1: + eth0: + dns_nameservers: + - 2.3.4.5 + - 3.4.5.6 + gateway: 1.2.3.1 + hwaddr: de:ad:be:ef:23:42 + ip: 1.2.3.4 + netmask: 255.255.255.0 + eth1: + hwaddr: de:ad:be:ef:42:23 + ip: 192.168.255.251 + netmask: 255.255.255.248 + type: + - ha_int + interfaces: + - lo + - eth0 + - eth1 + lo: + hwaddr: 00:00:00:00:00:00 + ip: 127.0.0.1 + netmask: 255.0.0.0 + shared_ip: ~ + shared_v6ip: ~ + type: + - sip_int + - web_ext + - sip_ext + - rtp_ext + - ssh_ext + - mon_ext + - web_int + v6ip: '::1' + peer: sp2 + role: + - proxy + - lb + - mgmt + sp2: + eth1: + ip: 192.168.255.252 + netmask: 255.255.255.248 + type: + - ha_int + interfaces: + - lo + - eth1 + lo: + hwaddr: 00:00:00:00:00:00 + ip: 127.0.0.1 + netmask: 255.0.0.0 + shared_ip: ~ + shared_v6ip: ~ + type: + - sip_int + - web_ext + - sip_ext + - rtp_ext + - ssh_ext + - mon_ext + - web_int + v6ip: '::1' + peer: sp1 + role: + - proxy + - lb + - mgmt diff --git a/testsuite/network-config/eth42.yml b/testsuite/network-config/eth42.yml new file mode 100644 index 00000000..d1b29b25 --- /dev/null +++ b/testsuite/network-config/eth42.yml @@ -0,0 +1,24 @@ +--- +hosts: + sp1: + eth42: + ip: 1.2.3.4 + netmask: 255.255.255.248 + interfaces: + - lo + - eth42 + lo: + ip: 127.0.0.1 + netmask: 255.255.255.0 + type: + - sip_int + - ha_int + - web_ext + - sip_ext + - rtp_ext + - ssh_ext + - mon_ext + role: + - proxy + - lb + - mgmt diff --git a/testsuite/network-config/network_pro.yml b/testsuite/network-config/network_pro.yml new file mode 100644 index 00000000..522a9619 --- /dev/null +++ b/testsuite/network-config/network_pro.yml @@ -0,0 +1,20 @@ +--- +hosts: + sp1: + interfaces: + - lo + lo: + ip: 127.0.0.1 + netmask: 255.255.255.0 + type: + - sip_int + - ha_int + - web_ext + - sip_ext + - rtp_ext + - ssh_ext + - mon_ext + role: + - proxy + - lb + - mgmt diff --git a/testsuite/ngcp-config/config.yml b/testsuite/ngcp-config/config.yml new file mode 100644 index 00000000..5ca2ee7c --- /dev/null +++ b/testsuite/ngcp-config/config.yml @@ -0,0 +1,279 @@ + + +#These values might be changed to constants.yml +networking: + eaddress: 192.168.51.117 +ha: + eiface: eth0 + bindnetaddr: 192.168.51.0 + mcastaddr: 226.94.1.1 + mcastport: 5405 + +database: + bufferpoolsize: 254M +kamailio: + port: 5060 + disable_tcp: yes + authenticate_bye: no + + multi_homed: no + allow_peer_relay: no + + use_enum: no + enum_suffix: e164.arpa. + patterns: + emergency: 112|911|999 +asterisk: + sip: + bindport: 5070 + dtmfmode: rfc2833 + rtp: + minport: 10000 + maxport: 20000 + voicemail: + serveremail: voicebox@sip.sipwise.com + maxmsg: 30 + max_msg_length: 180 + min_msg_length: 3 + maxgreet: 60 + fromstring: Voicemail server + mailsubject: '[Voicebox] New message ${VM_MSGNUM} in voicebox ${VM_MAILBOX}' + mailbody: 'You have received a new message from ${VM_CALLERID} in voicebox ${VM_MAILBOX} on ${VM_DATE}.' + log: + facility: local6 +mediator: + interval: 10 +rateomat: + splitpeakparts: 0 + loopinterval: 10 +ossbss: + htpasswd: + - user: ngcpsoap + pass: '{SHA}w4zj3mxbmynIQ1jsUEjSkN2z2pk=' + apache: + port: 2443 + serveradmin: support@sipwise.com + servername: '"myserver"' + sslcertfile: /etc/apache2/ssl/myserver.crt + sslcertkeyfile: /etc/apache2/ssl/myserver.pem + provisioning: + log_passwords: 0 + no_logline_truncate: 0 + allow_ip_as_domain: 1 + allow_numeric_usernames: 0 + tmpdir: /tmp + + faxpw_min_char: 0 + + routing: + cc_regex: '[1-9]\d{0,3}' + ac_regex: '[1-9]\d{0,4}' + sn_regex: '[1-9]\d+' + logging: + ossbss: + facility: local0 + level: DEBUG + identity: provisioning + web: + facility: local0 + level: DEBUG + apache: + err: + facility: local7 + level: info + acc: + facility: daemon + level: info + identity: oss +www_admin: + apache: + port: 1443 + serveradmin: support@sipwise.com + servername: '"myserver"' + sslcertfile: /etc/apache2/ssl/myserver.crt + sslcertkeyfile: /etc/apache2/ssl/myserver.pem + billing_features: 1 + peering_features: 1 + voicemail_features: 1 + fax_features: 1 + conference_features: 1 + cc_dial_prefix: 00 + ac_dial_prefix: 0 + dashboard: + enabled: 1 + subscriber: + extension_features: 0 + audiofile_features: 0 + domain: + rewrite_features: 1 + audiofile_features: 0 + vsc_features: 0 + default_admin_settings: + is_master: 0 + is_active: 1 + read_only: 0 + show_passwords: 1 + call_data: 0 + fees_csv: + element_order: + - destination + - zone + - zone_detail + - onpeak_init_rate + - onpeak_init_interval + - onpeak_follow_rate + - onpeak_follow_interval + - offpeak_init_rate + - offpeak_init_interval + - offpeak_follow_rate + - offpeak_follow_interval + - use_free_time + speed_dial_vsc_presets: + vsc: + - '*0' + - '*1' + - '*2' + - '*3' + - '*4' + - '*5' + - '*6' + - '*7' + - '*8' + - '*9' + logging: + apache: + err: + facility: local7 + level: info + acc: + facility: daemon + level: info + identity: oss +www_csc: + apache: + port: 443 + serveradmin: support@sipwise.com + servername: myserver + sslcertfile: /etc/apache2/ssl/myserver.crt + sslcertkeyfile: /etc/apache2/ssl/myserver.pem + site_domain: sip.yourdomain.tld + display_account_info: 0 + sip_server: sip.yourdomain.tld + tftp_server: tftp.yourdomain.tld + payment_features: 0 + cc_dial_prefix: 00 + ac_dial_prefix: 0 + site_config: + title: Sipwise NGCP CSC + default_language: en + default_uri: '/desktop' + languages: + - en + - es + company: + name: Your Company + logo: 'https://some.server/path/to/logo.gif' + hotline: '' + city: '' + street: '' + phone: '' + fax: '' + email: '' + webserver: '' + main_menu: + desktop: 1 + calllist: 1 + voicebox: 1 + addressbook: 1 + callforward: 1 + callblock: 1 + reminder: 1 + device: 0 + account: 1 + logging: + apache: + err: + facility: local7 + level: info + acc: + facility: daemon + level: info + identity: csc +rtpproxy: + minport: 30000 + maxport: 40000 +sems: + bindport: 5080 + lowport: 40001 + highport: 50000 + xmlrpcport: 8090 + vsc: + voicemail_number: 2000 + cfu_code: 72 + cfb_code: 90 + cft_code: 92 + cfna_code: 93 + speedial_code: 50 + reminder_code: 55 +rsyslog: + external_log: 0 + external_address: 192.168.32.1 + external_proto: udp + external_port: 514 + external_loglevel: warning +reminder: + retries: 2 + retry_time: 60 + wait_time: 30 + sip_fromuser: reminder + sip_fromdomain: voicebox.sipwise.local +checktools: + sip_check_enable: 1 + mysql_check_enable: 1 + mpt_check_enable: 0 + exim_check_enable: 0 + kamailio_check_dialog_active_enable: 1 + kamailio_check_dialog_early_enable: 0 + kamailio_check_usrloc_regusers_enable: 1 + kamailio_check_usrloc_regdevices_enable: 0 + oss_check_provisioned_subscribers_enable: 1 + + force: 0 + mysql_check_replication: 1 + kamailio_check_dialog_local_enable: 0 + kamailio_check_dialog_relay_enable: 0 + kamailio_check_dialog_incoming_enable: 0 + kamailio_check_dialog_outgoing_enable: 0 + + + collcheck: + maxage: 600 + cpuidle: 0.1 + swapfree: 0.5 + dfused: 0.9 + memused: 0.7 + loadshort: 3 + loadmedium: 2 + loadlong: 2 + siptimeout: 15 + eximmaxqueue: 15 + +cdrexport: + exportpath: '/home/jail/home/cdrexport' + filepreffix: sipwise + fileversion: 001 + monthly_folder: yes + daily_folder: yes +cleanuptools: + batch: 10000 + archive_targetdir: '/tmp' + compress: gzip + acc_days: 90 + trash_days: 30 + acc2_days: 180 +general: + companyname: sipwise + lang: en + adminmail: nomail@nodomain.org + +# vim: ft=yaml diff --git a/testsuite/ngcp-config/constants.yml b/testsuite/ngcp-config/constants.yml new file mode 100644 index 00000000..8344f3e8 --- /dev/null +++ b/testsuite/ngcp-config/constants.yml @@ -0,0 +1,127 @@ + + +############################# +# +# DO NOT EDIT THIS FILE!! +# +# This file contains some values not being handled by the ngcp configuration framework. +# Editing this file may cause your system to stop working. +# Do not manually edit this file unless you know what you're doing. +# +# +############################ + +database: + dbhost: localhost + dbport: 3306 +kamailio: + dbengine: MYSQL + dbname: kamailio + dbrootuser: root + dbropw: jJXgh7AAmkJ4KCMWYHCX + dbrouser: kamailioro + dbrwpw: g7PYspcXhLRNjgwPghkx + dbrwuser: kamailio + uaccryptpw: UwrpwmpCUMhfv4zscuha +asterisk: + asterisk: + internal_timing: no + odbc: + dbuser: asterisk + dbpassword: cx9W7PWMmkjRVpzzYkts + dbname: kamailio +mediator: + dbuser: mediator + dbpassword: AcMcrhbzXhUmTvTf9gsm + srcdbname: kamailio + destdbname: accounting + provdbname: provisioning +rateomat: + billingdb: + name: billing + user: rateomat + pass: dn7iM9YgPcJhmHXo9eWr + accountingdb: + name: accounting + user: rateomat + pass: dn7iM9YgPcJhmHXo9eWr +mysql: + + repuser: replicator + reppassword: KtXpr3jT7jtcd7PmnTbd + +ossbss: + provisioning: + database: + name: provisioning + user: soap + pass: hEuxXrzLF43X93ULhoNu + billingdb: + name: billing + user: soap + pass: hEuxXrzLF43X93ULhoNu + openserdb: + name: kamailio + user: soap + pass: hEuxXrzLF43X93ULhoNu + acl: + - user: csc + pass: RMYisfV4rvpWYVKosYnU + allow: + - Voip + - Billing + fax: + sendfax: /usr/bin/sendfax + faxserver: 127.0.0.1 + default_sender: webfax + routing: + internal_domain: voip.sipwise.local + no_such_number: no_such_number + voicebox_domain: voicebox.local + fax2mail_domain: fax2mail.local + conference_domain: conference.local + backends: + available: + - Billing + - Voip + enabled: + - Billing + - Voip +www_csc: + prov_user: csc + prov_pass: RMYisfV4rvpWYVKosYnU +sems: + dbuser: soap + dbpassword: hEuxXrzLF43X93ULhoNu +rsyslog: + dbname: syslog + dbuser: rsyslog + dbpassword: bAmjr7gTaevK4tFofkaP + rotate_days: 28 +reminder: + context: reminder + sip_peer: sip_proxy + dbname: provisioning + dbuser: soap + dbpassword: hEuxXrzLF43X93ULhoNu +checktools: + dbuser: nagios + dbpassword: XM4s47qEyJrckCoqPVYe + sipuser: nagios + sipdomain: voip.sipwise.local + sip_check_ip: 127.0.0.1 +cdrexport: + dbuser: exporter + dbpassword: zghivhVEv7fPMRiLAPRR + dbname: accounting +cleanuptools: + dbuser: dbcleaner + dbpassword: q43aaqRucmTmYz9gvJhe + acc_db: kamailio + trash_db: kamailio +#### Do not touch this! #### +configuration_framework: + constants_version: 4083 + config_version: 4083 + +# vim: ft=yaml diff --git a/testsuite/ngcp-config/templates/testsuite/crontab.tt2 b/testsuite/ngcp-config/templates/testsuite/crontab.tt2 new file mode 100644 index 00000000..6a3d0182 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/crontab.tt2 @@ -0,0 +1,18 @@ + +# /etc/crontab: system-wide crontab +# Unlike any other crontab you don't have to run the `crontab' +# command to install the new version when you edit this file +# and files in /etc/cron.d. These files also have username fields, +# that none of the other crontabs do. + +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# m h dom mon dow user command +17 * * * * root cd / && run-parts --report /etc/cron.hourly +59 23 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) +47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) +52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly ) +# + + diff --git a/testsuite/ngcp-config/templates/testsuite/mysql/my.cnf.tt2.sp1 b/testsuite/ngcp-config/templates/testsuite/mysql/my.cnf.tt2.sp1 new file mode 100644 index 00000000..884b0677 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/mysql/my.cnf.tt2.sp1 @@ -0,0 +1,170 @@ +[client] + +port = [% database.dbport %] +socket = /var/run/mysqld/mysqld.sock +default_character_set = utf8 + +#---------------------------------------------------------------- + +[safe_mysqld] + +syslog + +#---------------------------------------------------------------- + +[mysqld] + + + + + + +user = mysql +port = [% database.dbport %] +socket = /var/run/mysqld/mysqld.sock + +max_connections = 2048 +back_log = 128 +max_connect_errors = 1000 +connect_timeout = 2 +wait_timeout = 60 + +max_allowed_packet = 16M +net_buffer_length = 8K + +default_character_set = utf8 +character_set_server = utf8 + +default_collation = utf8_general_ci +init_connect = 'SET NAMES utf8; SET sql_mode = STRICT_TRANS_TABLES' + +basedir = /usr +datadir = /var/lib/mysql +tmpdir = /tmp +language = /usr/share/mysql/english +log-error = /var/log/mysql/mysqld.err +pid-file = /var/run/mysqld/mysqld.pid +log_slow_queries = /var/log/mysql/slow-queries.log +log_output = FILE +long_query_time = 5 +log_long_format +#log = /var/log/mysql/queries.log + + +#Binlog options +log_bin = /var/lib/mysql/log-bin +max_binlog_size = 512M +expire_logs_days = 90 #Values 0-99 +binlog_format = row +binlog_cache_size = 1M +sync_binlog = 1 + +relay_log = /var/lib/mysql/log-relay-bin +max_relay_log_size = 512M + + + + +#Replication options + +server_id = 1 +auto_increment_offset = 1 +auto_increment_increment = 2 +master-host = sp2 + + + + +master-port=[% database.dbport %] +master-user=[% mysql.repuser %] +master-password=[% mysql.reppassword %] +master-connect-retry=10 + +replicate-wild-do-table=kamailio.% +replicate-wild-do-table=provisioning.% +replicate-wild-do-table=operating.% +replicate-wild-do-table=billing.% +replicate-wild-do-table=accounting.% +replicate-wild-do-table=syslog.% +replicate-ignore-table=accounting.acc_backup +replicate-ignore-table=accounting.acc_trash +replicate-ignore-table=kamailio.acc_backup +replicate-ignore-table=kamailio.acc_trash + + + + +table_cache = 4096 +join_buffer_size = 8M +tmp_table_size = 64M +sort_buffer_size = 8M +thread_cache_size = 64 +thread_concurrency = 8 +thread_stack = 192K + + + +# Query cache, disabled +query_cache_size = 0 +query_cache_type = 1 +query_cache_limit = 2M + +transaction_isolation = REPEATABLE-READ + +# MyISAM options +key_buffer_size = 256M +read_buffer_size = 2M +read_rnd_buffer_size = 8M +myisam_sort_buffer_size = 128M +bulk_insert_buffer_size = 64M +myisam_max_sort_file_size = 10G +myisam_repair_threads = 2 +#myisam_recover_options = DEFAULT + + + +# InnoDB options +innodb_data_home_dir = /var/lib/mysql +innodb_data_file_path = ibdata1:10M:autoextend +innodb_file_per_table +innodb_buffer_pool_size = [% database.bufferpoolsize %] +innodb_additional_mem_pool_size = 32M +innodb_log_group_home_dir = /var/lib/mysql +innodb_log_files_in_group = 4 +innodb_log_file_size = 128M +innodb_log_buffer_size = 8M +innodb_max_dirty_pages_pct = 80 +innodb_flush_log_at_trx_commit = 1 +innodb_lock_wait_timeout = 50 +innodb_flush_method = O_DIRECT +innodb_thread_concurrency = 32 +innodb_autoinc_lock_mode = 1 +innodb_locks_unsafe_for_binlog +innodb_fast_shutdown = 1 +innodb_max_purge_lag = 0 + +#---------------------------------------------------------------- + +[mysqldump] + +quick +max_allowed_packet = 16M +single_transaction + + +#---------------------------------------------------------------- + +[mysql] + +#no_auto_rehash + + +#---------------------------------------------------------------- + +[myisamchk] + +# databases +key_buffer = 256M +sort_buffer = 256M +read_buffer = 64M +write_buffer = 64M diff --git a/testsuite/ngcp-config/templates/testsuite/mysql/my.cnf.tt2.sp2 b/testsuite/ngcp-config/templates/testsuite/mysql/my.cnf.tt2.sp2 new file mode 100644 index 00000000..2e7204cd --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/mysql/my.cnf.tt2.sp2 @@ -0,0 +1,170 @@ +[client] + +port = [% database.dbport %] +socket = /var/run/mysqld/mysqld.sock +default_character_set = utf8 + +#---------------------------------------------------------------- + +[safe_mysqld] + +syslog + +#---------------------------------------------------------------- + +[mysqld] + + + + + + +user = mysql +port = [% database.dbport %] +socket = /var/run/mysqld/mysqld.sock + +max_connections = 2048 +back_log = 128 +max_connect_errors = 1000 +connect_timeout = 2 +wait_timeout = 60 + +max_allowed_packet = 16M +net_buffer_length = 8K + +default_character_set = utf8 +character_set_server = utf8 + +default_collation = utf8_general_ci +init_connect = 'SET NAMES utf8; SET sql_mode = STRICT_TRANS_TABLES' + +basedir = /usr +datadir = /var/lib/mysql +tmpdir = /tmp +language = /usr/share/mysql/english +log-error = /var/log/mysql/mysqld.err +pid-file = /var/run/mysqld/mysqld.pid +log_slow_queries = /var/log/mysql/slow-queries.log +log_output = FILE +long_query_time = 5 +log_long_format +#log = /var/log/mysql/queries.log + + +#Binlog options +log_bin = /var/lib/mysql/log-bin +max_binlog_size = 512M +expire_logs_days = 90 #Values 0-99 +binlog_format = row +binlog_cache_size = 1M +sync_binlog = 1 + +relay_log = /var/lib/mysql/log-relay-bin +max_relay_log_size = 512M + + + + +#Replication options + +server_id = 2 +auto_increment_offset = 2 +auto_increment_increment = 2 +master-host = sp1 + + + + +master-port=[% database.dbport %] +master-user=[% mysql.repuser %] +master-password=[% mysql.reppassword %] +master-connect-retry=10 + +replicate-wild-do-table=kamailio.% +replicate-wild-do-table=provisioning.% +replicate-wild-do-table=operating.% +replicate-wild-do-table=billing.% +replicate-wild-do-table=accounting.% +replicate-wild-do-table=syslog.% +replicate-ignore-table=accounting.acc_backup +replicate-ignore-table=accounting.acc_trash +replicate-ignore-table=kamailio.acc_backup +replicate-ignore-table=kamailio.acc_trash + + + + +table_cache = 4096 +join_buffer_size = 8M +tmp_table_size = 64M +sort_buffer_size = 8M +thread_cache_size = 64 +thread_concurrency = 8 +thread_stack = 192K + + + +# Query cache, disabled +query_cache_size = 0 +query_cache_type = 1 +query_cache_limit = 2M + +transaction_isolation = REPEATABLE-READ + +# MyISAM options +key_buffer_size = 256M +read_buffer_size = 2M +read_rnd_buffer_size = 8M +myisam_sort_buffer_size = 128M +bulk_insert_buffer_size = 64M +myisam_max_sort_file_size = 10G +myisam_repair_threads = 2 +#myisam_recover_options = DEFAULT + + + +# InnoDB options +innodb_data_home_dir = /var/lib/mysql +innodb_data_file_path = ibdata1:10M:autoextend +innodb_file_per_table +innodb_buffer_pool_size = [% database.bufferpoolsize %] +innodb_additional_mem_pool_size = 32M +innodb_log_group_home_dir = /var/lib/mysql +innodb_log_files_in_group = 4 +innodb_log_file_size = 128M +innodb_log_buffer_size = 8M +innodb_max_dirty_pages_pct = 80 +innodb_flush_log_at_trx_commit = 1 +innodb_lock_wait_timeout = 50 +innodb_flush_method = O_DIRECT +innodb_thread_concurrency = 32 +innodb_autoinc_lock_mode = 1 +innodb_locks_unsafe_for_binlog +innodb_fast_shutdown = 1 +innodb_max_purge_lag = 0 + +#---------------------------------------------------------------- + +[mysqldump] + +quick +max_allowed_packet = 16M +single_transaction + + +#---------------------------------------------------------------- + +[mysql] + +#no_auto_rehash + + +#---------------------------------------------------------------- + +[myisamchk] + +# databases +key_buffer = 256M +sort_buffer = 256M +read_buffer = 64M +write_buffer = 64M diff --git a/testsuite/ngcp-config/templates/testsuite/mysql/ngcpcfg.services b/testsuite/ngcp-config/templates/testsuite/mysql/ngcpcfg.services new file mode 100644 index 00000000..6c658e15 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/mysql/ngcpcfg.services @@ -0,0 +1,6 @@ +#!/bin/bash + +/etc/init.d/mysql restart +sleep 2 + +# vim: ft=sh diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/logging.conf.postbuild b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/logging.conf.postbuild new file mode 100644 index 00000000..112a1e79 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/logging.conf.postbuild @@ -0,0 +1 @@ +echo chgrp www-data ${output_file} diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/logging.conf.tt2 b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/logging.conf.tt2 new file mode 100644 index 00000000..1fcedb46 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/logging.conf.tt2 @@ -0,0 +1,16 @@ + +log4perl.logger.Sipwise.Provisioning = [% ossbss.logging.ossbss.level %], ProvSyslogApp + +log4perl.appender.ProvSyslogApp = Log::Dispatch::Syslog +log4perl.appender.ProvSyslogApp.facility = [% ossbss.logging.ossbss.facility %] +log4perl.appender.ProvSyslogApp.ident = [% ossbss.logging.ossbss.identity %] +log4perl.appender.ProvSyslogApp.layout = PatternLayout +log4perl.appender.ProvSyslogApp.layout.ConversionPattern = %M: %m%n + +log4perl.logger.Catalyst = WARN, CatalystSyslogApp +log4perl.logger.csc = [% ossbss.logging.web.level %], CatalystSyslogApp +log4perl.logger.admin = [% ossbss.logging.web.level %], CatalystSyslogApp + +log4perl.appender.CatalystSyslogApp = Log::Dispatch::Syslog +log4perl.appender.CatalystSyslogApp.facility = [% ossbss.logging.web.facility %] +log4perl.appender.CatalystSyslogApp.layout = PatternLayout diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/mysql_values.cfg.services b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/mysql_values.cfg.services new file mode 100644 index 00000000..da2aa7e7 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/mysql_values.cfg.services @@ -0,0 +1,21 @@ +#!/bin/bash + +MYSQL_USER='sipwise' + +. /etc/mysql/sipwise.cnf +. /etc/ngcp-ossbss/mysql_values.cfg + +#dispatcher values reloaded +MYCOM="mysql -u$MYSQL_USER -p$SIPWISE_DB_PASSWORD kamailio -e" +$MYCOM "update dispatcher set destination='sip:$EADDRESS:$ASTERISK_PORT' where setid='2' " +$MYCOM "update dispatcher set destination='sip:$EADDRESS:$SEMS_PORT' where setid='3' " + +#xmlrcp values reloaded + #sems ip does not need to change + #kamailio rpc port not configurable at the moment +MYCOM="mysql -u$MYSQL_USER -p$SIPWISE_DB_PASSWORD provisioning -e" +$MYCOM "update xmlhosts set ip='$EADDRESS' where id='1' " +$MYCOM "update xmlhosts set port='$SEMS_XMLRPCPORT' where id='2' " + +/etc/init.d/kamailio restart +sleep 2 diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/mysql_values.cfg.tt2 b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/mysql_values.cfg.tt2 new file mode 100644 index 00000000..1a64c2cb --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/mysql_values.cfg.tt2 @@ -0,0 +1,4 @@ +EADDRESS='[% networking.eaddress %]' +ASTERISK_PORT='[% asterisk.sip.bindport %]' +SEMS_PORT='[% sems.bindport %]' +SEMS_XMLRPCPORT='[% sems.xmlrpcport %]' diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/provisioning.conf.tt2 b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/provisioning.conf.tt2 new file mode 100644 index 00000000..ccc16aa8 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/provisioning.conf.tt2 @@ -0,0 +1,65 @@ + + + + + + [% FOREACH aclentry = ossbss.provisioning.acl %] + <[% aclentry.user %] password="[% aclentry.pass %]" [% FOREACH aclallow = aclentry.allow %][% aclallow %]="" [% END %] /> + [% END %] + + + + + error + unknown + cfu_on + cfu_off + cfb_on + cfb_off + cft_on + cft_off + cfna_on + cfna_off + reminder_on + reminder_off + + + voicebox + + [% FOREACH backend = ossbss.provisioning.backends.enabled %] + [% backend %] + [% END %] + diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/provisioning.htpasswd.tt2 b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/provisioning.htpasswd.tt2 new file mode 100644 index 00000000..35d91576 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-ossbss/provisioning.htpasswd.tt2 @@ -0,0 +1,4 @@ + +[% FOREACH htentry = ossbss.htpasswd %] +[% htentry.user %]:[% htentry.pass %] +[% END %] diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-www-admin/admin.conf.services b/testsuite/ngcp-config/templates/testsuite/ngcp-www-admin/admin.conf.services new file mode 100644 index 00000000..a2eac8c6 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-www-admin/admin.conf.services @@ -0,0 +1,2 @@ + +/etc/init.d/apache2 reload diff --git a/testsuite/ngcp-config/templates/testsuite/ngcp-www-admin/admin.conf.tt2 b/testsuite/ngcp-config/templates/testsuite/ngcp-www-admin/admin.conf.tt2 new file mode 100644 index 00000000..62a230c8 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/ngcp-www-admin/admin.conf.tt2 @@ -0,0 +1,40 @@ + + + + + + + + [% FOREACH feecsvelement = www_admin.fees_csv.element_order %] + [% feecsvelement %] + [% END %] + + + [% FOREACH speeddialvsc = www_admin.speed_dial_vsc_presets.vsc %] + [% speeddialvsc %] + [% END %] + + diff --git a/testsuite/ngcp-config/templates/testsuite/precedence/all/test.customtt.tt2 b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.customtt.tt2 new file mode 100644 index 00000000..cf566826 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.customtt.tt2 @@ -0,0 +1 @@ +test.custom.tt2 diff --git a/testsuite/ngcp-config/templates/testsuite/precedence/all/test.customtt.tt2.sp1 b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.customtt.tt2.sp1 new file mode 100644 index 00000000..66a8840b --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.customtt.tt2.sp1 @@ -0,0 +1 @@ +test.custom.tt2.sp1 diff --git a/testsuite/ngcp-config/templates/testsuite/precedence/all/test.tt2 b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.tt2 new file mode 100644 index 00000000..326dad1a --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.tt2 @@ -0,0 +1 @@ +test.tt2 diff --git a/testsuite/ngcp-config/templates/testsuite/precedence/all/test.tt2.sp1 b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.tt2.sp1 new file mode 100644 index 00000000..a5ca125e --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/precedence/all/test.tt2.sp1 @@ -0,0 +1 @@ +test.tt2.sp1 diff --git a/testsuite/ngcp-config/templates/testsuite/testtemplate.tt2 b/testsuite/ngcp-config/templates/testsuite/testtemplate.tt2 new file mode 100644 index 00000000..9b6389c0 --- /dev/null +++ b/testsuite/ngcp-config/templates/testsuite/testtemplate.tt2 @@ -0,0 +1 @@ +[% TAGS [- -] %]foo diff --git a/testsuite/ngcp-network b/testsuite/ngcp-network new file mode 100644 index 00000000..75e70b0e --- /dev/null +++ b/testsuite/ngcp-network @@ -0,0 +1,120 @@ +#!/bin/bash + +# defaults +if [ -d local ] ; then # running with carton + CMD="perl -Ilocal/lib/perl5 -CSD ../sbin/ngcp-network" +else + CMD="perl -CSD ../sbin/ngcp-network" +fi + +count=0 +RC=0 + +OUTPUT=$(mktemp) || exit 1 + +# generated files +rm -rf testfiles +mkdir testfiles + +bailout() { + rm -f "$OUTPUT" +} + +trap bailout 1 2 3 3 6 9 14 15 + +OK() { + printf "ok ${1} ${2}\n" +} + +FAIL() { + printf "not ok ${1} ${2}\n" + RC=1 +} + +assertEqualFiles() { + [ "$#" -eq 2 ] || return 1 + + (( count++ )) + + if cmp "$1" "$2" 2>/dev/null ; then + OK "$count" "$2" >> "$OUTPUT" + else + echo "files $1 and $2 do not match" >&2 + FAIL "$count" "$2 # $2 differs from $1" >> "$OUTPUT" + return 1 + fi +} + + +# make sure ip/netmask/interface can be set on exisiting host +${CMD} --input-file=network-config/network_pro.yml --output-file=testfiles/eth42.yml \ + --host=sp1 --set-interface=eth42 --ip=1.2.3.4 --netmask=255.255.255.248 +assertEqualFiles testfiles/eth42.yml network-config/eth42.yml + +# verify deployment steps +ROLE=sp1; PEER=sp2; DEFAULT_INSTALL_DEV=eth0; INTERNAL_DEV=eth1; + +in_counter=1; out_counter=1 # do not hardcode input/output files + +${CMD} --host=$ROLE --set-interface=lo --ip=127.0.0.1 --netmask=255.0.0.0 \ + --hwaddr=00:00:00:00:00:00 --ipv6='::1' --type=web_int \ + --input-file=network-config/network_pro.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) + +${CMD} --host=$ROLE --set-interface=lo --shared-ip=none --shared-ipv6=none \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$ROLE --set-interface=$DEFAULT_INSTALL_DEV --ip=1.2.3.4 \ + --netmask=255.255.255.0 --hwaddr=de:ad:be:ef:23:42 --dns=2.3.4.5 --dns=3.4.5.6 \ + --gateway=1.2.3.1 \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$ROLE --set-interface=$INTERNAL_DEV --ip=192.168.255.251 \ + --netmask=255.255.255.248 --hwaddr=de:ad:be:ef:42:23 \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$ROLE --peer=$PEER \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$ROLE --move-from=lo --move-to=$INTERNAL_DEV --type=ha_int \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$PEER --peer=$ROLE \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$PEER --set-interface=lo --shared-ip=none --shared-ipv6=none \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$PEER --set-interface=lo --ipv6='::1' --ip=127.0.0.1 --netmask=255.0.0.0 --hwaddr=00:00:00:00:00:00 \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$PEER --set-interface=eth1 --ip=192.168.255.252 --netmask=255.255.255.248 --type=ha_int \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$PEER --role=proxy --role=lb --role=mgmt \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deploy_${out_counter}.yml +((out_counter++)) ; ((in_counter++)) + +${CMD} --host=$PEER --set-interface=lo --type=sip_int --type=web_ext --type=sip_ext \ + --type=rtp_ext --type=ssh_ext --type=mon_ext --type=web_int \ + --input-file=testfiles/deploy_${in_counter}.yml --output-file=testfiles/deployment.yml + +assertEqualFiles testfiles/deployment.yml network-config/deployment.yml + + +# TAP output +printf "1..$count\n" +cat "$OUTPUT" + +bailout + +exit $RC diff --git a/testsuite/ngcpcfg-testsuite.cfg b/testsuite/ngcpcfg-testsuite.cfg new file mode 100644 index 00000000..d37da945 --- /dev/null +++ b/testsuite/ngcpcfg-testsuite.cfg @@ -0,0 +1,31 @@ +# Filename: /etc/ngcp-config/ngcpcfg.cfg +# Purpose: main configuration file for ngcpcfg tools +# Note: do not modify unless you have a really good reason to do so + +# directory name where ngcpcfg is managed through git +NGCPCTL_MAIN='./ngcp-config' +NGCPCTL_CONFIG="${NGCPCTL_MAIN}/config.yml" +HOST_CONFIG="${NGCPCTL_MAIN}/config.$(hostname).yml" +LOCAL_CONFIG="${NGCPCTL_MAIN}/config.local.yml" +CONSTANTS_CONFIG="${NGCPCTL_MAIN}/constants.yml" + +# configuration files that should be managed +CONFIG_POOL='/tmp/output' + +# location of templates +TEMPLATE_POOL="${NGCPCTL_MAIN}/templates/testsuite" + +# location of service definitions +SERVICES_POOL="${NGCPCTL_MAIN}/templates/testsuite" + +## NOTE: only supported with ngcp-ngcpcfg-ha +# supported values: {true,false} +SHARED_STORAGE='true' + +# name of shared storage +GLUSTERFS='./glusterfs' + +# name of shared storage repository +NGCPCTL_SHARE="${GLUSTERFS}/ngcpcfg-share" + +## END OF FILE #################################################################