stage_4 support 68/5668/3
authorMike Marchetti <mmarchetti@sandvine.com>
Mon, 6 Nov 2017 16:04:38 +0000 (11:04 -0500)
committerMike Marchetti <mmarchetti@sandvine.com>
Thu, 16 Nov 2017 15:09:34 +0000 (10:09 -0500)
Signed-off-by: Mike Marchetti <mmarchetti@sandvine.com>
Change-Id: Ic997b0baf6ad9cedf486a506f050e016a0c5796a

17 files changed:
descriptor-packages/vnfd/cirros_vnf/src/cirros_vnfd.yaml
jenkins/ci-pipelines/ci_helper.groovy
jenkins/ci-pipelines/ci_stage_1.groovy
jenkins/ci-pipelines/ci_stage_2.groovy
jenkins/ci-pipelines/ci_stage_3.groovy
jenkins/ci-pipelines/ci_stage_4.groovy
jenkins/common/install_common
jenkins/system/start_build
systest/.gitignore
systest/Makefile
systest/lib/openstack/fixtures.py
systest/lib/osm/fixtures.py
systest/lib/osm/osm.py
systest/testcases/vnfs/test_vnfs.py
tools/.gitignore [new file with mode: 0644]
tools/gen-repo.sh
tools/generatechangelog.sh [new file with mode: 0755]

index e868ae0..ee30fb3 100644 (file)
@@ -29,7 +29,7 @@ vnfd:vnfd-catalog:
                 storage-gb: 2
 
             # Image/checksum or image including the full path
-            image: 'CirrOS 0.3.4 64-bit'
+            image: 'cirros-0.3.5-x86_64-disk.img'
             #checksum: 
 
             interface:
index c047a12..21a8373 100644 (file)
@@ -45,6 +45,10 @@ def lxc_run(container_name,cmd) {
     return sh(returnStdout: true, script: "lxc exec ${container_name} -- ${cmd}").trim()
 }
 
+def lxc_file_push(container_name,file,destination) {
+    return sh(returnStdout: true, script: "lxc file push ${file} ${container_name}/${destination}").trim()
+}
+
 // start a http server
 // return the http server URL
 def start_http_server(repo_dir,server_name) {
@@ -57,12 +61,22 @@ def lxc_get_file(container_name,file,destination) {
     sh "lxc file pull ${container_name}/${file} ${destination}"
 }
 
-def systest_run(container_name, test) {
+def systest_run(container_name, test, source_rc = null) {
     // need to get the SO IP inside the running container
     so_ip = lxc_run(container_name,"lxc list SO-ub -c 4|grep eth0 |awk '{print \$2}'")
+    ro_ip = lxc_run(container_name,"lxc list RO -c 4|grep eth0 |awk '{print \$2}'")
     //container_ip = get_ip_from_container(container_name)
-    // 
-    lxc_run(container_name, "make -C devops/systest OSM_HOSTNAME=${so_ip} ${test}")
+    if ( source_rc ) {
+        pre_source = "/tmp/" + source_rc.substring(source_rc.lastIndexOf('/')+1)
+        lxc_file_push(container_name,source_rc,pre_source)
+        lxc_run(container_name, "sh -c '. ${pre_source}; make -C devops/systest OSM_HOSTNAME=${so_ip} OSM_RO_HOSTNAME=${ro_ip} ${test}'")
+    }
+    else
+    {
+        lxc_run(container_name, "make -C devops/systest OSM_HOSTNAME=${so_ip} OSM_RO_HOSTNAME=${ro_ip} ${test}")
+    }
     lxc_get_file(container_name, "/root/devops/systest/reports/pytest-${test}.xml",'.')
 }
 
index 69b9018..117265a 100644 (file)
@@ -48,6 +48,11 @@ node("${params.NODE}") {
                stage_name = "stage_2"
                break
         }
+        do_stage_4 = false
+        if (params.DO_STAGE_4)
+        {
+            do_stage_4 = params.DO_STAGE_4
+        }
 
         // pipeline running from gerrit trigger.
         // kickoff the downstream multibranch pipeline
@@ -58,6 +63,7 @@ node("${params.NODE}") {
             string(name: 'GERRIT_PATCHSET_REVISION', value: GERRIT_PATCHSET_REVISION),
             string(name: 'PROJECT_URL_PREFIX', value: params.PROJECT_URL_PREFIX),
             booleanParam(name: 'TEST_INSTALL', value: params.TEST_INSTALL),
+            booleanParam(name: 'DO_STAGE_4', value: do_stage_4),
         ]
      
         if ( params.STAGE )
index d51c298..7620111 100644 (file)
@@ -26,8 +26,8 @@ def project_checkout(url_prefix,project,refspec,revision) {
     }
 }
 
-def ci_pipeline(mdg,url_prefix,project,branch,refspec,revision,build_system,artifactory_server,docker_args="") {
-    println("build_system = ${build_system}")
+def ci_pipeline(mdg,url_prefix,project,branch,refspec,revision,do_stage_3,artifactory_server,docker_args="",do_stage_4=false) {
+    println("do_stage_3= ${do_stage_3}")
     ci_helper = load "devops/jenkins/ci-pipelines/ci_helper.groovy"
 
     stage('Prepare') {
@@ -66,13 +66,14 @@ def ci_pipeline(mdg,url_prefix,project,branch,refspec,revision,build_system,arti
         ci_helper.archive(artifactory_server,mdg,branch,'untested')
     }
 
-    if ( build_system ) {
+    if ( do_stage_3 ) {
 
         stage('Build System') {
             def downstream_params_stage_3 = [
                 string(name: 'GERRIT_BRANCH', value: "${branch}"),
                 string(name: 'UPSTREAM_JOB_NAME', value: "${JOB_NAME}" ),
                 string(name: 'UPSTREAM_JOB_NUMBER', value: "${BUILD_NUMBER}" ),
+                booleanParam(name: 'DO_STAGE_4', value: do_stage_4 )
             ]
             stage_3_job = "osm-stage_3"
             if ( JOB_NAME.contains('merge') ) {
index 082f728..9ea2d7f 100644 (file)
@@ -30,8 +30,10 @@ properties([
         string(defaultValue: '', description: '', name: 'UPSTREAM_JOB_NUMBER'),
         string(defaultValue: 'dpkg1', description: '', name: 'GPG_KEY_NAME'),
         string(defaultValue: 'artifactory-osm', description: '', name: 'ARTIFACTORY_SERVER'),
+        string(defaultValue: 'osm-stage_4', description: '', name: 'DOWNSTREAM_STAGE_NAME'),
         booleanParam(defaultValue: false, description: '', name: 'SAVE_CONTAINER_ON_FAIL'),
-        booleanParam(defaultValue: false, description: '', name: 'SAVE_CONTAINER_ON_PASS')
+        booleanParam(defaultValue: false, description: '', name: 'SAVE_CONTAINER_ON_PASS'),
+        booleanParam(defaultValue: false, description: '', name: 'DO_STAGE_4'),
     ])
 ])
 
@@ -181,11 +183,22 @@ node("${params.NODE}") {
                """
         }
 
-        stage("Test") {
+        stage("Smoke") {
             ci_helper.systest_run(container_name, 'smoke')
             junit '*.xml'
         }
 
+        if ( params.DO_STAGE_4 ) {
+            stage("stage_4") {
+                def downstream_params = [
+                    string(name: 'CONTAINER_NAME', value: container_name),
+                ]
+                stage_4_result = build job: "${params.DOWNSTREAM_STAGE_NAME}/${GERRIT_BRANCH}", parameters: downstream_params, propagate: false 
+               
+                currentBuild.result = stage_4_result.result
+            }
+        }
+
         // save the artifacts of this build if this is a merge job
         if ( save_artifacts ) {
             stage("Archive") {
@@ -200,6 +213,7 @@ node("${params.NODE}") {
         }
     }
     catch(caughtError) {
+        println("Caught error!")
         error = caughtError
         currentBuild.result = 'FAILURE'
     }
index 928fdd8..5e390e3 100644 (file)
 
 properties([
     parameters([
+        string(defaultValue: '', description: '', name: 'CONTAINER_NAME' ),
         string(defaultValue: 'osm-stage_3', description: '', name: 'UPSTREAM_PROJECT'),
-        string(defaultValue: 'release', description: '', name: 'RELEASE'),
         string(defaultValue: 'pipeline', description: '', name: 'NODE'),
+        string(defaultValue: '/home/jenkins/hive/openstack-telefonica.rc', description: '', name: 'HIVE_VIM_1'),
     ])
 ])
 
@@ -31,25 +32,26 @@ node("${params.NODE}") {
 
     ci_helper = load "jenkins/ci-pipelines/ci_helper.groovy"
 
-    stage("get artifacts") {
-        // grab the upstream artifact name
+    if ( params.CONTAINER_NAME ) {
+        container_name = params.CONTAINER_NAME
+    }
+    else if ( params.UPSTREAM_PROJECT ) {
         step ([$class: 'CopyArtifact',
               projectName: "${params.UPSTREAM_PROJECT}/${BRANCH_NAME}"])
+        container_name = sh(returnStdout: true, script: 'cat build_version.txt').trim()
     }
-
-    container_name = sh(returnStdout: true, script: 'cat build_version.txt').trim()
-
-    stage("Test") {
-        ci_helper.systest_run(container_name, 'smoke')
-        junit '*.xml'
+    else {
+        println("no OSM container found")
+        currentBuild.result = 'FAILURE'
+        return
     }
+    println("OSM container = ${container_name}")
 
-/*  os_credentials = "OS_AUTH_URL=${params.OS_AUTH_URL} OS_USERNAME=${params.OS_USERNAME} OS_PASSWORD=${params.OS_PASSWORD} OS_PROJECT_NAME=${params.OS_PROJECT_NAME}"
-        stage("cirros-test") {
-            sh """
-               make -C systest OSM_HOSTNAME=${osm_ip} ${os_credentials} cirros
-               """
-            junit 'systest/reports/pytest-cirros.xml'
+    if ( params.HIVE_VIM_1 ) {
+        stage( "${params.HIVE_VIM_1}" ) {
+            ci_helper.systest_run(container_name, 'cirros', params.HIVE_VIM_1)
+            ci_helper.systest_run(container_name, 'ns_scale', params.HIVE_VIM_1)
+            junit '*.xml'
         }
-*/
+    }
 }
index 3884b0e..b769014 100755 (executable)
@@ -21,7 +21,7 @@ REPOSITORY_KEY="OSM%20ETSI%20Release%20Key.gpg"
 REPOSITORY="stable"
 REPOSITORY_BASE="http://osm-download.etsi.org/repository/osm/debian"
 
-while getopts ":r:k:u:R:b:" o; do
+while getopts ":r:k:u:R:b:-:" o; do
     case "${o}" in
         r)
             REPOSITORY=${OPTARG}
@@ -38,6 +38,8 @@ while getopts ":r:k:u:R:b:" o; do
         b)
             ## ignore branch option
             ;;
+        -)
+            ;;
     esac
 done
 
index e1b91ac..03fe930 100755 (executable)
@@ -53,7 +53,7 @@ EOF
 systemctl enable lxd-bridge
 systemctl start lxd-bridge
 
-apt-get install -y python-pip python python-pycurl charm-tools python-pytest
+apt-get install -y python-pip python python-pycurl charm-tools python-pytest python-openstackclient
 
 apt-get install -y python-osmclient
 
index 97f7edd..1fae3e1 100644 (file)
@@ -3,3 +3,4 @@ pytest-output.xml
 .cache
 descriptor-packages/
 *.xml
+images/*
index 90c56d5..6631eb1 100644 (file)
 #    export OS_PASSWORD=admin
 #    export OS_PROJECT_NAME=admin
 OSM_HOSTNAME ?=
+OSM_RO_HOSTNAME ?=
 OS_AUTH_URL ?=
 OS_USERNAME ?=
 OS_PASSWORD_NAME ?=
 OS_PROJECT_NAME ?=
+VIM_CONFIG ?=
 
 TOPDIR=$(shell readlink -f .|sed -e 's/systest.*//')
 
@@ -49,11 +51,22 @@ endif
 ifdef TEST_NSD_DESCRIPTORS
     OPTION_TEST_NSD_DESCRIPTORS=--osm-nsd-descriptor-packages $(TEST_NSD_DESCRIPTORS)
 endif
+ifdef OSM_RO_HOSTNAME
+    OPTION_RO_HOSTNAME=--osm_ro_host $(OSM_RO_HOSTNAME)
+else
+    OPTION_RO_HOSTNAME=--osm_ro_host $(OSM_HOSTNAME)
+endif
+
+ifdef VIM_CONFIG
+    OPTION_VIM_CONFIG=--vim-config "$(VIM_CONFIG)"
+else
+endif
 
 DESCRIPTOR_DIR ?= $(TOPDIR)/descriptor-packages
 
-TEST_OSM_NS_NAME_PREFIX=pytest-$(shell date +%D-%T)-
-OPTION_TEST_OSM_NS_NAME_PREFIX=--osm-ns-name-prefix $(TEST_OSM_NS_NAME_PREFIX)
+#TODO: Need to re-add this once charm application name length issue is resolved
+#TEST_OSM_NS_NAME_PREFIX=pytest-$(shell date +%D-%T)-
+#OPTION_TEST_OSM_NS_NAME_PREFIX=--osm-ns-name-prefix $(TEST_OSM_NS_NAME_PREFIX)
 
 JUNITXML_DIR = reports
 
@@ -87,11 +100,13 @@ report_dir:
        @mkdir -p reports
 
 _run_test: report_dir
-       $(Q)py.test \
+       -$(Q)py.test \
         --osmhost $(OSM_HOSTNAME) \
+        $(OPTION_RO_HOSTNAME) \
         $(OPTION_OS_AUTH_URL) \
         $(OPTION_OS_USERNAME) \
         $(OPTION_OS_PASSWORD) \
+        $(OPTION_VIM_CONFIG) \
         $(OPTION_OS_PROJECT_NAME) \
         $(OPTION_TEST_VNFD_DESCRIPTORS) \
         $(OPTION_TEST_NSD_DESCRIPTORS) \
@@ -106,9 +121,34 @@ $(DESCRIPTOR_DIR)/vnfd/cirros_vnf/build/%.tar.gz:
 $(DESCRIPTOR_DIR)/nsd/cirros_ns/build/%.tar.gz:
        $(MAKE) -C $(DESCRIPTOR_DIR)/nsd/cirros_ns
 
+images/cache/cirros-0.3.5-x86_64-disk.img:
+       $(Q)mkdir -p images/cache
+       $(Q)wget http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img -O $@
+
+images/cache/Fedora-x86_64-20-20131211.1-sda-ping.qcow2:
+       $(Q)mkdir -p images/cache
+       $(Q)wget ftp://osm-download.etsi.org/examples/ping_pong_ns/images/Fedora-x86_64-20-20131211.1-sda-ping.qcow2 -O $@
+
+images/cache/Fedora-x86_64-20-20131211.1-sda-pong.qcow2:
+       $(Q)mkdir -p images/cache
+       $(Q)wget ftp://osm-download.etsi.org/examples/ping_pong_ns/images/Fedora-x86_64-20-20131211.1-sda-pong.qcow2 -O $@
+
+# images are prefixed with 'osm/' to separate osm uploaded images from VIM installed images
+OSM_IMAGE_PREFIX ?=
+
+ifdef OS_AUTH_URL
+images/%.qcow2 images/%.img:
+       $(Q)openstack image show $(OSM_IMAGE_PREFIX)$(shell basename $@) || \
+          sh -c "make images/cache/$(shell basename $@) && openstack image create --file images/cache/$(shell basename $@) $(OSM_IMAGE_PREFIX)$(shell basename $@)"
+else
+images/%.img:
+       echo "No method selected to upload image to VIM"
+endif
+
 cirros: check_OSM_HOSTNAME check_openstack_env \
         $(DESCRIPTOR_DIR)/vnfd/cirros_vnf/build/cirros_vnf.tar.gz \
-        $(DESCRIPTOR_DIR)/nsd/cirros_ns/build/cirros_ns.tar.gz
+        $(DESCRIPTOR_DIR)/nsd/cirros_ns/build/cirros_ns.tar.gz \
+        images/cirros-0.3.5-x86_64-disk.img
        $(Q)$(MAKE) \
         TEST_VNFD_DESCRIPTORS=$(DESCRIPTOR_DIR)/vnfd/cirros_vnf/build/cirros_vnf.tar.gz \
         TEST_NSD_DESCRIPTORS=$(DESCRIPTOR_DIR)/nsd/cirros_ns/build/cirros_ns.tar.gz \
@@ -117,7 +157,8 @@ cirros: check_OSM_HOSTNAME check_openstack_env \
 
 ns_scale: check_OSM_HOSTNAME check_openstack_env \
         $(DESCRIPTOR_DIR)/vnfd/cirros_vnf/build/cirros_vnf.tar.gz \
-        $(DESCRIPTOR_DIR)/nsd/cirros_ns/build/cirros_ns.tar.gz
+        $(DESCRIPTOR_DIR)/nsd/cirros_ns/build/cirros_ns.tar.gz \
+        images/cirros-0.3.5-x86_64-disk.img
        $(Q)$(MAKE) \
         TEST_VNFD_DESCRIPTORS=$(DESCRIPTOR_DIR)/vnfd/cirros_vnf/build/cirros_vnf.tar.gz \
         TEST_NSD_DESCRIPTORS=$(DESCRIPTOR_DIR)/nsd/cirros_ns/build/cirros_ns.tar.gz \
@@ -146,7 +187,9 @@ $(DESCRIPTOR_DIR)/nsd/ping_pong_ns/build/%.tar.gz:
 ping_pong: check_OSM_HOSTNAME check_openstack_env \
     $(DESCRIPTOR_DIR)/vnfd/ping_vnf/build/ping_vnf.tar.gz \
     $(DESCRIPTOR_DIR)/vnfd/pong_vnf/build/pong_vnf.tar.gz \
-    $(DESCRIPTOR_DIR)/nsd/ping_pong_ns/build/ping_pong_ns.tar.gz
+    $(DESCRIPTOR_DIR)/nsd/ping_pong_ns/build/ping_pong_ns.tar.gz \
+    images/Fedora-x86_64-20-20131211.1-sda-ping.qcow2 \
+    images/Fedora-x86_64-20-20131211.1-sda-pong.qcow2
        $(Q)$(MAKE) \
         TEST_VNFD_DESCRIPTORS="$(DESCRIPTOR_DIR)/vnfd/ping_vnf/build/ping_vnf.tar.gz,$(DESCRIPTOR_DIR)/vnfd/pong_vnf/build/pong_vnf.tar.gz" \
         TEST_NSD_DESCRIPTORS=$(DESCRIPTOR_DIR)/nsd/ping_pong_ns/build/ping_pong_ns.tar.gz \
index e6953b7..5b654a9 100644 (file)
@@ -23,6 +23,7 @@ def openstack_add_options(parser):
     parser.addoption("--os-username", default="", help="openstack username")
     parser.addoption("--os-password", default="", help="openstack password")
     parser.addoption("--os-project-name", default="", help="openstack project name")
+    parser.addoption("--vim-config", default="", help="vim/openstack specific configuration")
 
 @pytest.fixture
 def openstack(request):
@@ -34,5 +35,6 @@ def openstack(request):
     access['vim-tenant-name'] = request.config.getoption("--os-project-name")
     access['vim-type'] = 'openstack'
     access['description'] = 'pytest system test'
+    access['config'] = request.config.getoption("--vim-config")
 
     return openstack.Openstack(access)
index edfc076..2c66ac5 100644 (file)
@@ -20,6 +20,7 @@ import json
 
 def osm_add_options(parser):
     parser.addoption("--osmhost", default="", help="osm hostname")
+    parser.addoption("--osm_ro_host", default="", help="osm ro_host")
     parser.addoption("--osm-descriptor-packages", default="", help="location of descriptor packages")
     parser.addoption("--osm-vnfd-descriptor-packages", default="", help="vnfd packages to test")
     parser.addoption("--osm-nsd-descriptor-packages", default="", help="nsd package to test")
@@ -40,11 +41,13 @@ def osm(request):
 def osm(request):
     from lib.osm import osm
     osmhost=request.config.getoption("--osmhost")
+    osm_ro_host=request.config.getoption("--osm_ro_host")
     descriptors_dir=request.config.getoption("--osm-descriptor-packages")
     vnfd_descriptors_list=request.config.getoption("--osm-vnfd-descriptor-packages").split(',')
     nsd_descriptors_list=request.config.getoption("--osm-nsd-descriptor-packages").split(',')
     ns_name_prefix=request.config.getoption("--osm-ns-name-prefix")
     return osm.Osm(osmhost,
+                   ro_host=osm_ro_host,
                    descriptors_dir=descriptors_dir,
                    vnfd_descriptors_list=vnfd_descriptors_list,
                    nsd_descriptors_list=nsd_descriptors_list,
index 8798be8..f67cf57 100644 (file)
@@ -18,8 +18,8 @@
 from osmclient.client import client
 
 class Osm():
-    def __init__(self,osmhost,descriptors_dir=None,vnfd_descriptors_list=None,nsd_descriptors_list=None,ns_name_prefix=None):
-        self._OsmApi=client.Client(host=osmhost)
+    def __init__(self,osmhost,ro_host=None,descriptors_dir=None,vnfd_descriptors_list=None,nsd_descriptors_list=None,ns_name_prefix=None):
+        self._OsmApi=client.Client(host=osmhost,ro_host=ro_host)
         self._descriptors_dir = descriptors_dir
         self.vnfd_descriptors_list = vnfd_descriptors_list
         self.nsd_descriptors_list  = nsd_descriptors_list 
index d32fc71..d987e93 100644 (file)
@@ -89,7 +89,7 @@ class TestClass(object):
             assert utils.wait_for_value(lambda: osm.get_api().ns.get_field(ns_name,'operational-status'),result='vnf-init-phase')
 
             # make sure ns is running
-            assert utils.wait_for_value(lambda: osm.get_api().ns.get_field(ns_name,'operational-status'),result='running',wait_time=240)
+            assert utils.wait_for_value(lambda: osm.get_api().ns.get_field(ns_name,'operational-status'),result='running',wait_time=120)
 
             if ns_scale:
                 # for each descriptor, scale it
@@ -98,10 +98,10 @@ class TestClass(object):
                     assert not osm.get_api().ns.scale(ns_name, scale['name'], 1)
 
                     # ensure ns is scaling-out
-                    assert utils.wait_for_value(lambda: osm.get_api().ns.get_field(ns_name,'operational-status'),result='scaling-out',wait_time=240)
+                    assert utils.wait_for_value(lambda: osm.get_api().ns.get_field(ns_name,'operational-status'),result='scaling-out',wait_time=120)
 
                     # wait for ns to be in running-state
-                    assert utils.wait_for_value(lambda: osm.get_api().ns.get_field(ns_name,'operational-status'),result='running',wait_time=240)
+                    assert utils.wait_for_value(lambda: osm.get_api().ns.get_field(ns_name,'operational-status'),result='running',wait_time=120)
 
             time.sleep(10)
 
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644 (file)
index 0000000..d250963
--- /dev/null
@@ -0,0 +1 @@
+repos/*
index 6fb2ad8..502b941 100755 (executable)
@@ -14,6 +14,7 @@ function usage() {
     echo -e "  -h  <rsync user@host>    "
     echo -e "  -R  <rsync options>      "
     echo -e "  -P  <public key file>    "
+    echo -e "  -c  <changelogfile>      "
     exit 1
 }
 
@@ -46,8 +47,9 @@ RELEASE_DIR=ReleaseTWO
 RSYNC_USER_HOST=osmusers@osm-download.etsi.org
 CURR_DIR=$(pwd)
 PUBLIC_KEY_FILE=~/OSM\ ETSI\ Release\ Key.gpg
+CHANGE_LOG_FILE=
 
-while getopts ":p:i:o:k:j::d:b:r:h:R:P:" o; do
+while getopts ":p:i:o:k:j::d:b:r:h:R:P:c:" o; do
     case "${o}" in
         p)
             PASSPHRASE_FILE=${OPTARG}
@@ -82,6 +84,9 @@ while getopts ":p:i:o:k:j::d:b:r:h:R:P:" o; do
         P)
             PUBLIC_KEY_FILE=${OPTARG}
             ;;
+        c)
+            CHANGE_LOG_FILE=${OPTARG}
+            ;;
         *)
             usage
             exit 1
@@ -139,4 +144,7 @@ cd $CURR_DIR/$REPO_BASE
 # copy over the public key file
 [ "$PUBLIC_KEY_FILE" ] && cp "$PUBLIC_KEY_FILE" osm/debian/$RELEASE_DIR
 
+# copy over the changelog file
+[ "$CHANGE_LOG_FILE" ] && cp "$CHANGE_LOG_FILE" osm/debian/$RELEASE_DIR
+
 rsync -avR $RSYNC_OPTIONS osm/debian/$RELEASE_DIR rsync://$RSYNC_USER_HOST/repos
diff --git a/tools/generatechangelog.sh b/tools/generatechangelog.sh
new file mode 100755 (executable)
index 0000000..33a5302
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/bash
+if [ $# -ne 2 ]; then
+    echo "Usage $0 <repo> <outfile>"
+    exit 1
+fi
+
+REPO="$1"
+OUTFILE=$2
+
+modules="devops openvim RO SO UI IM osmclient"
+list=""
+for i in $modules; do
+    if [ $REPO == "$i" -o $REPO == "all" ]; then
+        list=$REPO
+        break
+    fi
+done
+
+[ $REPO == "all" ] && list=$modules
+
+if [ -z "$list" ]; then
+    echo "Repo must be one of these: $modules all"
+    exit 1
+fi
+
+echo "<h1>OSM Changelog</h1>" >> $OUTFILE
+for i in $list; do
+    echo
+    echo $i
+    if [ ! -d $i ]; then
+        git clone https://osm.etsi.org/gerrit/osm/$i
+    fi
+    git -C $i checkout master
+    git -C $i pull --rebase
+    git -C $i fetch --tags
+    TAG_START=$(git -C $i tag | sort -Vr | head -2 | sort -V | head -1)
+    TAG_END=$(git -C $i tag | sort -Vr | head -1)
+    echo "<h2>Changes for $i tag: ${TAG_START}..${TAG_END}</h2>" >> $OUTFILE
+    #git -C $i log --pretty=format:"* %h; author: %cn; date: %ci; subject:%s" ${TAG_START}..${TAG_END} >> $OUTFILE
+    git -C $i log --pretty=format:"<li> <a href=https://osm.etsi.org/gitweb/?p=osm/$i.git;a=commitdiff;h=%H>%h &bull;</a> %s</li> " --reverse  ${TAG_START}..${TAG_END} >> $OUTFILE
+    echo "" >> $OUTFILE
+done
+
+exit 0