]> review.fuel-infra Code Review - tools/sustaining.git/commitdiff
Initial commit 83/5283/7
authorDenis Meltsaykin <dmeltsaykin@mirantis.com>
Fri, 3 Apr 2015 13:19:06 +0000 (13:19 +0000)
committerDenis Meltsaykin <dmeltsaykin@mirantis.com>
Tue, 7 Apr 2015 11:37:59 +0000 (11:37 +0000)
apply_patches.py - script to patch/revert several nodes
with patches keeping order

mos_apply_mu.py - script to install updates repository into nodes
(still in progress)

fuel_repo_update.py - sketch of script to update Fuel's repository
list via REST-API

Change-Id: Iebf530f77c5eaf167ab8921b4f2ddf786f56310c

scripts/apply_patches.py [new file with mode: 0644]
scripts/custom.pkg [new file with mode: 0644]
scripts/fuel_repo_update.py [new file with mode: 0644]
scripts/mos_apply_mu.py [new file with mode: 0644]
scripts/patches.list [new file with mode: 0644]

diff --git a/scripts/apply_patches.py b/scripts/apply_patches.py
new file mode 100644 (file)
index 0000000..0c0d89a
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+import sys
+import json
+import os
+import subprocess
+
+#subprocess.Popen (["ssh", "127.0.0.1", 'patch --dry-run -Ntbp0'], stdin=file,stdout=log, stderr=log)
+
+CONFIG_FILE = "patches.list"
+cfg = dict()
+logfile = "patch_apply.log"
+dry_run = True
+
+def cat_file_via_pipe(file, host):
+    patch={
+        True:"patch --dry-run -d / -Ntbp0",
+        False:"patch -d / -Ntbp0"
+    }
+    PIPE=subprocess.PIPE
+    try:
+        fp=open(file,'r')
+    except Exception as e:
+        msg="Error opening file {0}: {1}\n".format(file, e.args[1])
+        print (msg)
+        LOG(msg)
+        return False
+    cmdline=["ssh", host, patch[dry_run]+reverse]
+    print (cmdline)
+    pp=subprocess.Popen(cmdline, stdin=fp, stdout=log, stderr=log)
+    pp.wait()
+    fp.close()
+    if pp.returncode != 0:
+        print ("Something went wrong, see {0} for infomation".format(logfile))
+        return False
+    return True
+
+def read_config():
+    global cfg
+    try:
+        file = open(CONFIG_FILE, 'r')
+        cfg = json.load(file)
+        file.close()
+    except:
+        print ("Couldn't load config-file")
+        os.exit(1)
+
+def apply_patches():
+    patches = cfg['patches']
+    if reverse != "": 
+        patches=reversed(cfg['patches'])
+    for node in cfg['nodes']:
+        for patch in patches:
+            LOG("\n>>>>>>>>>START of PATCHING {0} with {1} (dry-run:{2})\n\n".format(
+                                                                        node,patch,dry_run))
+            retval=cat_file_via_pipe(patch, node)
+            if retval is False:
+                msg="Unable to apply patch {0} to {1} (dry-run:{2})\n".format(patch, node, dry_run)
+            else:
+                msg="Successfuly patched {0} to {1} (dry-run:{2})\n".format(patch, node, dry_run)
+            LOG(msg)
+            print (msg)
+            LOG("\n<<<<<<<<<END of PATCHING {0} with {1} (dry_run:{2})\n".format(node,patch,dry_run))
+
+def LOG(str):
+    log.write(str)
+    log.flush()
+
+def main():
+    global log
+    global dry_run
+    global reverse
+    reverse = ""
+    try:
+        log=open(logfile, 'w', 0)
+    except:
+        os.exit(1)
+    for cmd in sys.argv[1:]:
+        if '--apply' in cmd: dry_run = False
+        if '--reverse' in cmd: reverse=" -R"
+
+    print ("To apply patches use --apply command line option, otherwise no changes will be made.\nUse --reverse to revert patches.")
+
+    read_config()
+    apply_patches()
+    log.close()
+
+if __name__ == "__main__": main()
diff --git a/scripts/custom.pkg b/scripts/custom.pkg
new file mode 100644 (file)
index 0000000..59bf2ee
--- /dev/null
@@ -0,0 +1,12 @@
+{
+    "centos": [
+        "http://mirror.centos.org/centos-6/6/updates/x86_64/Packages/kernel-2.6.32-504.12.2.el6.x86_64.rpm",
+        "http://mirror.centos.org/centos-6/6/updates/x86_64/Packages/openssl-1.0.1e-30.el6_6.7.x86_64.rpm"
+    ],
+    "ubuntu": [
+        "http://security.ubuntu.com/ubuntu/pool/main/l/linux-lts-trusty/linux-image-3.13.0-49-generic_3.13.0-49.81~precise1_amd64.deb",
+        "http://security.ubuntu.com/ubuntu/pool/main/o/openssl/openssl_1.0.1-4ubuntu5.25_amd64.deb",
+        "http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.0.0_1.0.1-4ubuntu5.25_amd64.deb",
+        "http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.0.1-4ubuntu5.25_amd64.deb"
+    ]
+}
diff --git a/scripts/fuel_repo_update.py b/scripts/fuel_repo_update.py
new file mode 100644 (file)
index 0000000..7068588
--- /dev/null
@@ -0,0 +1,55 @@
+# Copyright 2013 - 2015 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+import urllib2
+from keystoneclient.v2_0 import client
+
+if __name__ == "__main__":
+    username = 'admin'
+    password = 'admin'
+    tenant = 'admin'
+    auth = 'http://127.0.0.1:5000/v2.0'
+    releases = 'http://127.0.0.1:8000/api/v1/releases/{0}/'
+
+    ks = client.Client (username=username, password=password,
+            tenant_name=tenant, auth_url=auth)
+    token = ks.auth_token
+
+    for release in xrange(1,3):
+        req = urllib2.Request(releases.format(release))
+        req.add_header('X-Auth-Token',token)
+        instance = json.load(urllib2.urlopen(req))
+        repo_metadata = instance['orchestrator_data']['repo_metadata']
+
+        if repo_metadata.has_key('updates'):
+            print ("Updates repositiry is already installed for {0}".format(
+                                                instance['operating_system']))
+            continue
+        if instance['operating_system'] == 'Ubuntu':
+            repo_metadata.update({u'updates':
+                u'http://10.20.0.2:8080/updates/ubuntu precise main'})
+        else:
+            repo_metadata.update({u'updates':
+                u'http://10.20.0.2:8080/updates/centos/os/x86_64'})
+
+        req = urllib2.Request(releases.format(release), data=json.dumps(instance))
+        req.add_header('X-Auth-Token',token)
+        req.get_method = lambda: 'PUT'
+        print ("Pushing changes")
+        try:
+            urllib2.urlopen(req)
+        except urllib2.HTTPError as e:
+            print ("Changes can't be applied: {0}".format(e.args[0]))
+
diff --git a/scripts/mos_apply_mu.py b/scripts/mos_apply_mu.py
new file mode 100644 (file)
index 0000000..ed796da
--- /dev/null
@@ -0,0 +1,273 @@
+# Copyright 2013 - 2015 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+import sys
+import urllib2
+import subprocess
+import os
+
+try:
+    from keystoneclient.v2_0 import client
+except ImportError:
+    print ("Can't find keystone-client, this script must be only used on Fuel-node")
+    sys.exit(10)
+
+
+username = 'admin'
+password = 'admin'
+tenant = 'admin'
+auth = 'http://127.0.0.1:5000/v2.0'
+logfile = 'mos_apply_mu.log'
+
+
+env_id = 0
+all_envs = False
+try_offline = False
+really = False
+
+master_ip = "10.20.0.2"
+
+path = "custom.pkg"
+install_custom = False
+pkgs = None
+dest = "/var/www/nailgun/updates/custom/"
+
+
+def arg_parse ():
+    global env_id
+    global all_envs
+    global try_offline
+    global really
+    global username
+    global password
+    global tenant
+    global path
+    global install_custom
+    global repo_install
+    global master_ip
+
+    usage="""
+Tool to install updates repository into running cluster nodes.
+Usage:
+    python nodes_update.py [--env-id=X] [--all-envs]
+
+Params:
+    --master-ip         IP-Address of Fuel-master node
+                            default: 10.20.0.2
+    --env-id            ID of operational environment
+                            which needs to be updated
+    --all-envs          Update all operational environments
+    --offline           Try to update nodes which are currently offline in fuel
+                            (can cause significant timeouts)
+    --update            Make real update (without this nothing will be updated)
+    --user              Username used in Fuel-Keystone authentication
+                            default: admin
+    --pass              Password suitable to username
+                            default: admin
+    --tenant            Suitable tenant
+                            default: admin
+    --with-custom       Download and install custom packages from json-file
+                            default filename: custom.pkg
+
+    --file              Name of the config file in json-format with list of
+                        custom packages to download and install
+
+
+Examples:
+
+    python nodes_update.py --all-envs --user=op --pass=V3ryS3Cur3 --master-ip="10.100.15.2"
+    Inspects Fuel with op's credentials and shows commands that should be applied
+
+    python nodes_update.py --all-envs --user=op --pass=V3ryS3Cur3 --update --master-ip="10.100.15.2"
+    Makes real update
+
+Log is stored in mos_apply_mu.log please read it carefully.
+
+Questions: dmeltsaykin@mirantis.com
+
+Mirantis, 2015
+"""
+
+    for cmd in sys.argv[1:]:
+        if '--env-id' in cmd: env_id = int(cmd.split('=')[1])
+        if '--user' in cmd: username = cmd.split('=')[1]
+        if '--pass' in cmd: password = cmd.split('=')[1]
+        if '--tenant' in cmd: tenant = cmd.split('=')[1]
+        if '--all-envs' in cmd: all_envs = True
+        if '--offline' in cmd: try_offline = True
+        if '--update' in cmd: really = True
+        if '--file' in cmd: path = cmd.split('=')[1]
+        if '--with-custom' in cmd: install_custom = True
+        if '--master-ip' in cmd: master_ip = cmd.split('=')[1]
+
+    if (env_id > 0) and (all_envs == True):
+        print ("You should only select either --env-id or --all-envs.")
+        print (usage)
+        sys.exit(5)
+    if (env_id == 0) and (all_envs == False):
+        print ("At least one option (env-id or all-envs) must be set.")
+        print (usage)
+        sys.exit(6)
+
+    repo_install = {
+        'ubuntu': """(grep -q "updates" /etc/apt/sources.list || echo -e "\ndeb http://{0}:8080/updates/ubuntu precise main" >> /etc/apt/sources.list); apt-get update; apt-get upgrade -y""".format(master_ip),
+        'centos': """yum-config-manager --add-repo=http://{0}:8080/updates/centos/os/x86_64/; yum update --skip-broken -y --nogpgcheck""".format(master_ip)
+    }
+
+
+def get_downloads_list():
+    global pkgs
+    try:
+        file=open(path,'r')
+        pkgs=json.load(file)
+        file.close()
+    except:
+        return None
+    return True
+
+def packages_download ():
+    #check if dst dir exists if not create it (and others)
+    try:
+        os.makedirs(dest)
+    except os.error as err:
+        if err.args[0] != 17:
+            print ("Error during creating directory {0}: {1}".format(
+                dest, err.args[1]))
+            return (None)
+
+    retval = 0
+    for pkg in pkgs.values():
+        for t in pkg:
+            cmd="wget -c -P{0} \"{1}\"".format(dest, t)
+            print ("Running: {0}".format(cmd))
+            retval += os.system(cmd)
+
+    if retval != 0:
+        print ("Some downloads are failed!")
+    return (retval)
+
+def get_nodes ():
+    req = urllib2.Request('http://127.0.0.1:8000/api/v1/nodes/')
+    req.add_header('X-Auth-Token',token)
+    nodes = json.load(urllib2.urlopen(req))
+    return nodes
+
+def get_operational_envs (nodes, env_list):
+    for node in nodes:
+        if (node['status'] == "ready"):
+            if try_offline == True: env_list.add(node['cluster'])
+            elif node['online'] == True: env_list.add(node['cluster'])
+
+def do_node_update (nodes, env_list):
+    to_update = set()
+    for env in env_list:
+        for node in nodes:
+            if node['cluster'] == env:
+                if try_offline == True:
+                    to_update.add((node['ip'], node['os_platform']))
+                elif node['online'] == True:
+                    to_update.add((node['ip'], node['os_platform']))
+
+    if install_custom == True:
+        if get_downloads_list() is not None:
+            packages_download()
+        else:
+            print ("Unable to get packages list from file {0}".format(path))
+
+    print (to_update)
+    if really == True:
+        log = open(logfile, 'w',0)
+    else:
+        log = open('/dev/null', 'w')
+
+    should = len(to_update)
+    have = 0
+    for ip,os_version in to_update:
+        log.write("-------------- UPDATING {0} -----------------\n".format(ip))
+        cmdline = ["ssh", "-t", "-t", str(ip), repo_install[os_version]]
+        print (cmdline)
+        log.write(str(cmdline)+"\n")
+        if really == True:
+            tmp=subprocess.Popen(cmdline, stdin=None, stdout=log, stderr=log)
+            tmp.wait()
+            if tmp.returncode != 0:
+                msg="Update procedure on {0} returned FAIL({1}). Check log for details.\n".format(ip,tmp.returncode)
+            else:
+                msg="Update procedure on {0} completed with success!\n".format(ip)
+                have += 1
+            print(msg)
+            log.write(msg)
+        if install_custom == True:
+            do_install_custom(ip, os_version, flag=really, logfp=log)
+        log.write("---------------- DONE -------------------\n")
+    msg = "Total: {0} out of {1} nodes updated successfully.\n".format(have, should)
+    print (msg)
+    log.write(msg)
+    log.flush()
+    log.close()
+
+def do_install_custom (ip, os_version, flag=False, logfp=None):
+    install={"ubuntu": "/usr/bin/dpkg -i ", "centos": "rpm -Uvh "}
+    if pkgs is None: return (None)
+
+    for package in pkgs[os_version]:
+        cmdline=["scp", dest+package.split("/")[-1], str(ip)+":/tmp/"]
+        print (cmdline)
+        if flag == True:
+            tmp = subprocess.Popen(cmdline, stdin=None, stdout=logfp, stderr=logfp)
+            tmp.wait()
+            if tmp.returncode != 0:
+                msg = "Error in copying {0} to {1}. Installation skipped. See log for information\n".format(
+                                                                                package.split("/")[-1], ip)
+                print (msg)
+                logfp.write (msg)
+                continue
+            else:
+                msg = "Copied {0} to {1}\n".format(package.split("/")[-1], ip)
+                print (msg)
+                logfp.write (msg)
+
+        cmdline=["ssh","-t", "-t", str(ip), "{0}".format(install[os_version]+"/tmp/"+package.split("/")[-1])]
+        print (cmdline)
+        if flag == True:
+            tmp = subprocess.Popen(cmdline, stdin=None, stdout=logfp, stderr=logfp)
+            tmp.wait()
+            if tmp.returncode != 0:
+                msg = "Installation {0} on {1} returned FAIL. Please check log.\n".format(
+                                                                    package.split("/")[-1],ip)
+            else:
+                msg = "Installation {0} on {1} completed with success!.\n".format(
+                                                                    package.split("/")[-1],ip)
+            print (msg)
+            logfp.write(msg)
+
+if __name__ == "__main__":
+    arg_parse()
+
+    ks = client.Client (username=username, password=password,
+            tenant_name=tenant, auth_url=auth)
+    token = ks.auth_token
+
+    env_list = set()
+    nodes = get_nodes()
+
+    if (env_id > 0):
+        env_list.add(env_id)
+    else:
+        get_operational_envs(nodes,env_list)
+
+    print ("Following envs will be updated: " + ",".join([str(x) for x in env_list]))
+    do_node_update(nodes, env_list)
+    sys.exit(0)
diff --git a/scripts/patches.list b/scripts/patches.list
new file mode 100644 (file)
index 0000000..d02113e
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "nodes": [
+               "127.0.0.1"
+       ],
+       "patches": [
+               "./a.patch"
+       ]
+}