Adds ability to power off nodes in clean 17/19917/4
authorTim Rozet <trozet@redhat.com>
Tue, 30 Aug 2016 17:06:33 +0000 (13:06 -0400)
committerTim Rozet <trozet@redhat.com>
Tue, 30 Aug 2016 19:34:04 +0000 (15:34 -0400)
Now if an inventory file is provided to clean, those nodes will be
powered off.

JIRA: APEX-250

Change-Id: I2d78285717726c3d1c9d7d88c38e706d4617e337
Signed-off-by: Tim Rozet <trozet@redhat.com>
build/rpm_specs/opnfv-apex-common.spec
ci/clean.sh
ci/dev_deploy_check.sh
lib/python/apex/__init__.py
lib/python/apex/clean.py [new file with mode: 0644]
lib/python/apex/common/utils.py
lib/python/apex_python_utils.py
tests/config/inventory.yaml [new file with mode: 0644]
tests/test_apex_clean.py [new file with mode: 0644]
tests/test_apex_common_utils.py

index b9125b3..f3d1427 100644 (file)
@@ -69,6 +69,7 @@ install lib/python/apex/deploy_settings.py %{buildroot}%{python3_sitelib}/apex/
 install lib/python/apex/ip_utils.py %{buildroot}%{python3_sitelib}/apex/
 install lib/python/apex/network_environment.py %{buildroot}%{python3_sitelib}/apex/
 install lib/python/apex/network_settings.py %{buildroot}%{python3_sitelib}/apex/
+install lib/python/apex/clean.py %{buildroot}%{python3_sitelib}/apex/
 mkdir -p %{buildroot}%{python3_sitelib}/apex/common
 install lib/python/apex/common/__init__.py %{buildroot}%{python3_sitelib}/apex/common/
 install lib/python/apex/common/constants.py %{buildroot}%{python3_sitelib}/apex/common/
@@ -131,6 +132,8 @@ install config/inventory/pod_example_settings.yaml %{buildroot}%{_docdir}/opnfv/
 %doc %{_docdir}/opnfv/inventory.yaml.example
 
 %changelog
+* Tue Aug 30 2016 Tim Rozet <trozet@redhat.com> - 3.0-12
+- Add clean library.
 * Mon Aug 1 2016 Tim Rozet <trozet@redhat.com> - 3.0-11
 - Add nosdn fdio scenarios.
 * Tue Jul 5 2016 Dan Radez <dradez@redhat.com> - 3.0-10
index 4cf6b64..e4b2d10 100755 (executable)
 CONFIG=${CONFIG:-'/var/opt/opnfv'}
 RESOURCES=${RESOURCES:-"$CONFIG/images"}
 LIB=${LIB:-"$CONFIG/lib"}
+reset=$(tput sgr0 || echo "")
+blue=$(tput setaf 4 || echo "")
+red=$(tput setaf 1 || echo "")
+green=$(tput setaf 2 || echo "")
 
 ##LIBRARIES
-if ! source $LIB/common-functions.sh; then
-  echo "Failed to source $LIB/common-functions.sh"
-  exit 1
-fi
+for lib in common-functions parse-functions; do
+  if ! source $LIB/${lib}.sh; then
+    echo "Failed to source $LIB/${lib}.sh"
+    exit 1
+  fi
+done
 
 vm_index=4
 ovs_bridges="br-admin br-private br-public br-storage"
 OPNFV_NETWORK_TYPES="admin_network private_network public_network storage_network api_network"
 
+
+display_usage() {
+  echo -e "Usage:\n$0 [arguments] \n"
+  echo -e "   -i|--inventory : Full path to inventory yaml file. Required only for baremetal node clean"
+}
+
+##translates the command line parameters into variables
+##params: $@ the entire command line is passed
+##usage: parse_cmd_line() "$@"
+parse_cmdline() {
+  echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n"
+  echo "Use -h to display help"
+  sleep 2
+
+  while [ "${1:0:1}" = "-" ]
+  do
+    case "$1" in
+        -h|--help)
+                display_usage
+                exit 0
+            ;;
+        -i|--inventory)
+                INVENTORY_FILE=$2
+                shift 2
+            ;;
+        *)
+                display_usage
+                exit 1
+            ;;
+    esac
+  done
+
+  if [[ ! -z "$INVENTORY_FILE" && ! -f "$INVENTORY_FILE" ]]; then
+    echo -e "{$red}ERROR: Inventory File: ${INVENTORY_FILE} does not exist! Exiting...${reset}"
+    exit 1
+  fi
+}
+
+parse_cmdline "$@"
+
+if [ -n "$INVENTORY_FILE" ]; then
+  echo -e "${blue}INFO: Parsing inventory file...${reset}"
+  if ! python3.4 -B $LIB/python/apex_python_utils.py clean -f ${INVENTORY_FILE}; then
+    echo -e "${red}WARN: Unable to shutdown all nodes! Please check /var/log/apex.log${reset}"
+  else
+    echo -e "${blue}INFO: Node shutdown complete...${reset}"
+  fi
+fi
+
 # Clean off instack/undercloud VM
 for vm in instack undercloud; do
   virsh destroy $vm 2> /dev/null | xargs echo -n
index 68a9ac2..0ce135a 100755 (executable)
@@ -42,6 +42,9 @@ for i in epel-release python34-PyYAML openvswitch openstack-tripleo libguestfs l
     fi
 done
 
+# install pip dependencies
+easy_install-3.4 pip
+sudo pip3 install python-ipmi
 
 # Make sure jinja2 is installed
 easy_install-3.4 jinja2
index e1b8b54..9993dc9 100644 (file)
@@ -11,3 +11,4 @@
 from .network_settings import NetworkSettings
 from .deploy_settings import DeploySettings
 from .network_environment import NetworkEnvironment
+from .clean import clean_nodes
diff --git a/lib/python/apex/clean.py b/lib/python/apex/clean.py
new file mode 100644 (file)
index 0000000..184b5ec
--- /dev/null
@@ -0,0 +1,39 @@
+##############################################################################
+# Copyright (c) 2016 Tim Rozet (trozet@redhat.com) and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Clean will eventually be migrated to this file
+
+import logging
+import pyipmi
+import pyipmi.interfaces
+import sys
+
+from .common import utils
+
+
+def clean_nodes(inventory):
+    inv_dict = utils.parse_yaml(inventory)
+    if inv_dict is None or 'nodes' not in inv_dict:
+        logging.error("Inventory file is empty or missing nodes definition")
+        sys.exit(1)
+    for node, node_info in inv_dict['nodes'].items():
+        logging.info("Cleaning node: {}".format(node))
+        try:
+            interface = pyipmi.interfaces.create_interface(
+                'ipmitool', interface_type='lanplus')
+            connection = pyipmi.create_connection(interface)
+            connection.session.set_session_type_rmcp(node_info['ipmi_ip'])
+            connection.target = pyipmi.Target(0x20)
+            connection.session.set_auth_type_user(node_info['ipmi_user'],
+                                                  node_info['ipmi_pass'])
+            connection.session.establish()
+            connection.chassis_control_power_down()
+        except Exception as e:
+            logging.error("Failure while shutting down node {}".format(e))
+            sys.exit(1)
index b7678a2..fe34096 100644 (file)
@@ -7,9 +7,17 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
+import yaml
+
 
 def str2bool(var):
     if isinstance(var, bool):
         return var
     else:
         return var.lower() in ("true", "yes")
+
+
+def parse_yaml(yaml_file):
+    with open(yaml_file) as f:
+        parsed_dict = yaml.load(f)
+        return parsed_dict
index b6aaafa..1f5e407 100755 (executable)
@@ -7,6 +7,7 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
+import apex
 import argparse
 import sys
 import logging
@@ -61,6 +62,10 @@ def parse_deploy_settings(args):
     settings.dump_bash()
 
 
+def run_clean(args):
+    apex.clean_nodes(args.file)
+
+
 def find_ip(args):
     """
     Get and print the IP from a specific interface
@@ -192,6 +197,12 @@ def get_parser():
                                  help='path to deploy settings file')
     deploy_settings.set_defaults(func=parse_deploy_settings)
 
+    clean = subparsers.add_parser('clean',
+                                  help='Parse deploy settings file')
+    clean.add_argument('-f', '--file',
+                       help='path to inventory file')
+    clean.set_defaults(func=run_clean)
+
     return parser
 
 
diff --git a/tests/config/inventory.yaml b/tests/config/inventory.yaml
new file mode 100644 (file)
index 0000000..607df29
--- /dev/null
@@ -0,0 +1,56 @@
+nodes:
+  node1:
+    mac_address: "00:25:B5:cc:00:1e"
+    ipmi_ip: 72.30.8.69
+    ipmi_user: admin
+    ipmi_pass: octopus
+    pm_type: "pxe_ipmitool"
+    cpus: 2
+    memory: 8192
+    disk: 40
+    arch: "x86_64"
+    capabilities: "profile:control"
+  node2:
+    mac_address: "00:25:B5:cc:00:5d"
+    ipmi_ip: 72.30.8.78
+    ipmi_user: admin
+    ipmi_pass: octopus
+    pm_type: "pxe_ipmitool"
+    cpus: 2
+    memory: 8192
+    disk: 40
+    arch: "x86_64"
+    capabilities: "profile:control"
+  node3:
+    mac_address: "00:25:B5:cc:00:1d"
+    ipmi_ip: 72.30.8.67
+    ipmi_user: admin
+    ipmi_pass: octopus
+    pm_type: "pxe_ipmitool"
+    cpus: 2
+    memory: 8192
+    disk: 40
+    arch: "x86_64"
+    capabilities: "profile:control"
+  node4:
+    mac_address: "00:25:B5:cc:00:3c"
+    ipmi_ip: 72.30.8.76
+    ipmi_user: admin
+    ipmi_pass: octopus
+    pm_type: "pxe_ipmitool"
+    cpus: 2
+    memory: 8192
+    disk: 40
+    arch: "x86_64"
+    capabilities: "profile:compute"
+  node5:
+    mac_address: "00:25:B5:cc:00:5b"
+    ipmi_ip: 72.30.8.71
+    ipmi_user: admin
+    ipmi_pass: octopus
+    pm_type: "pxe_ipmitool"
+    cpus: 2
+    memory: 8192
+    disk: 40
+    arch: "x86_64"
+    capabilities: "profile:compute"
diff --git a/tests/test_apex_clean.py b/tests/test_apex_clean.py
new file mode 100644 (file)
index 0000000..2a436a7
--- /dev/null
@@ -0,0 +1,41 @@
+##############################################################################
+# Copyright (c) 2016 Tim Rozet (Red Hat)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import mock
+import pyipmi
+import pyipmi.chassis
+
+from apex import clean_nodes
+from mock import patch
+from nose import tools
+
+
+class TestClean(object):
+    @classmethod
+    def setup_class(klass):
+        """This method is run once for each class before any tests are run"""
+
+    @classmethod
+    def teardown_class(klass):
+        """This method is run once for each class _after_ all tests are run"""
+
+    def setUp(self):
+        """This method is run once before _each_ test method is executed"""
+
+    def teardown(self):
+        """This method is run once after _each_ test method is executed"""
+
+    def test_clean(self):
+        with mock.patch.object(pyipmi.Session, 'establish') as mock_method:
+            with patch.object(pyipmi.chassis.Chassis,
+                              'chassis_control_power_down') as mock_method2:
+                clean_nodes('config/inventory.yaml')
+
+        tools.assert_equal(mock_method.call_count, 5)
+        tools.assert_equal(mock_method2.call_count, 5)
index 7c988e3..9459865 100644 (file)
@@ -7,9 +7,9 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
-from apex.common.utils import str2bool
+import nose.tools
 
-from nose.tools import assert_equal
+from apex.common import utils
 
 
 class TestCommonUtils(object):
@@ -28,7 +28,12 @@ class TestCommonUtils(object):
         """This method is run once after _each_ test method is executed"""
 
     def test_str2bool(self):
-        assert_equal(str2bool(True), True)
-        assert_equal(str2bool(False), False)
-        assert_equal(str2bool("True"), True)
-        assert_equal(str2bool("YES"), True)
+        nose.tools.assert_equal(utils.str2bool(True), True)
+        nose.tools.assert_equal(utils.str2bool(False), False)
+        nose.tools.assert_equal(utils.str2bool("True"), True)
+        nose.tools.assert_equal(utils.str2bool("YES"), True)
+
+    def test_parse_yaml(self):
+        nose.tools.assert_is_instance(
+            utils.parse_yaml('../config/network/network_settings.yaml'),
+            dict)