| 1 | #!/usr/bin/python |
| 2 | # -*- coding: utf-8 -*- |
| 3 | # |
| 4 | # Copyright (C) 2015 - Michael Jeanson <mjeanson@efficios.com> |
| 5 | # |
| 6 | # This program is free software: you can redistribute it and/or modify |
| 7 | # it under the terms of the GNU General Public License as published by |
| 8 | # the Free Software Foundation, either version 3 of the License, or |
| 9 | # (at your option) any later version. |
| 10 | # |
| 11 | # This program is distributed in the hope that it will be useful, |
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | # GNU General Public License for more details. |
| 15 | # |
| 16 | # You should have received a copy of the GNU General Public License |
| 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 18 | |
| 19 | """ This script is used to upgrade the base snapshot of standalone ci slaves """ |
| 20 | |
| 21 | USERNAME = '' |
| 22 | APIKEY = '' |
| 23 | JENKINS_URL = 'https://ci.lttng.org' |
| 24 | |
| 25 | DISTRO_LIST = ['el', 'sles', 'ubuntu'] |
| 26 | DEFAULT_DISTRO = 'ubuntu' |
| 27 | DISTRO_COMMAND = { |
| 28 | 'el': 'yum update -y && package-cleanup -y --oldkernels --count=2 && yum clean all', |
| 29 | 'sles': 'zypper --non-interactive refresh && zypper --non-interactive patch --auto-agree-with-licenses --with-interactive', |
| 30 | 'ubuntu': 'apt-get update && apt-get dist-upgrade -V -y && apt-get clean && apt-get --purge autoremove -y', |
| 31 | } |
| 32 | |
| 33 | BASESNAP = 'base-configuration' |
| 34 | |
| 35 | SNAPSHOTXML = """ |
| 36 | <domainsnapshot> |
| 37 | <name>%s</name> |
| 38 | <description>Snapshot of OS install and updates</description> |
| 39 | <memory snapshot='no'/> |
| 40 | </domainsnapshot> |
| 41 | """ % BASESNAP |
| 42 | |
| 43 | import argparse |
| 44 | import sys |
| 45 | import libvirt |
| 46 | from jenkinsapi.jenkins import Jenkins |
| 47 | from time import sleep |
| 48 | import paramiko |
| 49 | import select |
| 50 | |
| 51 | |
| 52 | def main(): |
| 53 | """ Main """ |
| 54 | |
| 55 | parser = argparse.ArgumentParser(description='Update base snapshot.') |
| 56 | parser.add_argument('instance_name', metavar='INSTANCE', type=str, |
| 57 | help='the shortname of the instance to update') |
| 58 | parser.add_argument('vmhost_name', metavar='VMHOST', type=str, |
| 59 | help='the hostname of the VM host') |
| 60 | parser.add_argument('--distro', choices=DISTRO_LIST, |
| 61 | default=DEFAULT_DISTRO, type=str, |
| 62 | help='the distro of the target instance') |
| 63 | |
| 64 | args = parser.parse_args() |
| 65 | |
| 66 | instance_name = args.instance_name |
| 67 | vmhost_name = args.vmhost_name |
| 68 | distro = args.distro |
| 69 | |
| 70 | |
| 71 | # Get jenkibs connexion |
| 72 | jenkins = Jenkins(JENKINS_URL, username=USERNAME, password=APIKEY) |
| 73 | |
| 74 | # Get jenkins node |
| 75 | print("Getting node %s from Jenkins..." % instance_name) |
| 76 | node = jenkins.get_node(instance_name) |
| 77 | |
| 78 | if not node: |
| 79 | print("Could not get node %s on %s" % (instance_name, JENKINS_URL)) |
| 80 | sys.exit(1) |
| 81 | |
| 82 | # Check if node is idle |
| 83 | if not node.is_idle: |
| 84 | print("Node %s is not idle" % instance_name) |
| 85 | sys.exit(1) |
| 86 | |
| 87 | |
| 88 | # Set node temporarily offline |
| 89 | if not node.is_temporarily_offline(): |
| 90 | node.toggle_temporarily_offline('Down for upgrade to base snapshot') |
| 91 | |
| 92 | # Get libvirt connexion |
| 93 | print("Opening libvirt connexion to %s..." % vmhost_name) |
| 94 | vmhost = libvirt.open("qemu+ssh://root@%s/system" % vmhost_name) |
| 95 | |
| 96 | if not vmhost: |
| 97 | print("Could not connect to libvirt on %s" % vmhost_name) |
| 98 | sys.exit(1) |
| 99 | |
| 100 | # Get instance |
| 101 | print("Getting instance %s from libvirt..." % instance_name) |
| 102 | vminstance = vmhost.lookupByName(instance_name) |
| 103 | |
| 104 | if not vminstance: |
| 105 | print("Could not get instance %s on %s" % (instance_name, vmhost_name)) |
| 106 | sys.exit(1) |
| 107 | |
| 108 | # If instance is running, shutdown |
| 109 | print("Checking if instance %s is running..." % instance_name) |
| 110 | if vminstance.isActive(): |
| 111 | try: |
| 112 | print("Shutting down instance %s" % instance_name) |
| 113 | vminstance.destroy() |
| 114 | except: |
| 115 | print("Failed to shutdown %s", instance_name) |
| 116 | sys.exit(1) |
| 117 | |
| 118 | |
| 119 | # Revert to base snapshot |
| 120 | print("Getting base snapshot...") |
| 121 | basesnap = vminstance.snapshotLookupByName(BASESNAP) |
| 122 | if not basesnap: |
| 123 | print("Could not find base snapshot %s" % BASESNAP) |
| 124 | sys.exit(1) |
| 125 | |
| 126 | #if not basesnap.isCurrent(): |
| 127 | # print("Not current snapshot") |
| 128 | |
| 129 | print("Reverting to base snapshot...") |
| 130 | try: |
| 131 | vminstance.revertToSnapshot(basesnap) |
| 132 | except: |
| 133 | print("Failed to revert to base snapshot %s" % basesnap.getName()) |
| 134 | sys.exit(1) |
| 135 | |
| 136 | # Launch instance |
| 137 | try: |
| 138 | print("Starting instance %s.." % instance_name) |
| 139 | vminstance.create() |
| 140 | except: |
| 141 | print("Failed to start instance %s" % instance_name) |
| 142 | sys.exit(1) |
| 143 | |
| 144 | |
| 145 | # Wait for instance to boot |
| 146 | print("Waiting for instance to boot...") |
| 147 | sleep(10) |
| 148 | |
| 149 | # Run dist-upgrade |
| 150 | print("Running upgrade command...") |
| 151 | client = paramiko.SSHClient() |
| 152 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) |
| 153 | client.load_system_host_keys() |
| 154 | client.connect(instance_name, username="root") |
| 155 | stdin, stdout, stderr = client.exec_command(DISTRO_COMMAND[distro]) |
| 156 | while not stdout.channel.exit_status_ready(): |
| 157 | if stdout.channel.recv_ready(): |
| 158 | rl, wl, xl = select.select([stdout.channel], [], [], 0.0) |
| 159 | if len(rl) > 0: |
| 160 | print(stdout.channel.recv(1024)), |
| 161 | |
| 162 | if stdout.channel.recv_exit_status() != 0: |
| 163 | print("Update command failed!") |
| 164 | sys.exit(1) |
| 165 | |
| 166 | # Close ssh connexion |
| 167 | client.close() |
| 168 | |
| 169 | # Shutdown VM |
| 170 | print("Shutting down instance...") |
| 171 | try: |
| 172 | vminstance.shutdown() |
| 173 | except: |
| 174 | print("Failed to shutdown instance %s" % instance_name) |
| 175 | sys.exit(1) |
| 176 | |
| 177 | while vminstance.isActive(): |
| 178 | sleep(1) |
| 179 | print("Waiting for instance to shutdown...") |
| 180 | |
| 181 | # Delete original base snapshot |
| 182 | print("Deleting current base snapshot...") |
| 183 | try: |
| 184 | basesnap.delete() |
| 185 | except: |
| 186 | print("Failed to delete base snapshot %s" % basesnap.getName()) |
| 187 | sys.exit(1) |
| 188 | |
| 189 | # Create new base snapshot |
| 190 | print("Creating new base snapshot...") |
| 191 | try: |
| 192 | vminstance.snapshotCreateXML(SNAPSHOTXML) |
| 193 | except: |
| 194 | print("Failed to create new snapshot.") |
| 195 | sys.exit(1) |
| 196 | |
| 197 | # Set node online in jenkins |
| 198 | if node.is_temporarily_offline(): |
| 199 | node.toggle_temporarily_offline() |
| 200 | |
| 201 | # And we're done! |
| 202 | print("All done!") |
| 203 | |
| 204 | |
| 205 | if __name__ == "__main__": |
| 206 | main() |
| 207 | |
| 208 | # EOF |