Add 'packer-image-create' job

- add packer-image-create.groovy to create images with packer
- add 'tcp_tests/templates/_packer/' with configs for 'foundation'
  node

Change-Id: I23379bef389adad791df9343930095a57af3fb55
diff --git a/jobs/pipelines/packer-image-create.groovy b/jobs/pipelines/packer-image-create.groovy
new file mode 100644
index 0000000..94133a7
--- /dev/null
+++ b/jobs/pipelines/packer-image-create.groovy
@@ -0,0 +1,114 @@
+/**
+ *
+ * Deploy the product cluster using Jenkins master on CICD cluster
+ *
+ * Expected parameters:
+
+ *   IMAGE_NAME                    Name of the resulting image in the Glance or in artifacts
+
+ *   BUILD_CONFIG_DRIVE_PATH       Relative path in tcp-qa to the directory with meta-data and user-data files
+ *   BUILD_PACKER_CONFIG_PATH      Relative path in tcp-qa to the file with packer config (packer.json)
+ *   BASE_IMAGE_URL                Base image to build a new image, in qcow2. For example, released ubuntu cloud image
+ *   BASE_IMAGE_MD5                Base image MD5 checksum. Image will be re-downloaded if not match with the local image checksum
+
+ *   PACKER_URL                    URL to the zip archive with packer binary, see https://releases.hashicorp.com/packer/
+ *   PACKER_ZIP_MD5                MD5 of the zip archive with packer binary
+
+ *   OS_AUTH_URL                   OpenStack keystone catalog URL
+ *   OS_PROJECT_NAME               OpenStack project (tenant) name
+ *   OS_USER_DOMAIN_NAME           OpenStack user domain name
+ *   OS_CREDENTIALS                OpenStack username and password credentials ID in Jenkins
+ *   UPLOAD_IMAGE_TO_GLANCE        If True: upload image to glance; if False: store as an artifact
+
+ *   TCP_QA_REFS                   Reference to the tcp-qa change on review.gerrithub.io, like refs/changes/46/418546/41
+ */
+
+@Library('tcp-qa')_
+
+def common = new com.mirantis.mk.Common()
+def shared = new com.mirantis.system_qa.SharedPipeline()
+
+timeout(time: 6, unit: 'HOURS') {
+    node () {
+        try {
+
+            stage("Clean the environment and clone tcp-qa") {
+                deleteDir()
+                shared.run_cmd("""\
+                    git clone https://github.com/Mirantis/tcp-qa.git ${WORKSPACE}
+                """)
+                shared.update_working_dir(false)
+                sh "mkdir ./tmp"
+            }
+
+            def packer_zipname = "/tmp/packer.zip"
+            def configdrive_isoname = "./tmp/config-drive.iso"
+
+            stage("Prepare Packer") {
+                // Check that the archive is already downloaded and has a correct checksum. Remove if not match
+                if (fileExists(packer_zipname)) {
+                    sh(script: "bash -cex 'md5sum -c --status <(echo ${PACKER_ZIP_MD5} ${packer_zipname})' || rm -f ${packer_zipname}", returnStdout: true)
+                }
+                // If the file is missing or removed, then download it and check the checksum
+                if (!fileExists(packer_zipname)) {
+                    sh(script: "wget --quiet -O ${packer_zipname} ${PACKER_URL}", returnStdout: true)
+                    // Should fail the job if not match
+                    sh(script: "bash -cex 'md5sum -c --status <(echo ${PACKER_ZIP_MD5} ${packer_zipname})'", returnStdout: true)
+                }
+                sh "unzip ${packer_zipname}"
+            }
+
+            stage("Build the cloudinit ISO") {
+                // Check that genisoimage is installed, or try to install it
+                sh "which genisoimage || sudo apt-get -y install genisoimage"
+                // Generate config-drive ISO
+                sh "mkisofs -o ${configdrive_isoname} -V cidata -r -J --quiet ${BUILD_CONFIG_DRIVE_PATH}"
+            }
+
+            stage("Build the image '${IMAGE_NAME}'") {
+                // Build the image
+                sh (script: """\
+                    set -ex;
+                    export PACKER_LOG=1;
+                    export PACKER_CACHE_DIR='/tmp/packer_cache_${IMAGE_NAME}/';
+                    mkdir -p \${PACKER_CACHE_DIR};
+                    ./packer build -machine-readable -parallel=false -only='qemu' ${BUILD_PACKER_CONFIG_PATH};
+                """, returnStdout: true)
+            }
+
+
+            if (env.UPLOAD_IMAGE_TO_GLANCE) {
+
+                stage("Upload generated config drive ISO into volume on cfg01 node") {
+                    withCredentials([
+                       [$class          : 'UsernamePasswordMultiBinding',
+                       credentialsId   : env.OS_CREDENTIALS,
+                       passwordVariable: 'OS_PASSWORD',
+                       usernameVariable: 'OS_USERNAME']
+                    ]) {
+                        env.OS_IDENTITY_API_VERSION = 3
+
+                        def imagePath = "tmp/${IMAGE_NAME}/${IMAGE_NAME}.qcow2"
+                        shared.run_cmd("""\
+                            openstack --insecure image delete ${IMAGE_NAME} || true
+                            sleep 3
+                            openstack --insecure image create ${IMAGE_NAME} --file ${imagePath} --disk-format qcow2 --container-format bare
+                        """)
+                    }
+                }
+            } else {
+
+                stage("Archive artifacts") {
+                    archiveArtifacts artifacts: "tmp/${IMAGE_NAME}/${IMAGE_NAME}.qcow2"
+                }
+            }
+
+        } catch (e) {
+            common.printMsg("Job is failed", "purple")
+            throw e
+        } finally {
+            // Remove the image after job is finished
+            sh "rm -f ./tmp/${IMAGE_NAME}.qcow2 || true"
+        } // try
+    } // node
+} // timeout
\ No newline at end of file
diff --git a/src/com/mirantis/system_qa/SharedPipeline.groovy b/src/com/mirantis/system_qa/SharedPipeline.groovy
index 5a78a9f..06ca19d 100644
--- a/src/com/mirantis/system_qa/SharedPipeline.groovy
+++ b/src/com/mirantis/system_qa/SharedPipeline.groovy
@@ -185,15 +185,18 @@
         """)
 }
 
-def update_working_dir() {
+def update_working_dir(Boolean updateRequirements=true) {
         // Use to fetch a patchset from gerrit to the working dir
         run_cmd("""\
             if [ -n "$TCP_QA_REFS" ]; then
                 set -e
                 git fetch https://review.gerrithub.io/Mirantis/tcp-qa $TCP_QA_REFS && git checkout FETCH_HEAD || exit \$?
-            fi
-            pip install -r tcp_tests/requirements.txt
-        """)
+            fi""")
+        if (updateRequirements) {
+            run_cmd("""\
+                pip install -r tcp_tests/requirements.txt
+            """)
+        }
 }
 
 def swarm_bootstrap_salt_cluster_devops() {
diff --git a/tcp_tests/templates/_packer/foundation/config-drive/meta-data b/tcp_tests/templates/_packer/foundation/config-drive/meta-data
new file mode 100644
index 0000000..b0c74c9
--- /dev/null
+++ b/tcp_tests/templates/_packer/foundation/config-drive/meta-data
@@ -0,0 +1 @@
+hostname: foundation
diff --git a/tcp_tests/templates/_packer/foundation/config-drive/user-data b/tcp_tests/templates/_packer/foundation/config-drive/user-data
new file mode 100644
index 0000000..1d68c57
--- /dev/null
+++ b/tcp_tests/templates/_packer/foundation/config-drive/user-data
@@ -0,0 +1,72 @@
+#cloud-config, see http://cloudinit.readthedocs.io/en/latest/topics/examples.html
+
+ssh_pwauth: True
+users:
+  - name: root
+    sudo: ALL=(ALL) NOPASSWD:ALL
+    shell: /bin/bash
+  - name: jenkins
+    sudo: ALL=(ALL) NOPASSWD:ALL
+    shell: /bin/bash
+    ssh_authorized_keys:
+      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFSxeuXh2sO4VYL8N2dlNFVyNcr2RvoH4MeDD/cV2HThfU4/BcH6IOOWXSDibIU279bWVKCL7QUp3mf0Vf7HPuyFuC12QM+l7MwT0jCYh5um3hmAvM6Ga0nkhJygHexe9/rLEYzZJkIjP9/IS/YXSv8rhHg484wQ6qkEuq15nyMqil8tbDQCq0XQ+AWEpNpIa4pUoKmFMsOP8lq10KZXIXsJyZxizadr6Bh4Lm9LWrk8YCw7qP3rmgWxK/s8qXQh1ISZe6ONfcmk6p03qbh4H3CwKyWzxmnIHQvE6PgN/O+PuAZj3PbR2mkkJjYX4jNPlxvj8uTStaVPhAwfR9Spdx jenkins@cz8133
+
+disable_root: false
+chpasswd:
+  list: |
+    root:r00tme
+    jenkins:qalab
+  expire: False
+
+output:
+  all: '| tee -a /var/log/cloud-init-output.log /dev/tty0'
+
+runcmd:
+  # Create swap
+  - fallocate -l 16G /swapfile
+  - chmod 600 /swapfile
+  - mkswap /swapfile
+  - swapon /swapfile
+  - echo "/swapfile   none    swap    defaults   0   0" >> /etc/fstab
+
+  # Enable root access
+  - sed -i -e '/^PermitRootLogin/s/^.*$/PermitRootLogin yes/' /etc/ssh/sshd_config
+  - service sshd restart
+
+write_files:
+  - path: /etc/default/grub.d/97-enable-grub-menu.cfg
+    content: |
+      GRUB_RECORDFAIL_TIMEOUT=30
+      GRUB_TIMEOUT=3
+      GRUB_TIMEOUT_STYLE=menu
+
+  - path: /etc/network/interfaces
+    content: |
+      auto ens3
+      iface ens3 inet dhcp
+
+  - path: /etc/bash_completion.d/fuel_devops30_activate
+    content: |
+      source /home/jenkins/fuel-devops30/bin/activate
+
+  - path: /etc/sysctl.d/99-fuel-devops.conf
+    content: |
+      net.bridge.bridge-nf-call-arptables = 0
+      net.bridge.bridge-nf-call-ip6tables = 0
+      net.bridge.bridge-nf-call-iptables = 0
+
+  - path: /etc/ssh/ssh_config
+    content: |
+      Host *
+          SendEnv LANG LC_*
+          HashKnownHosts yes
+          GSSAPIAuthentication yes
+          GSSAPIDelegateCredentials no
+          ServerAliveInterval 300
+          ServerAliveCountMax 10
+          StrictHostKeyChecking no
+          UserKnownHostsFile /dev/null
+
+  - path: /etc/sudoers.d/99-mirantis
+    content: |
+      %mirantis ALL=(ALL) NOPASSWD:ALL
diff --git a/tcp_tests/templates/_packer/foundation/packer.json b/tcp_tests/templates/_packer/foundation/packer.json
new file mode 100644
index 0000000..452fdef
--- /dev/null
+++ b/tcp_tests/templates/_packer/foundation/packer.json
@@ -0,0 +1,64 @@
+{
+  "variables": {
+    "vm_name": "{{ env `IMAGE_NAME` }}.qcow2",
+    "image_path": "tmp/{{ env `IMAGE_NAME` }}",
+    "base_image_url": "{{ env `BASE_IMAGE_URL` }}",
+    "base_image_md5": "{{ env `BASE_IMAGE_MD5` }}",
+    "base_image_path": "base_image.qcow2",
+    "ssh_username": "root",
+    "ssh_password": "r00tme",
+    "ssh_wait_timeout": "30m",
+    "disk_size": "51200",
+    "boot_wait": "120s"
+  },
+
+  "builders":
+  [
+    {
+      "type": "qemu",
+      "qemuargs": [
+        [ "-m", "1024M" ],
+        [ "-cdrom", "tmp/config-drive.iso" ],
+        ["-device", "virtio-net,netdev=user.0"],
+        ["-object","rng-random,id=objrng0,filename=/dev/urandom"],
+        ["-device", "virtio-rng-pci,rng=objrng0,id=rng0,bus=pci.0,addr=0x10" ]
+      ],
+      "vm_name": "{{ user `vm_name` }}",
+      "output_directory": "{{ user `image_path` }}",
+      "format": "qcow2",
+      "iso_url": "{{ user `base_image_url` }}",
+      "iso_checksum": "{{ user `base_image_md5` }}",
+      "iso_checksum_type": "md5",
+      "iso_target_path": "{{ user `base_image_path`}}",
+      "disk_image": true,
+      "disk_compression": true,
+      "accelerator": "kvm",
+      "disk_size": "{{ user `disk_size`}}",
+      "headless": true,
+      "ssh_username": "{{ user `ssh_username` }}",
+      "ssh_password": "{{ user `ssh_password` }}",
+      "ssh_wait_timeout": "{{ user `ssh_wait_timeout` }}",
+      "ssh_host_port_min": 7000,
+      "ssh_host_port_max": 7050,
+      "shutdown_command": "shutdown -P now",
+      "boot_wait": "{{ user `boot_wait` }}"
+    }
+  ],
+
+  "provisioners": [
+    {
+      "type": "shell",
+      "environment_vars": [
+        "DEBIAN_FRONTEND=noninteractive"
+      ],
+      "execute_command": "echo '{{ user `ssh_password` }}' | {{.Vars}} sudo -S -E bash -x '{{.Path}}'",
+      "scripts": [
+        "tcp_tests/templates/_packer/scripts/ubuntu_packets.sh",
+        "tcp_tests/templates/_packer/scripts/ubuntu_ldap.sh",
+        "tcp_tests/templates/_packer/scripts/jenkins_virtualenvs.sh",
+        "tcp_tests/templates/_packer/scripts/ubuntu_cleanup.sh",
+        "tcp_tests/templates/_packer/scripts/zerodisk.sh"
+      ]
+    }
+  ]
+}
diff --git a/tcp_tests/templates/_packer/scripts/jenkins_virtualenvs.sh b/tcp_tests/templates/_packer/scripts/jenkins_virtualenvs.sh
new file mode 100644
index 0000000..eb83ab4
--- /dev/null
+++ b/tcp_tests/templates/_packer/scripts/jenkins_virtualenvs.sh
@@ -0,0 +1,23 @@
+#!/bin/bash -xe
+
+DEVOPS_VENV_PATH=/home/jenkins/fuel-devops30
+REPORT_VENV_PATH=/home/jenkins/venv_testrail_reporter
+
+if [ ! -d ${DEVOPS_VENV_PATH} ]; then
+    virtualenv ${DEVOPS_VENV_PATH}
+fi
+if [ ! -d ${REPORT_VENV_PATH} ]; then
+    virtualenv ${REPORT_VENV_PATH}
+fi
+
+# Install tcp-qa requirements
+. ${DEVOPS_VENV_PATH}/bin/activate
+pip install -r https://raw.githubusercontent.com/Mirantis/tcp-qa/master/tcp_tests/requirements.txt
+pip install psycopg2  # workaround for setup with PostgreSQL , to keep requirements.txt for Sqlite3 only
+
+# Install xunit2testrail
+. ${REPORT_VENV_PATH}/bin/activate
+#pip install xunit2testrail -U
+pip install git+https://github.com/dis-xcom/testrail_reporter -U  # Removed accessing to an unexisting pastebin on srv62
+
+chown -R jenkins:jenkins /home/jenkins/
diff --git a/tcp_tests/templates/_packer/scripts/ubuntu_cleanup.sh b/tcp_tests/templates/_packer/scripts/ubuntu_cleanup.sh
new file mode 100644
index 0000000..63a7586
--- /dev/null
+++ b/tcp_tests/templates/_packer/scripts/ubuntu_cleanup.sh
@@ -0,0 +1,70 @@
+#!/bin/bash -xe
+
+apt-get -y remove --purge unattended-upgrades || true
+apt-get -y autoremove --purge
+apt-get -y clean
+
+rm -rf /var/lib/apt/lists/* || true
+rm -rv /etc/apt/sources.list.d/* || true
+rm -rv /etc/apt/preferences.d/* || true
+echo > /etc/apt/sources.list  || true
+rm -vf /usr/sbin/policy-rc.d || true
+
+echo "cleaning up hostname"
+sed -i "/.*ubuntu.*/d" /etc/hosts
+sed -i "/.*salt.*/d" /etc/hosts
+
+echo "cleaning up guest additions"
+rm -rf VBoxGuestAdditions_*.iso VBoxGuestAdditions_*.iso.? || true
+
+echo "cleaning up dhcp leases"
+rm -rf /var/lib/dhcp/* || true
+rm -rfv /var/lib/ntp/ntp.conf.dhcp || true
+
+echo "cleaning up udev rules"
+rm -fv /etc/udev/rules.d/70-persistent-net.rules || true
+rm -rf /dev/.udev/ || true
+rm -fv /lib/udev/rules.d/75-persistent-net-generator.rules || true
+
+echo "cleaning up minion_id for salt"
+rm -vf /etc/salt/minion_id || true
+
+echo "cleaning up resolvconf"
+sed -i '/172\.18\.208\.44/d' /etc/resolvconf/resolv.conf.d/base
+
+echo "cleaning up /var/cache/{apt,salt}/*"
+rm -rf /var/cache/{apt,salt}/* || true
+
+rm -rf /root/.cache || true
+rm -rf /root/.ssh/known_hosts || true
+
+# Remove flags
+rm -v /done_ubuntu_base || true
+rm -v /done_ubuntu_salt_bootstrap || true
+
+# Force cleanup cloud-init data, if it was
+if [[ -d '/var/lib/cloud/' ]] ; then
+  rm -rf /var/lib/cloud/* || true
+  cloud-init clean || true
+  echo > /var/log/cloud-init-output.log || true
+  echo > /var/log/cloud-init.log || true
+fi
+
+cat << EOF > /etc/network/interfaces
+# This file describes the network interfaces available on your system
+# and how to activate them. For more information, see interfaces(5).
+
+# The loopback network interface
+auto lo
+iface lo inet loopback
+
+# Source interfaces
+# Please check /etc/network/interfaces.d before changing this file
+# as interfaces may have been defined in /etc/network/interfaces.d
+# See LP: #1262951
+source /etc/network/interfaces.d/*.cfg
+EOF
+
+# Clear\drop cache's
+sync
+echo 3 > /proc/sys/vm/drop_caches
diff --git a/tcp_tests/templates/_packer/scripts/ubuntu_ldap.sh b/tcp_tests/templates/_packer/scripts/ubuntu_ldap.sh
new file mode 100644
index 0000000..4c400fb
--- /dev/null
+++ b/tcp_tests/templates/_packer/scripts/ubuntu_ldap.sh
@@ -0,0 +1,56 @@
+#!/bin/bash -xe
+
+apt-get update
+apt-get install -y ldap-auth-client nscd ldap-utils
+
+auth-client-config -t nss -p lac_ldap
+
+sed -i 's$^#bind_policy hard$bind_policy soft$' /etc/ldap.conf
+sed -i 's$base dc=.*$base dc=mirantis,dc=net$' /etc/ldap.conf
+sed -i 's$uri ldap.*$uri ldap://ldap-bud.bud.mirantis.net/$' /etc/ldap.conf
+sed -i 's$^\(rootbinddn.*\)$#\1$' /etc/ldap.conf
+
+cat << 'EOF' >> /etc/ldap/ldap.conf
+BASE    dc=mirantis,dc=net
+URI     ldap://ldap-bud.bud.mirantis.net/
+EOF
+
+cat << 'EOF' > /usr/share/pam-configs/my_mkhomedir
+Name: activate mkhomedir
+Default: yes
+Priority: 900
+Session-Type: Additional
+Session:
+        required                        pam_mkhomedir.so umask=0022 skel=/etc/skel
+EOF
+
+cat << 'EOF' >> /etc/security/group.conf
+*;*;*;Al0000-2400;audio,cdrom,dialout,floppy,kvm,libvirtd
+EOF
+
+cat << 'EOF' > /usr/share/pam-configs/my_groups
+Name: activate /etc/security/group.conf
+Default: yes
+Priority: 900
+Auth-Type: Primary
+Auth:
+        required                        pam_group.so use_first_pass
+EOF
+
+cat << 'EOF' > /usr/local/sbin/ssh-ldap-keyauth
+#!/bin/bash
+
+/usr/bin/ldapsearch -x '(&(objectClass=posixAccount)(uid='"$1"'))' sshPublicKey | sed -n '/^ /{H;d};/sshPublicKey:/x;$g;s/\n *//g;s/sshPublicKey: //gp'
+EOF
+
+cat << 'EOF' >> /etc/ssh/sshd_config
+
+AuthorizedKeysCommand /usr/local/sbin/ssh-ldap-keyauth
+AuthorizedKeysCommandUser nobody
+EOF
+
+chmod +x /usr/local/sbin/ssh-ldap-keyauth
+DEBIAN_FRONTEND=noninteractive pam-auth-update
+
+#systemctl restart nscd.service;
+#systemctl restart sshd.service;
diff --git a/tcp_tests/templates/_packer/scripts/ubuntu_packets.sh b/tcp_tests/templates/_packer/scripts/ubuntu_packets.sh
new file mode 100644
index 0000000..883f620
--- /dev/null
+++ b/tcp_tests/templates/_packer/scripts/ubuntu_packets.sh
@@ -0,0 +1,17 @@
+#!/bin/bash -xe
+
+apt-get update
+
+# for Jenkins agent
+apt-get install -y openjdk-8-jre-headless
+# for fuel-devops and tcp-qa
+apt-get install -y libyaml-dev libffi-dev libvirt-dev python-dev pkg-config vlan bridge-utils python-pip python3-pip virtualenv
+# additional tools
+apt-get install -y ebtables curl ethtool iputils-ping lsof strace tcpdump traceroute wget iptables htop \
+    git jq ntpdate tree mc byobu at pm-utils genisoimage iotop
+
+# ldap
+apt-get install -y ldap-auth-client nscd ldap-utils
+
+# update kernel
+apt-get install -y linux-generic-hwe-16.04
diff --git a/tcp_tests/templates/_packer/scripts/zerodisk.sh b/tcp_tests/templates/_packer/scripts/zerodisk.sh
new file mode 100644
index 0000000..159ae13
--- /dev/null
+++ b/tcp_tests/templates/_packer/scripts/zerodisk.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -x
+
+dd if=/dev/zero of=/EMPTY bs=1M || true
+rm -f /EMPTY
+
+sync
+echo 3 > /proc/sys/vm/drop_caches