Merge "deploy.sh: simple wrapper for fuel@opnfv deploy script" into stable/brahmaputra
authorAlexandru Avadanii <Alexandru.Avadanii@enea.com>
Sun, 8 May 2016 17:35:48 +0000 (17:35 +0000)
committerGerrit Code Review <gerrit@172.30.200.206>
Sun, 8 May 2016 17:35:48 +0000 (17:35 +0000)
16 files changed:
patches/opnfv-fuel/0010-deployment.py-stdout-not-consumed-when-deploying-cha.patch [new file with mode: 0644]
patches/opnfv-fuel/0011-common.py-catch-stderr-in-exec_cmd.patch [new file with mode: 0644]
patches/opnfv-fuel/0012-deploy.sh-do-not-expect-a-parameter-for-h.patch [new file with mode: 0644]
patches/opnfv-fuel/0013-VirtualFuel-Add-temp_dir-and-vm_name-attributes.patch [new file with mode: 0644]
patches/opnfv-fuel/0014-virtual_fuel-factor-out-image-creation-into-a-method.patch [new file with mode: 0644]
patches/opnfv-fuel/0015-virtual_fuel-initial-support-for-libvirt-volumes.patch [new file with mode: 0644]
patches/opnfv-fuel/0016-Remove-check-for-root.patch [new file with mode: 0644]
patches/opnfv-fuel/0017-virtual_fuel-make-vm_template-an-attibute.patch [new file with mode: 0644]
patches/opnfv-fuel/0018-virtual_fuel-add-XML-tree-as-attribute-of-VirtualFue.patch [new file with mode: 0644]
patches/opnfv-fuel/0019-transplant-Generate-extra-interfaces-config-file.patch [new file with mode: 0644]
patches/opnfv-fuel/0020-deploy.sh-no-need-to-set-umask-0000.patch [new file with mode: 0644]
patches/opnfv-fuel/0021-common.py-allow-specifying-number-of-attempts-in-exe.patch [new file with mode: 0644]
patches/opnfv-fuel/0022-ipmi_adapter-simplify-retry-if-command-fails.patch [new file with mode: 0644]
patches/opnfv-fuel/0023-deploy.py-add-multiple-bridges-support.patch [new file with mode: 0644]
patches/opnfv-fuel/0024-deploy.sh-allow-specifying-several-bridges.patch [new file with mode: 0644]
patches/opnfv-fuel/0025-Fuel-VM-for-the-Enea-Armband-lab.patch [new file with mode: 0644]

diff --git a/patches/opnfv-fuel/0010-deployment.py-stdout-not-consumed-when-deploying-cha.patch b/patches/opnfv-fuel/0010-deployment.py-stdout-not-consumed-when-deploying-cha.patch
new file mode 100644 (file)
index 0000000..584413e
--- /dev/null
@@ -0,0 +1,66 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] deployment.py: stdout not consumed when deploying changes
+
+During the automatic deployment, when the environment is ready to be
+deployed, the deploy.py script will spawn a shell process that will
+perform the command "fuel deploy-changes". The standard output of this
+process is then piped to a "tee" process, which redirects the output
+to the standard output of the shell process, and to a file named
+cloud.log. The file is monitored by the deploy script to find out the
+status of the deployment, and print it to the log file of the automatic
+deployment script, including percentages for each node being
+provisioned. However, the deploy script never consumes the standard
+output of the shell process. If the shell process produces enough
+output, its standard output buffer will fill up, thus making the tee
+process block trying to write to its standard output, and the cloud.log
+file will not be updated. At this point, the deploy process, which is
+monitoring cloud.log, will not detect any progress in the deployment,
+and eventually it will time out and assume the deployment failed,
+although it might have finished fine after that.
+
+The solution here is to remove the "tee" process from the shell command,
+and instead redirect standard output to the cloud.log file.
+Another solution would be to actually parse the standard output of the
+shell command from the deploy script itself, but that would require a
+bit more work, as reading a line at a time might block the script.
+
+Finally, with this patch the cloud.log file won't be deleted unless the
+shell process has already finished.
+
+Change-Id: I03a77be42d220b1606e48fc4ca35e22d73a6e583
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/cloud/deployment.py | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/deploy/cloud/deployment.py b/deploy/cloud/deployment.py
+index 306abf0..0127d2a 100644
+--- a/deploy/cloud/deployment.py
++++ b/deploy/cloud/deployment.py
+@@ -101,8 +101,8 @@ class Deployment(object):
+         LOG_FILE = 'cloud.log'
+         log('Starting deployment of environment %s' % self.env_id)
+-        run_proc('fuel --env %s deploy-changes | strings | tee %s'
+-                 % (self.env_id, LOG_FILE))
++        p = run_proc('fuel --env %s deploy-changes | strings > %s'
++                     % (self.env_id, LOG_FILE))
+         ready = False
+         for i in range(int(self.deploy_timeout)):
+@@ -119,7 +119,13 @@ class Deployment(object):
+                 break
+             else:
+                 time.sleep(SLEEP_TIME)
+-        delete(LOG_FILE)
++
++        p.poll()
++        if p.returncode == None:
++            log('The process deploying the changes has not yet finished.')
++            log('''The file %s won't be deleted''' % LOG_FILE)
++        else:
++            delete(LOG_FILE)
+         if ready:
+             log('Environment %s successfully deployed' % self.env_id)
diff --git a/patches/opnfv-fuel/0011-common.py-catch-stderr-in-exec_cmd.patch b/patches/opnfv-fuel/0011-common.py-catch-stderr-in-exec_cmd.patch
new file mode 100644 (file)
index 0000000..918a119
--- /dev/null
@@ -0,0 +1,40 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] common.py: catch stderr in exec_cmd
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/common.py | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/deploy/common.py b/deploy/common.py
+index 787a21a..41b4e27 100644
+--- a/deploy/common.py
++++ b/deploy/common.py
+@@ -38,20 +38,20 @@ LOG.addHandler(out_handler)
+ os.chmod(LOGFILE, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
+ def exec_cmd(cmd, check=True):
+-    nul_f = open(os.devnull, 'w')
+     process = subprocess.Popen(cmd,
+                                stdout=subprocess.PIPE,
+-                               stderr=nul_f,
++                               stderr=subprocess.PIPE,
+                                shell=True)
+-    nul_f.close()
+-    response = process.communicate()[0].strip()
++    (response, stderr) = process.communicate()
+     return_code = process.returncode
++    response = response.strip()
+     if check:
+         if return_code > 0:
++            stderr = stderr.strip()
+             print "Failed command: " + str(cmd)
+-            print "Command returned response: " + str(response)
++            print "Command returned response: " + str(stderr)
+             print "Command return code: " + str(return_code)
+-            raise Exception(response)
++            raise Exception(stderr)
+         else:
+             print "Command: " + str(cmd)
+             print str(response)
diff --git a/patches/opnfv-fuel/0012-deploy.sh-do-not-expect-a-parameter-for-h.patch b/patches/opnfv-fuel/0012-deploy.sh-do-not-expect-a-parameter-for-h.patch
new file mode 100644 (file)
index 0000000..f515aab
--- /dev/null
@@ -0,0 +1,47 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] deploy.sh: do not expect a parameter for -h
+
+If -h was given as a parameter to the script, it would report an error
+as it expected a parameter, and if it was called as the only parameter,
+it would run deploy.py as if "old style" parameters had been given, thus
+showing the usage for the python script, instead of the expected usage
+message for this script.
+
+Update the usage message to include -h.
+
+Change-Id: I0930936962c1cb479ec4409ff114cd60a386b276
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ ci/deploy.sh | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/ci/deploy.sh b/ci/deploy.sh
+index d83bba2..dc13f1c 100755
+--- a/ci/deploy.sh
++++ b/ci/deploy.sh
+@@ -40,6 +40,7 @@ OPTIONS:
+   -f  Deploy on existing Fuel master
+   -e  Do not launch environment deployment
+   -F  Do only create a Fuel master
++  -h  Print this message and exit
+   -H  No health check
+   -l  Lab-name
+   -p  Pod-name
+@@ -62,6 +63,7 @@ Input parameters to the build script is:
+ -f Deploy on existing Fuel master
+ -e Do not launch environment deployment
+ -F Do only create a Fuel master
++-h Print this message and exit
+ -H Do not run fuel built in health-check after successfull deployment
+ -l Lab name as defined in the configuration directory, e.g. lf
+ -p POD name as defined in the configuration directory, e.g. pod-1
+@@ -116,7 +118,7 @@ DRY_RUN=0
+ ############################################################################
+ # BEGIN of main
+ #
+-while getopts "b:B:dfFHl:p:s:S:i:h:e" OPTION
++while getopts "b:B:dfFHl:p:s:S:i:he" OPTION
+ do
+     case $OPTION in
+         b)
diff --git a/patches/opnfv-fuel/0013-VirtualFuel-Add-temp_dir-and-vm_name-attributes.patch b/patches/opnfv-fuel/0013-VirtualFuel-Add-temp_dir-and-vm_name-attributes.patch
new file mode 100644 (file)
index 0000000..83d6e29
--- /dev/null
@@ -0,0 +1,57 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] VirtualFuel: Add temp_dir and vm_name attributes
+
+These two variables are defined in one of the methods right now. They
+will be useful to other methods too, so we add them as attributes to the
+object here.
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/environments/virtual_fuel.py | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
+index cb3bc6c..966bb91 100644
+--- a/deploy/environments/virtual_fuel.py
++++ b/deploy/environments/virtual_fuel.py
+@@ -25,6 +25,12 @@ class VirtualFuel(ExecutionEnvironment):
+     def __init__(self, storage_dir, pxe_bridge, dha_file, root_dir):
+         super(VirtualFuel, self).__init__(storage_dir, dha_file, root_dir)
+         self.pxe_bridge = pxe_bridge
++        self.temp_dir = tempfile.mkdtemp()
++        self.vm_name = self.dha.get_node_property(self.fuel_node_id,
++                                                  'libvirtName')
++
++    def __del__(self):
++        delete(self.temp_dir)
+     def set_vm_nic(self, temp_vm_file):
+         with open(temp_vm_file) as f:
+@@ -46,23 +52,20 @@ class VirtualFuel(ExecutionEnvironment):
+             vm_xml.write(f, pretty_print=True, xml_declaration=True)
+     def create_vm(self):
+-        temp_dir = tempfile.mkdtemp()
+-        vm_name = self.dha.get_node_property(self.fuel_node_id, 'libvirtName')
+         vm_template = '%s/%s' % (self.root_dir,
+                                  self.dha.get_node_property(
+                                      self.fuel_node_id, 'libvirtTemplate'))
+         check_file_exists(vm_template)
+-        disk_path = '%s/%s.raw' % (self.storage_dir, vm_name)
++        disk_path = '%s/%s.raw' % (self.storage_dir, self.vm_name)
+         disk_sizes = self.dha.get_disks()
+         disk_size = disk_sizes['fuel']
+         exec_cmd('qemu-img create -f qcow2 %s %s' % (disk_path, disk_size))
+-        temp_vm_file = '%s/%s' % (temp_dir, vm_name)
++        temp_vm_file = '%s/%s' % (self.temp_dir, self.vm_name)
+         exec_cmd('cp %s %s' % (vm_template, temp_vm_file))
+         self.set_vm_nic(temp_vm_file)
+         vm_definition_overwrite = self.dha.get_vm_definition('fuel')
+-        self.define_vm(vm_name, temp_vm_file, disk_path,
++        self.define_vm(self.vm_name, temp_vm_file, disk_path,
+                        vm_definition_overwrite)
+-        delete(temp_dir)
+     def setup_environment(self):
+         check_if_root()
diff --git a/patches/opnfv-fuel/0014-virtual_fuel-factor-out-image-creation-into-a-method.patch b/patches/opnfv-fuel/0014-virtual_fuel-factor-out-image-creation-into-a-method.patch
new file mode 100644 (file)
index 0000000..4e1f583
--- /dev/null
@@ -0,0 +1,35 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] virtual_fuel: factor out image creation into a method
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/environments/virtual_fuel.py | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
+index 966bb91..82c4e47 100644
+--- a/deploy/environments/virtual_fuel.py
++++ b/deploy/environments/virtual_fuel.py
+@@ -51,15 +51,20 @@ class VirtualFuel(ExecutionEnvironment):
+         with open(temp_vm_file, 'w') as f:
+             vm_xml.write(f, pretty_print=True, xml_declaration=True)
++    def create_image(self, disk_path, disk_size):
++        exec_cmd('qemu-img create -f qcow2 %s %s' % (disk_path, disk_size))
++
+     def create_vm(self):
+         vm_template = '%s/%s' % (self.root_dir,
+                                  self.dha.get_node_property(
+                                      self.fuel_node_id, 'libvirtTemplate'))
+         check_file_exists(vm_template)
++
+         disk_path = '%s/%s.raw' % (self.storage_dir, self.vm_name)
+         disk_sizes = self.dha.get_disks()
+         disk_size = disk_sizes['fuel']
+-        exec_cmd('qemu-img create -f qcow2 %s %s' % (disk_path, disk_size))
++        self.create_image(disk_path, disk_size)
++
+         temp_vm_file = '%s/%s' % (self.temp_dir, self.vm_name)
+         exec_cmd('cp %s %s' % (vm_template, temp_vm_file))
+         self.set_vm_nic(temp_vm_file)
diff --git a/patches/opnfv-fuel/0015-virtual_fuel-initial-support-for-libvirt-volumes.patch b/patches/opnfv-fuel/0015-virtual_fuel-initial-support-for-libvirt-volumes.patch
new file mode 100644 (file)
index 0000000..87266ef
--- /dev/null
@@ -0,0 +1,209 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] virtual_fuel: initial support for libvirt volumes
+
+This patch introduces the ability to create volumes on the libvirt host
+where the Fuel VM is being deployed. For now a default pool is used,
+but the idea is to allow this to be configured.
+
+Since all virsh commands honor LIBVIRT_DEFAULT_URI, we use this
+environment variable to detect wheter we should create a volume or not.
+The rationale being that this environment variable will only be set if
+the user wants to do the VM deployment on a remote libvirt host.
+
+All this could also be done using scp and a user directory on the host
+machine, but using pools allows us to take advantage of libvirt's
+policies and file permissions.
+
+CHANGE: before this patch, the file system image was named like the VM:
+vm_name.raw. This patch introduces a change and adds a timestamp suffix
+to the image: vm_name-timestamp.raw. This is so to avoid collisions with
+an image with the same name on the remote pool. It may also be useful to
+keep around old images for later testing, while the VM definition can
+likely be the same.
+
+FIXME: This patch will use a pool called "jenkins" in the libvirt
+server, and it will fail if it is not present. This is a requirement
+that should be amended in the future, and properly documented.
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/deploy.py                       |  5 +++
+ deploy/dha_adapters/libvirt_adapter.py | 28 +++++++++++++++++
+ deploy/environments/virtual_fuel.py    | 57 +++++++++++++++++++++++++++++-----
+ deploy/install_fuel_master.py          |  8 +++--
+ 4 files changed, 88 insertions(+), 10 deletions(-)
+
+diff --git a/deploy/deploy.py b/deploy/deploy.py
+index f86f2be..265e888 100755
+--- a/deploy/deploy.py
++++ b/deploy/deploy.py
+@@ -243,6 +243,11 @@ class AutoDeploy(object):
+ def check_bridge(pxe_bridge, dha_path):
++    # Assume that bridges on remote nodes exists, we could ssh but
++    # the remote user might not have a login shell.
++    if os.environ.get('LIBVIRT_DEFAULT_URI'):
++        return
++
+     with io.open(dha_path) as yaml_file:
+         dha_struct = yaml.load(yaml_file)
+     if dha_struct['adapter'] != 'libvirt':
+diff --git a/deploy/dha_adapters/libvirt_adapter.py b/deploy/dha_adapters/libvirt_adapter.py
+index 85913ac..8f3042c 100644
+--- a/deploy/dha_adapters/libvirt_adapter.py
++++ b/deploy/dha_adapters/libvirt_adapter.py
+@@ -11,6 +11,7 @@
+ from lxml import etree
+ from hardware_adapter import HardwareAdapter
+ import tempfile
++import os
+ from common import (
+     log,
+@@ -23,6 +24,13 @@ DEV = {'pxe': 'network',
+        'disk': 'hd',
+        'iso': 'cdrom'}
++vol_xml_template = '''<volume type='file'>
++  <name>%s</name>
++  <capacity unit='%s'>%s</capacity>
++  <target>
++    <format type='%s'/>
++  </target>
++</volume>'''
+ class LibvirtAdapter(HardwareAdapter):
+@@ -140,3 +148,23 @@ class LibvirtAdapter(HardwareAdapter):
+     def get_virt_net_conf_dir(self):
+         return self.dha_struct['virtNetConfDir']
++
++    def upload_iso(self, iso_file):
++        size = os.path.getsize(iso_file)
++        vol_name = os.path.basename(iso_file)
++        vol_xml = vol_xml_template % (vol_name, 'bytes', str(size), 'raw')
++        fd, fname = tempfile.mkstemp(text=True, suffix='deploy')
++        os.write(fd, vol_xml)
++        os.close(fd)
++
++        log(vol_xml)
++        pool = 'jenkins' # FIXME
++        exec_cmd('virsh vol-create --pool %s %s' % (pool, fname))
++        vol_path = exec_cmd('virsh vol-path --pool %s %s' % (pool, vol_name))
++
++        exec_cmd('virsh vol-upload %s %s' % (vol_path, iso_file),
++                 attempts=5, delay=10, verbose=True)
++
++        delete(fname)
++
++        return vol_path
+diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
+index 82c4e47..56d6f98 100644
+--- a/deploy/environments/virtual_fuel.py
++++ b/deploy/environments/virtual_fuel.py
+@@ -11,14 +11,33 @@
+ from lxml import etree
+ from execution_environment import ExecutionEnvironment
+ import tempfile
++import os
++import re
+ from common import (
+     exec_cmd,
+     check_file_exists,
+     check_if_root,
+     delete,
++    log,
+ )
++vol_xml_template = '''<volume type='file'>
++  <name>%s</name>
++  <capacity unit='%s'>%s</capacity>
++  <target>
++    <format type='%s'/>
++  </target>
++</volume>'''
++
++def get_size_and_unit(s):
++    p = re.compile('^(\d+)\s*(\D+)')
++    m = p.match(s)
++    if m == None:
++        return None, None
++    size = m.groups()[0]
++    unit = m.groups()[1]
++    return size, unit
+ class VirtualFuel(ExecutionEnvironment):
+@@ -51,19 +70,41 @@ class VirtualFuel(ExecutionEnvironment):
+         with open(temp_vm_file, 'w') as f:
+             vm_xml.write(f, pretty_print=True, xml_declaration=True)
++    def create_volume(self, pool, name, su, img_type='qcow2'):
++        log('Creating image using Libvirt volumes in pool %s, name: %s' %
++            (pool, name))
++        size, unit = get_size_and_unit(su)
++        if size == None:
++            err('Could not determine size and unit of %s' % s)
++
++        vol_xml = vol_xml_template % (name, unit, str(size), img_type)
++        fname = os.path.join(self.temp_dir, '%s_vol.xml' % name)
++        with file(fname, 'w') as f:
++            f.write(vol_xml)
++
++        exec_cmd('virsh vol-create --pool %s %s' % (pool, fname))
++        vol_path = exec_cmd('virsh vol-path --pool %s %s' % (pool, name))
++
++        delete(fname)
++
++        return vol_path
++
+     def create_image(self, disk_path, disk_size):
+-        exec_cmd('qemu-img create -f qcow2 %s %s' % (disk_path, disk_size))
++        if os.environ.get('LIBVIRT_DEFAULT_URI') == None:
++            exec_cmd('qemu-img create -f qcow2 %s %s' % (disk_path, disk_size))
++        else:
++            pool = 'jenkins' # FIXME
++            name = os.path.basename(disk_path)
++            disk_path = self.create_volume(pool, name, disk_size)
+-    def create_vm(self):
+-        vm_template = '%s/%s' % (self.root_dir,
+-                                 self.dha.get_node_property(
+-                                     self.fuel_node_id, 'libvirtTemplate'))
+-        check_file_exists(vm_template)
++        return disk_path
+-        disk_path = '%s/%s.raw' % (self.storage_dir, self.vm_name)
++    def create_vm(self):
++        stamp = time.strftime("%Y%m%d%H%M%S")
++        disk_path = '%s/%s-%s.raw' % (self.storage_dir, self.vm_name, stamp)
+         disk_sizes = self.dha.get_disks()
+         disk_size = disk_sizes['fuel']
+-        self.create_image(disk_path, disk_size)
++        disk_path = self.create_image(disk_path, disk_size)
+         temp_vm_file = '%s/%s' % (self.temp_dir, self.vm_name)
+         exec_cmd('cp %s %s' % (vm_template, temp_vm_file))
+diff --git a/deploy/install_fuel_master.py b/deploy/install_fuel_master.py
+index 4f6a052..1c1bf05 100644
+--- a/deploy/install_fuel_master.py
++++ b/deploy/install_fuel_master.py
+@@ -54,8 +54,12 @@ class InstallFuelMaster(object):
+         self.dha.node_power_off(self.fuel_node_id)
+-        log('Zero the MBR')
+-        self.dha.node_zero_mbr(self.fuel_node_id)
++        if os.environ.get('LIBVIRT_DEFAULT_URI'):
++            log('Upload ISO to pool')
++            self.iso_file = self.dha.upload_iso(self.iso_file)
++        else:
++            log('Zero the MBR')
++            self.dha.node_zero_mbr(self.fuel_node_id)
+         self.dha.node_set_boot_order(self.fuel_node_id, ['disk', 'iso'])
diff --git a/patches/opnfv-fuel/0016-Remove-check-for-root.patch b/patches/opnfv-fuel/0016-Remove-check-for-root.patch
new file mode 100644 (file)
index 0000000..4c24bb0
--- /dev/null
@@ -0,0 +1,79 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] Remove check for root
+
+---
+ ci/deploy.sh                        | 5 -----
+ deploy/deploy-config.py             | 1 -
+ deploy/deploy.py                    | 2 --
+ deploy/environments/virtual_fuel.py | 2 --
+ 4 files changed, 10 deletions(-)
+
+diff --git a/ci/deploy.sh b/ci/deploy.sh
+index dc13f1c..343d499 100755
+--- a/ci/deploy.sh
++++ b/ci/deploy.sh
+@@ -193,11 +193,6 @@ do
+     esac
+ done
+-if [[ $EUID -ne 0 ]]; then
+-    echo "This script must be run as root" 1>&2
+-    exit 1
+-fi
+-
+ if [ -z $BASE_CONFIG_URI ] || [ -z $TARGET_LAB ] || \
+    [ -z $TARGET_POD ] || [ -z $DEPLOY_SCENARIO ] || \
+    [ -z $ISO ]; then
+diff --git a/deploy/deploy-config.py b/deploy/deploy-config.py
+index 65d51b2..88a1111 100644
+--- a/deploy/deploy-config.py
++++ b/deploy/deploy-config.py
+@@ -40,7 +40,6 @@ from common import (
+     check_file_exists,
+     create_dir_if_not_exists,
+     delete,
+-    check_if_root,
+     ArgParser,
+ )
+diff --git a/deploy/deploy.py b/deploy/deploy.py
+index 265e888..ff4582a 100755
+--- a/deploy/deploy.py
++++ b/deploy/deploy.py
+@@ -32,7 +32,6 @@ from common import (
+     check_file_exists,
+     create_dir_if_not_exists,
+     delete,
+-    check_if_root,
+     ArgParser,
+ )
+@@ -230,7 +229,6 @@ class AutoDeploy(object):
+         return 0
+     def run(self):
+-        check_if_root()
+         if self.cleanup_only:
+             self.cleanup_execution_environment()
+         else:
+diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
+index 56d6f98..f07207f 100644
+--- a/deploy/environments/virtual_fuel.py
++++ b/deploy/environments/virtual_fuel.py
+@@ -17,7 +17,6 @@ import re
+ from common import (
+     exec_cmd,
+     check_file_exists,
+-    check_if_root,
+     delete,
+     log,
+ )
+@@ -114,7 +113,6 @@ class VirtualFuel(ExecutionEnvironment):
+                        vm_definition_overwrite)
+     def setup_environment(self):
+-        check_if_root()
+         self.cleanup_environment()
+         self.create_vm()
diff --git a/patches/opnfv-fuel/0017-virtual_fuel-make-vm_template-an-attibute.patch b/patches/opnfv-fuel/0017-virtual_fuel-make-vm_template-an-attibute.patch
new file mode 100644 (file)
index 0000000..db60202
--- /dev/null
@@ -0,0 +1,33 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] virtual_fuel: make vm_template an attibute
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/environments/virtual_fuel.py | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
+index f07207f..92a234c 100644
+--- a/deploy/environments/virtual_fuel.py
++++ b/deploy/environments/virtual_fuel.py
+@@ -46,6 +46,10 @@ class VirtualFuel(ExecutionEnvironment):
+         self.temp_dir = tempfile.mkdtemp()
+         self.vm_name = self.dha.get_node_property(self.fuel_node_id,
+                                                   'libvirtName')
++        self.vm_template = '%s/%s' % (self.root_dir,
++                                      self.dha.get_node_property(
++                                          self.fuel_node_id, 'libvirtTemplate'))
++        check_file_exists(self.vm_template)
+     def __del__(self):
+         delete(self.temp_dir)
+@@ -106,7 +110,7 @@ class VirtualFuel(ExecutionEnvironment):
+         disk_path = self.create_image(disk_path, disk_size)
+         temp_vm_file = '%s/%s' % (self.temp_dir, self.vm_name)
+-        exec_cmd('cp %s %s' % (vm_template, temp_vm_file))
++        exec_cmd('cp %s %s' % (self.vm_template, temp_vm_file))
+         self.set_vm_nic(temp_vm_file)
+         vm_definition_overwrite = self.dha.get_vm_definition('fuel')
+         self.define_vm(self.vm_name, temp_vm_file, disk_path,
diff --git a/patches/opnfv-fuel/0018-virtual_fuel-add-XML-tree-as-attribute-of-VirtualFue.patch b/patches/opnfv-fuel/0018-virtual_fuel-add-XML-tree-as-attribute-of-VirtualFue.patch
new file mode 100644 (file)
index 0000000..ebaad98
--- /dev/null
@@ -0,0 +1,102 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] virtual_fuel: add XML tree as attribute of VirtualFuel
+
+Now the VM XML definition tree is an attribute of the object, this way
+it can be used by all methods without having to re-read the file from
+the file.
+
+Methods added:
+update_vm_template_file: Flushes the contents of the in-memory XML
+    representation of the VM to the backing file.
+
+del_vm_nics: Deletes all interfaces from the VM
+
+add_vm_nic: Adds a new NIC to the VM, it now takes the name of the
+    bridge as a parameter.
+
+Add a function to flush the contents of the in-memory XML representation
+to the file update_vm_template_file
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/environments/virtual_fuel.py | 37 +++++++++++++++++++++++++------------
+ 1 file changed, 25 insertions(+), 12 deletions(-)
+
+diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
+index 92a234c..b68577e 100644
+--- a/deploy/environments/virtual_fuel.py
++++ b/deploy/environments/virtual_fuel.py
+@@ -13,6 +13,7 @@ from execution_environment import ExecutionEnvironment
+ import tempfile
+ import os
+ import re
++import time
+ from common import (
+     exec_cmd,
+@@ -50,28 +51,38 @@ class VirtualFuel(ExecutionEnvironment):
+                                       self.dha.get_node_property(
+                                           self.fuel_node_id, 'libvirtTemplate'))
+         check_file_exists(self.vm_template)
++        with open(self.vm_template) as f:
++            self.vm_xml = etree.parse(f)
++
++        self.temp_vm_file = '%s/%s' % (self.temp_dir, self.vm_name)
++        self.update_vm_template_file()
+     def __del__(self):
+         delete(self.temp_dir)
+-    def set_vm_nic(self, temp_vm_file):
+-        with open(temp_vm_file) as f:
+-            vm_xml = etree.parse(f)
+-        interfaces = vm_xml.xpath('/domain/devices/interface')
++    def update_vm_template_file(self):
++        with open(self.temp_vm_file, "wc") as f:
++            self.vm_xml.write(f, pretty_print=True, xml_declaration=True)
++
++    def del_vm_nics(self):
++        interfaces = self.vm_xml.xpath('/domain/devices/interface')
+         for interface in interfaces:
+             interface.getparent().remove(interface)
++
++    def add_vm_nic(self, bridge):
+         interface = etree.Element('interface')
+         interface.set('type', 'bridge')
+         source = etree.SubElement(interface, 'source')
+-        source.set('bridge', self.pxe_bridge)
++        source.set('bridge', bridge)
+         model = etree.SubElement(interface, 'model')
+         model.set('type', 'virtio')
+-        devices = vm_xml.xpath('/domain/devices')
++
++        devices = self.vm_xml.xpath('/domain/devices')
+         if devices:
+             device = devices[0]
+             device.append(interface)
+-        with open(temp_vm_file, 'w') as f:
+-            vm_xml.write(f, pretty_print=True, xml_declaration=True)
++        else:
++            err('No devices!')
+     def create_volume(self, pool, name, su, img_type='qcow2'):
+         log('Creating image using Libvirt volumes in pool %s, name: %s' %
+@@ -109,11 +120,13 @@ class VirtualFuel(ExecutionEnvironment):
+         disk_size = disk_sizes['fuel']
+         disk_path = self.create_image(disk_path, disk_size)
+-        temp_vm_file = '%s/%s' % (self.temp_dir, self.vm_name)
+-        exec_cmd('cp %s %s' % (self.vm_template, temp_vm_file))
+-        self.set_vm_nic(temp_vm_file)
++        self.del_vm_nics()
++        self.add_vm_nic(self.pxe_bridge)
++        self.update_vm_template_file()
++
+         vm_definition_overwrite = self.dha.get_vm_definition('fuel')
+-        self.define_vm(self.vm_name, temp_vm_file, disk_path,
++
++        self.define_vm(self.vm_name, self.temp_vm_file, disk_path,
+                        vm_definition_overwrite)
+     def setup_environment(self):
diff --git a/patches/opnfv-fuel/0019-transplant-Generate-extra-interfaces-config-file.patch b/patches/opnfv-fuel/0019-transplant-Generate-extra-interfaces-config-file.patch
new file mode 100644 (file)
index 0000000..b6a351e
--- /dev/null
@@ -0,0 +1,107 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 17:58:56 +0200
+Subject: [PATCH] transplant: Generate extra interfaces config file
+
+The DEA override may contain a IFCGF_<interface> section in its 'fuel:'
+section, containing the necessary keys to produce a ifcfg-<interface>
+file, like in this example:
+
+fuel:
+   IFCFG_ETH1:
+     device: eth1
+     ipaddress: 10.0.1.10
+     netmask: 255.255.255.0
+     gateway: 10.0.1.254
+
+FIXME: In order for Network Manager to use the newly added interfaces
+for outgoing traffic and honor their GATEWAY setting (e.g. if we just
+added one public interface), the default route on admin iface (most of
+the time called eth0) should be disabled. For now, we assume the admin
+interface is always "eth0".
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+Signed-off-by: Alexandu Avadanii <alexandru.avadanii@enea.com>
+---
+ deploy/transplant_fuel_settings.py | 37 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 37 insertions(+)
+
+diff --git a/deploy/transplant_fuel_settings.py b/deploy/transplant_fuel_settings.py
+index e57a4fb..9a65cf6 100644
+--- a/deploy/transplant_fuel_settings.py
++++ b/deploy/transplant_fuel_settings.py
+@@ -11,10 +11,14 @@
+ import sys
+ import io
+ import yaml
++import re
++import os
+ from dea import DeploymentEnvironmentAdapter
+ from common import (
+     check_file_exists,
++    exec_cmd,
++    log,
+ )
+ ASTUTE_YAML = '/etc/fuel/astute.yaml'
+@@ -35,15 +39,45 @@ def parse_arguments():
+     check_file_exists(dea_file)
+     return dea_file
++def write_ifcfg_file(key, fuel_conf):
++    config = ('BOOTPROTO=none\n'
++              'ONBOOT=yes\n'
++              'TYPE=Ethernet\n'
++              'NM_CONTROLLED=yes\n')
++    for skey in ('ipaddress', 'device', 'netmask', 'gateway'):
++        if not fuel_conf[key].get(skey):
++            log('Warning: missing key %s for %s' % (skey, key))
++            config += '%s=\n' % skey.upper()
++        elif skey == 'ipaddress':
++            config += 'IPADDR=%s\n' % fuel_conf[key][skey]
++        else:
++            config += '%s=%s\n' % (skey.upper(), fuel_conf[key][skey])
++
++    fname = os.path.join('/etc/sysconfig/network-scripts/',
++                         key.lower().replace('_','-'))
++    with open(fname, 'wc') as f:
++        f.write(config)
+ def transplant(dea, astute):
+     fuel_conf = dea.get_fuel_config()
++    require_network_restart = False
+     for key in fuel_conf.iterkeys():
+         if key == 'ADMIN_NETWORK':
+             for skey in fuel_conf[key].iterkeys():
+                 astute[key][skey] = fuel_conf[key][skey]
++        elif re.match('^IFCFG', key):
++            log('Adding interface configuration for: %s' % key.lower())
++            require_network_restart = True
++            write_ifcfg_file(key, fuel_conf)
++            if astute.has_key(key):
++                astute.pop(key, None)
+         else:
+             astute[key] = fuel_conf[key]
++    if require_network_restart:
++        admin_ifcfg = '/etc/sysconfig/network-scripts/ifcfg-eth0'
++        exec_cmd('echo "DEFROUTE=no" >> %s' % admin_ifcfg)
++        log('At least one interface was reconfigured, restart network manager')
++        exec_cmd('systemctl restart network')
+     return astute
+@@ -51,11 +85,14 @@ def main():
+     dea_file = parse_arguments()
+     check_file_exists(ASTUTE_YAML)
+     dea = DeploymentEnvironmentAdapter(dea_file)
++    log('Reading astute file %s' % ASTUTE_YAML)
+     with io.open(ASTUTE_YAML) as stream:
+         astute = yaml.load(stream)
++    log('Initiating transplant')
+     transplant(dea, astute)
+     with io.open(ASTUTE_YAML, 'w') as stream:
+         yaml.dump(astute, stream, default_flow_style=False)
++    log('Transplant done')
+ if __name__ == '__main__':
diff --git a/patches/opnfv-fuel/0020-deploy.sh-no-need-to-set-umask-0000.patch b/patches/opnfv-fuel/0020-deploy.sh-no-need-to-set-umask-0000.patch
new file mode 100644 (file)
index 0000000..241f307
--- /dev/null
@@ -0,0 +1,33 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Fri, 6 May 2016 03:07:40 +0200
+Subject: [PATCH] deploy.sh: no need to set umask 0000
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ ci/deploy.sh | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/ci/deploy.sh b/ci/deploy.sh
+index 343d499..34ecc57 100755
+--- a/ci/deploy.sh
++++ b/ci/deploy.sh
+@@ -76,9 +76,6 @@ Input parameters to the build script is:
+ -i .iso image to be deployed (needs to be provided in a URI
+    style, it can be a local resource: file:// or a remote resource http(s)://)
+-NOTE: Root priviledges are needed for this script to run
+-
+-
+ Examples:
+ sudo `basename $0` -b file:///home/jenkins/lab-config -l lf -p pod1 -s ha_odl-l3_heat_ceilometer -i file:///home/jenkins/myiso.iso
+ EOF
+@@ -207,9 +204,6 @@ fi
+ # Enable the automatic exit trap
+ trap do_exit SIGINT SIGTERM EXIT
+-# Set no restrictive umask so that Jenkins can removeeee any residuals
+-umask 0000
+-
+ clean
+ pushd ${DEPLOY_DIR} > /dev/null
diff --git a/patches/opnfv-fuel/0021-common.py-allow-specifying-number-of-attempts-in-exe.patch b/patches/opnfv-fuel/0021-common.py-allow-specifying-number-of-attempts-in-exe.patch
new file mode 100644 (file)
index 0000000..d799723
--- /dev/null
@@ -0,0 +1,70 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Fri, 6 May 2016 03:28:26 +0200
+Subject: [PATCH] common.py: allow specifying number of attempts in exec_cmd
+
+Some commands executed by exec_cmd may fail because of a temporary
+cause, and it may be desirable to retry the same command several times
+until it succeeds. One example of this are the ipmitool commands, which
+may fail temorarily on some targets if they get too many requests
+simultaneously.
+
+In this patch two new optional parameters are introduced to the function
+signature, which do not break backward compatibility:
+  attempts: which indicates how many times the command should be run if
+            it returns a non-zero value*, and defaults to 1 (as today).
+  delay:    which indicates the delay in seconds between attempts, and
+            defaults to 5 seconds.
+  verbose:  It will print the remaining attempts left for the current
+            command if set to True.
+
+* It may be desirable to add yet another parameter to indicate what
+  return value should be considered an error, but zero for now seems a
+  reasonable default
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/common.py | 24 +++++++++++++++++-------
+ 1 file changed, 17 insertions(+), 7 deletions(-)
+
+diff --git a/deploy/common.py b/deploy/common.py
+index 41b4e27..3cd3e0e 100644
+--- a/deploy/common.py
++++ b/deploy/common.py
+@@ -16,6 +16,7 @@ import argparse
+ import shutil
+ import stat
+ import errno
++import time
+ N = {'id': 0, 'status': 1, 'name': 2, 'cluster': 3, 'ip': 4, 'mac': 5,
+      'roles': 6, 'pending_roles': 7, 'online': 8, 'group_id': 9}
+@@ -37,13 +38,22 @@ out_handler.setFormatter(formatter)
+ LOG.addHandler(out_handler)
+ os.chmod(LOGFILE, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
+-def exec_cmd(cmd, check=True):
+-    process = subprocess.Popen(cmd,
+-                               stdout=subprocess.PIPE,
+-                               stderr=subprocess.PIPE,
+-                               shell=True)
+-    (response, stderr) = process.communicate()
+-    return_code = process.returncode
++def exec_cmd(cmd, check=True, attempts=1, delay=5, verbose=False):
++    # a negative value means forever
++    while attempts != 0:
++        attempts = attempts - 1
++        process = subprocess.Popen(cmd,
++                                   stdout=subprocess.PIPE,
++                                   stderr=subprocess.PIPE,
++                                   shell=True)
++        (response, stderr) = process.communicate()
++        return_code = process.returncode
++        if return_code == 0 or attempts == 0:
++            break
++        time.sleep(delay)
++        if verbose:
++            log('%d attempts left: %s' % (attempts, cmd))
++
+     response = response.strip()
+     if check:
+         if return_code > 0:
diff --git a/patches/opnfv-fuel/0022-ipmi_adapter-simplify-retry-if-command-fails.patch b/patches/opnfv-fuel/0022-ipmi_adapter-simplify-retry-if-command-fails.patch
new file mode 100644 (file)
index 0000000..c1617f0
--- /dev/null
@@ -0,0 +1,171 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Fri, 6 May 2016 12:09:58 +0200
+Subject: [PATCH] ipmi_adapter: simplify, retry if command fails
+
+The method get_node_state has been added to the The IpmiAdapter class.
+
+In addition, now the power on/off methods will try several times to
+perform their IPMI command before giving up, instead of bailing out at
+the first error.
+
+After the power on/off command is completed, the method will wait until
+the node is in the desired state.
+
+FIXME: a command could potentially take several minutes if the defaults
+are used; each IPMI command can take 1 minutes, and there can be three
+commands issued per operation, one of them may be retried 20 times with
+the current defaults. Ideally we would use eventlet or something alike
+to allow each command a limited time to execute:
+    with eventlet.timeout.Timeout(seconds) as t:
+        power_on/off_command
+
+FIXME: There is a potential dead-lock situation by issuing the command
+and then checking the status, as someone could have intervened in
+between the two commands.
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/dha_adapters/ipmi_adapter.py | 101 +++++++++++++++---------------------
+ 1 file changed, 42 insertions(+), 59 deletions(-)
+
+diff --git a/deploy/dha_adapters/ipmi_adapter.py b/deploy/dha_adapters/ipmi_adapter.py
+index 8fda4f9..283bd57 100644
+--- a/deploy/dha_adapters/ipmi_adapter.py
++++ b/deploy/dha_adapters/ipmi_adapter.py
+@@ -1,5 +1,6 @@
+ ###############################################################################
+ # Copyright (c) 2015 Ericsson AB and others.
++#           (c) 2016 Enea Software AB
+ # szilard.cserey@ericsson.com
+ # All rights reserved. This program and the accompanying materials
+ # are made available under the terms of the Apache License, Version 2.0
+@@ -20,8 +21,10 @@ from common import (
+ class IpmiAdapter(HardwareAdapter):
+-    def __init__(self, yaml_path):
++    def __init__(self, yaml_path, attempts=20, delay=3):
+         super(IpmiAdapter, self).__init__(yaml_path)
++        self.attempts = attempts
++        self.delay = delay
+     def get_access_info(self, node_id):
+         ip = self.get_node_property(node_id, 'ipmiIp')
+@@ -40,69 +43,46 @@ class IpmiAdapter(HardwareAdapter):
+         mac_list.append(self.get_node_property(node_id, 'pxeMac').lower())
+         return mac_list
++    def node_get_state(self, node_id):
++        state = exec_cmd('%s chassis power status' % self.ipmi_cmd(node_id),
++                         attempts=self.attempts, delay=self.delay,
++                         verbose=True)
++        return state
++
++    def __node_power_cmd__(self, node_id, cmd):
++        expected = 'Chassis Power is %s' % cmd
++        if self.node_get_state(node_id) == expected:
++            return
++
++        pow_cmd = '%s chassis power %s' % (self.ipmi_cmd(node_id), cmd)
++        exec_cmd(pow_cmd, attempts=self.attempts, delay=self.delay,
++                 verbose=True)
++
++        attempts = self.attempts
++        while attempts:
++            state = self.node_get_state(node_id)
++            attempts -= 1
++            if state == expected:
++                return
++            elif attempts != 0:
++                # reinforce our will, but allow the command to fail,
++                # we know our message got across once already...
++                exec_cmd(pow_cmd, check=False)
++
++        err('Could not set chassis %s for node %s' % (cmd, node_id))
++
+     def node_power_on(self, node_id):
+-        WAIT_LOOP = 200
+-        SLEEP_TIME = 3
+         log('Power ON Node %s' % node_id)
+-        cmd_prefix = self.ipmi_cmd(node_id)
+-        state = exec_cmd('%s chassis power status' % cmd_prefix)
+-        if state == 'Chassis Power is off':
+-            exec_cmd('%s chassis power on' % cmd_prefix)
+-            done = False
+-            for i in range(WAIT_LOOP):
+-                state, _ = exec_cmd('%s chassis power status' % cmd_prefix,
+-                                    False)
+-                if state == 'Chassis Power is on':
+-                    done = True
+-                    break
+-                else:
+-                    time.sleep(SLEEP_TIME)
+-            if not done:
+-                err('Could Not Power ON Node %s' % node_id)
++        self.__node_power_cmd__(node_id, 'on')
+     def node_power_off(self, node_id):
+-        WAIT_LOOP = 200
+-        SLEEP_TIME = 3
+         log('Power OFF Node %s' % node_id)
+-        cmd_prefix = self.ipmi_cmd(node_id)
+-        state = exec_cmd('%s chassis power status' % cmd_prefix)
+-        if state == 'Chassis Power is on':
+-            done = False
+-            exec_cmd('%s chassis power off' % cmd_prefix)
+-            for i in range(WAIT_LOOP):
+-                state, _ = exec_cmd('%s chassis power status' % cmd_prefix,
+-                                    False)
+-                if state == 'Chassis Power is off':
+-                    done = True
+-                    break
+-                else:
+-                    time.sleep(SLEEP_TIME)
+-            if not done:
+-                err('Could Not Power OFF Node %s' % node_id)
++        self.__node_power_cmd__(node_id, 'off')
+     def node_reset(self, node_id):
+-        WAIT_LOOP = 600
+         log('RESET Node %s' % node_id)
+-        cmd_prefix = self.ipmi_cmd(node_id)
+-        state = exec_cmd('%s chassis power status' % cmd_prefix)
+-        if state == 'Chassis Power is on':
+-            was_shut_off = False
+-            done = False
+-            exec_cmd('%s chassis power reset' % cmd_prefix)
+-            for i in range(WAIT_LOOP):
+-                state, _ = exec_cmd('%s chassis power status' % cmd_prefix,
+-                                    False)
+-                if state == 'Chassis Power is off':
+-                    was_shut_off = True
+-                elif state == 'Chassis Power is on' and was_shut_off:
+-                    done = True
+-                    break
+-                time.sleep(1)
+-            if not done:
+-                err('Could Not RESET Node %s' % node_id)
+-        else:
+-            err('Cannot RESET Node %s because it\'s not Active, state: %s'
+-                % (node_id, state))
++        cmd = '%s chassis power reset' % self.ipmi_cmd(node_id)
++        exec_cmd(cmd, attempts=self.attempts, delay=self.delay, verbose=True)
+     def node_set_boot_order(self, node_id, boot_order_list):
+         log('Set boot order %s on Node %s' % (boot_order_list, node_id))
+@@ -111,9 +91,12 @@ class IpmiAdapter(HardwareAdapter):
+         for dev in boot_order_list:
+             if dev == 'pxe':
+                 exec_cmd('%s chassis bootdev pxe options=persistent'
+-                         % cmd_prefix)
++                         % cmd_prefix, attempts=self.attempts, delay=self.delay,
++                         verbose=True)
+             elif dev == 'iso':
+-                exec_cmd('%s chassis bootdev cdrom' % cmd_prefix)
++                exec_cmd('%s chassis bootdev cdrom' % cmd_prefix,
++                         attempts=self.attempts, delay=self.delay, verbose=True)
+             elif dev == 'disk':
+                 exec_cmd('%s chassis bootdev disk options=persistent'
+-                         % cmd_prefix)
++                         % cmd_prefix, attempts=self.attempts, delay=self.delay,
++                         verbose=True)
diff --git a/patches/opnfv-fuel/0023-deploy.py-add-multiple-bridges-support.patch b/patches/opnfv-fuel/0023-deploy.py-add-multiple-bridges-support.patch
new file mode 100644 (file)
index 0000000..376a722
--- /dev/null
@@ -0,0 +1,69 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Fri, 6 May 2016 04:32:06 +0200
+Subject: [PATCH] deploy.py: add multiple bridges support
+
+Some Fuel VMs may need more than one network interface. To be able to do
+that, we now allow the user to specify the "-b" paramter (bridge)
+multiple times, creating a new NIC for each one of them.
+
+The NICs are created in the same order as they are given in the command
+line.
+
+There is no change in behavior from earlier versions, pxebr will still
+be the default bridge if none is specified.
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ deploy/deploy.py                    | 10 +++++++---
+ deploy/environments/virtual_fuel.py |  3 ++-
+ 2 files changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/deploy/deploy.py b/deploy/deploy.py
+index ff4582a..041ba2f 100755
+--- a/deploy/deploy.py
++++ b/deploy/deploy.py
+@@ -312,8 +312,8 @@ def parse_arguments():
+     parser.add_argument('-s', dest='storage_dir', action='store',
+                         default='%s/images' % CWD,
+                         help='Storage Directory [default: images]')
+-    parser.add_argument('-b', dest='pxe_bridge', action='store',
+-                        default='pxebr',
++    parser.add_argument('-b', dest='pxe_bridge', action='append',
++                        default=[],
+                         help='Linux Bridge for booting up the Fuel Master VM '
+                              '[default: pxebr]')
+     parser.add_argument('-p', dest='fuel_plugins_dir', action='store',
+@@ -332,6 +332,9 @@ def parse_arguments():
+     args = parser.parse_args()
+     log(args)
++    if not args.pxe_bridge:
++        args.pxe_bridge = ['pxebr']
++
+     check_file_exists(args.dha_file)
+     if not args.cleanup_only:
+@@ -343,7 +346,8 @@ def parse_arguments():
+         check_file_exists(args.iso_file)
+         log('Using image directory: %s' % args.storage_dir)
+         create_dir_if_not_exists(args.storage_dir)
+-        check_bridge(args.pxe_bridge, args.dha_file)
++        for bridge in args.pxe_bridge:
++            check_bridge(bridge, args.dha_file)
+     kwargs = {'no_fuel': args.no_fuel, 'fuel_only': args.fuel_only,
+               'no_health_check': args.no_health_check,
+diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
+index b68577e..6b673d0 100644
+--- a/deploy/environments/virtual_fuel.py
++++ b/deploy/environments/virtual_fuel.py
+@@ -121,7 +121,8 @@ class VirtualFuel(ExecutionEnvironment):
+         disk_path = self.create_image(disk_path, disk_size)
+         self.del_vm_nics()
+-        self.add_vm_nic(self.pxe_bridge)
++        for bridge in self.pxe_bridge:
++            self.add_vm_nic(bridge)
+         self.update_vm_template_file()
+         vm_definition_overwrite = self.dha.get_vm_definition('fuel')
diff --git a/patches/opnfv-fuel/0024-deploy.sh-allow-specifying-several-bridges.patch b/patches/opnfv-fuel/0024-deploy.sh-allow-specifying-several-bridges.patch
new file mode 100644 (file)
index 0000000..b10effe
--- /dev/null
@@ -0,0 +1,47 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Fri, 6 May 2016 04:39:44 +0200
+Subject: [PATCH] deploy.sh: allow specifying several bridges
+
+It might be desirable to add several bridges to the fuel VM, so we let
+the user specify -B more than once, and honor that when calling
+deploy.py. We also make it possible to specify a comma separated list of
+bridges, as in: -B br1,br2, for convenience for the Jenkins jobs.
+
+There is a change in behavior from the previous version, and that is
+that it may call the deploy.py python script with more than one instance
+of the "-b" parameter.
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ ci/deploy.sh | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/ci/deploy.sh b/ci/deploy.sh
+index 34ecc57..c9b836b 100755
+--- a/ci/deploy.sh
++++ b/ci/deploy.sh
+@@ -57,7 +57,10 @@ and provides a fairly simple mechanism to execute a deployment.
+ Input parameters to the build script is:
+ -b Base URI to the configuration directory (needs to be provided in a URI
+    style, it can be a local resource: file:// or a remote resource http(s)://)
+--B PXE Bridge for booting of Fuel master, default is pxebr
++-B PXE Bridge for booting of Fuel master. It can be specified several times,
++   or as a comma separated list of bridges, or both: -B br1 -B br2,br3
++   One NIC connected to each specified bridge will be created in the Fuel VM,
++   in the same order as provided in the command line. The default is pxebr.
+ -d Dry-run - Produces deploy config files (config/dea.yaml and
+    config/dha.yaml), but does not execute deploy
+ -f Deploy on existing Fuel master
+@@ -130,9 +133,9 @@ do
+             fi
+             ;;
+         B)
+-            if [[ ${OPTARG} ]]; then
+-                PXE_BRIDGE="-b ${OPTARG}"
+-            fi
++            for bridge in ${OPTARG//,/ }; do
++                PXE_BRIDGE+=" -b $bridge"
++            done
+             ;;
+         d)
+             DRY_RUN=1
diff --git a/patches/opnfv-fuel/0025-Fuel-VM-for-the-Enea-Armband-lab.patch b/patches/opnfv-fuel/0025-Fuel-VM-for-the-Enea-Armband-lab.patch
new file mode 100644 (file)
index 0000000..fbcd11d
--- /dev/null
@@ -0,0 +1,106 @@
+From: Josep Puigdemont <josep.puigdemont@enea.com>
+Date: Wed, 4 May 2016 14:27:23 +0200
+Subject: [PATCH] Fuel VM for the Enea Armband lab
+
+This is the initial VM description fit for Enea's Armband lab.
+
+Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
+---
+ .../hardware_environment/vms/enea_lab/fuel.xml     | 88 ++++++++++++++++++++++
+ 1 file changed, 88 insertions(+)
+ create mode 100644 deploy/templates/hardware_environment/vms/enea_lab/fuel.xml
+
+diff --git a/deploy/templates/hardware_environment/vms/enea_lab/fuel.xml b/deploy/templates/hardware_environment/vms/enea_lab/fuel.xml
+new file mode 100644
+index 0000000..8773ed4
+--- /dev/null
++++ b/deploy/templates/hardware_environment/vms/enea_lab/fuel.xml
+@@ -0,0 +1,88 @@
++<domain type='kvm' id='1'>
++  <name>fuel</name>
++  <memory unit='KiB'>8290304</memory>
++  <currentMemory unit='KiB'>8290304</currentMemory>
++  <vcpu placement='static'>8</vcpu>
++  <resource>
++    <partition>/machine</partition>
++  </resource>
++  <os>
++    <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
++    <boot dev='cdrom'/>
++    <boot dev='hd'/>
++    <bootmenu enable='no'/>
++  </os>
++  <features>
++    <acpi/>
++    <apic/>
++    <pae/>
++  </features>
++  <cpu mode='host-model'>
++    <model fallback='allow'/>
++  </cpu>
++  <clock offset='utc'>
++    <timer name='rtc' tickpolicy='catchup'/>
++    <timer name='pit' tickpolicy='delay'/>
++    <timer name='hpet' present='no'/>
++  </clock>
++  <on_poweroff>destroy</on_poweroff>
++  <on_reboot>restart</on_reboot>
++  <on_crash>restart</on_crash>
++  <pm>
++    <suspend-to-mem enabled='no'/>
++    <suspend-to-disk enabled='no'/>
++  </pm>
++  <devices>
++    <emulator>/usr/libexec/qemu-kvm</emulator>
++    <disk type='file' device='disk'>
++      <driver name='qemu' type='qcow2'/>
++      <target dev='vda' bus='virtio'/>
++    </disk>
++    <disk type='block' device='cdrom'>
++      <driver name='qemu' type='raw'/>
++      <target dev='hdb' bus='ide'/>
++      <readonly/>
++    </disk>
++    <controller type='usb' index='0' model='ich9-ehci1'>
++    </controller>
++    <controller type='usb' index='0' model='ich9-uhci1'>
++      <master startport='0'/>
++    </controller>
++    <controller type='usb' index='0' model='ich9-uhci2'>
++      <master startport='2'/>
++    </controller>
++    <controller type='usb' index='0' model='ich9-uhci3'>
++      <master startport='4'/>
++    </controller>
++    <controller type='pci' index='0' model='pci-root'>
++    </controller>
++    <controller type='ide' index='0'>
++    </controller>
++    <controller type='virtio-serial' index='0'>
++    </controller>
++    <interface type='bridge'>
++      <model type='virtio'/>
++    </interface>
++    <interface type='bridge'>
++      <model type='virtio'/>
++    </interface>
++    <serial type='pty'>
++      <source path='/dev/pts/0'/>
++      <target port='0'/>
++    </serial>
++    <console type='pty' tty='/dev/pts/0'>
++      <source path='/dev/pts/0'/>
++      <target type='serial' port='0'/>
++    </console>
++    <input type='mouse' bus='ps2'/>
++    <input type='keyboard' bus='ps2'/>
++    <graphics type='vnc' port='5906' autoport='yes' listen='127.0.0.1'>
++      <listen type='address' address='127.0.0.1'/>
++    </graphics>
++    <video>
++      <model type='vga' vram='16384' heads='1'/>
++    </video>
++    <memballoon model='virtio'>
++    </memballoon>
++  </devices>
++</domain>