def prep(self): self.sshifc = SSHInterface(device=self.device, address=self.address, username=self.options.username, password=self.options.password, timeout=self.options.timeout, port=self.options.ssh_port) self.sshifc.open()
class UcsTool(Macro): def __init__(self, options, address=None, ucs_name=None): self.options = Options(options) self.ucs_name = ucs_name self.sshparams = Options(device=self.options.device, address=address, timeout=self.options.timeout, username=self.options.username, password=self.options.password) super(UcsTool, self).__init__() def prep(self): self.sshifc = SSHInterface(**self.sshparams) self.api = self.sshifc.open() self.api.run('rm -rf {0} && mkdir -p {0}'.format(UCS_TMP)) def cleanup(self): self.sshifc.close() def setup(self): if not self.api.exists('/var/local/ucs/{}.ucs'.format(self.ucs_name)): raise RuntimeError('UCS not found on the target.') LOG.info('Processing...') self.api.run(r'cd {} && tar xf /var/local/ucs/{}.ucs'.format(UCS_TMP, self.ucs_name)) # Replace all encrypted secrets which will fail to load anyway. self.api.run(r"sed -ibak '/^\s*#/!s/secret.*$/secret default/g' {}/config/bigip.conf".format(UCS_TMP)) # Adding the license back self.api.run(r"cp -f /config/bigip.license {}/config/".format(UCS_TMP)) # Preserve: # - management IP # - management route # Note: DHCP is not supported text = self.api.sftp().open('/config/bigip_base.conf').read() config = tmsh.parser(text) with self.api.sftp().open('{}/config/bigip_base.conf'.format(UCS_TMP), 'at+') as f: f.write(config.match('sys management-ip .*').dumps()) f.write(config.match('sys management-route /Common/default').dumps()) bit = config.match('sys global-settings') bit['sys global-settings']['mgmt-dhcp'] = 'disabled' bit['sys global-settings']['gui-security-banner-text'] = 'Welcome to the BIG-IP Configuration Utility. Configuration imported by UCS Tool 1.0' f.write(bit.dumps()) self.api.run(r'tar -C {} --xform s:"^./":: -czf /var/local/ucs/{}_imported.ucs .'.format(UCS_TMP, self.ucs_name)) #print config if self.options.load: LOG.info('Loading UCS...') self.api.run('tmsh load sys ucs {}_imported no-platform-check no-license'.format(self.ucs_name)) LOG.info('Done.')
class Tool(Macro): def __init__(self, options, address=None, filename=None): self.options = Options(options) self.filename = filename self.sshparams = Options(device=self.options.device, address=address, timeout=self.options.timeout, username=self.options.username, password=self.options.password, port=self.options.port) super(Tool, self).__init__() def prep(self): self.sshifc = SSHInterface(**self.sshparams) self.api = self.sshifc.open() def cleanup(self): self.sshifc.close() def setup(self): if not self.api.exists(self.filename): raise RuntimeError('File %s not found on the target.' % self.filename) LOG.info('Processing...') with self.api.sftp().open(self.filename) as f: result = parser(f.read()) shell = self.api.interactive() shell.expect_exact(PROMPT) shell.sendline('tmsh') shell.expect_exact(PROMPT) leftovers = set() for path in reversed(result.keys()): if path.startswith('ltm pool'): for member in result[path]['members'].keys(): if member.count('.') == 1: # 2002::1.http leftovers.add('ltm node {}'.format(member.split('.')[0])) elif member.count(':') == 1: # 1.1.1.1:21 leftovers.add('ltm node {}'.format(member.split(':')[0])) else: raise ValueError(member) shell.sendline(TMSH_DELETE.format(path)) shell.expect_exact(PROMPT) LOG.info(shell.before) for path in leftovers: shell.sendline(TMSH_DELETE.format(path)) shell.expect_exact(PROMPT) LOG.info(shell.before) LOG.info('Done.')
def reset_all(self): group = RCMD.device.DEFAULT_ALLBIGIQS_GROUP for device in [self.default] + self.peers: with SSHInterface(device=device) as sshifc: LOG.info('Wiping storage on {0}'.format(device)) SCMD.ssh.generic(SCMD.bigiq.ha.HA_WIPE_COMMAND, ifc=sshifc) with EmapiInterface(device=device, auth=AUTH.BASIC) as rstifc: RCMD.system.wait_restjavad([self.default] + self.peers, ifc=rstifc) # For IPv6 runs where localhost will get reset to IPv4. for device in [self.default] + self.peers: with EmapiInterface(device=device, auth=AUTH.BASIC) as rstifc: resp = rstifc.api.get(DeviceResolver.DEVICES_URI % group) selfip_expect = device.get_discover_address() selfips_actual = [x.address for x in resp['items']] if selfip_expect not in selfips_actual: LOG.info( "selfip mismatch. Setting {0}".format(selfip_expect)) self_addr = IPAddress(selfip_expect) payload = NetworkDiscover() payload.discoveryAddress = self_addr.format(ipv6_full) rstifc.api.put(NetworkDiscover.URI, payload=payload) DeviceResolver.wait(rstifc.api, group) # For BZ workarounds.. bigips = [] context = ContextHelper() default_bigiq = context.get_icontrol(device=self.default).version session = context.get_config().get_session().name for device in context.get_config().get_devices(): v = context.get_icontrol(device=device).version if v.product.is_bigip and v >= 'bigip 11.3.0': bigips.append(device) if default_bigiq > 'bigiq 4.3.0' and default_bigiq < 'bigiq 4.5.0': with EmapiInterface(device=self.default, auth=AUTH.BASIC) as rstifc: RCMD.device.clean_dg_certs(bigips, ifc=rstifc) with EmapiInterface(device=self.default, auth=AUTH.BASIC) as rstifc: RCMD.system.bz_help1([self.default] + self.peers, ifc=rstifc) if default_bigiq > 'bigiq 4.3.0' and default_bigiq < 'bigiq 4.5.0': with SSHInterface(device=self.default) as sshifc: SCMD.bigiq.ha.wait_ha_peer(self.peers, session=session, ifc=sshifc)
def do_refresh(self): LOG.info('* Refresh mode *') mode = self.options.refresh with SSHInterface(**self.emparams) as emsshifc: bpmgmt = self.bigipparams.address if mode == 1: rows = SQL.query("SELECT * FROM device WHERE mgmt_address='%s' AND last_refresh IS NULL;" % bpmgmt, ifc=emsshifc) elif mode == 2: rows = SQL.query("SELECT * FROM device WHERE mgmt_address='%s' AND (last_refresh IS NULL OR refresh_failed_at IS NOT NULL);" % bpmgmt, ifc=emsshifc) else: rows = SQL.query("SELECT * FROM device WHERE mgmt_address='%s'" % bpmgmt, ifc=emsshifc) LOG.info("Found %d devices.", len(rows)) with EMInterface(**self.emicparams) as emicifc: emapi = emicifc.api for row in rows: if not int(row.is_local_em): LOG.info('Refresh %s', row.access_address) try: emapi.device.refresh_device(deviceIds=[row.uid]) except IControlTransportError, e: LOG.error(e)
def configure(self, options, noseconfig): """ Construct list of DUTs for which to collection system stats """ super(DutSystemStats, self).configure(options, noseconfig) from ...interfaces.config import ConfigInterface from ...interfaces.testcase import ContextHelper self.cfgifc = ConfigInterface() self.context = ContextHelper() self.data = self.context.set_container(PLUGIN_NAME) self.duts = options.get( 'duts', self.cfgifc.config.get('plugins', {}).get('_default_', {}).get('duts', DEFAULT_DUTS)) self.dutlist = [] for device in expand_devices(self.duts): self.dutlist.append({ 'name': device.get_alias(), 'ssh': SSHInterface(device=device), 'rest': self.context.get_icontrol_rest(device=device), 'results': [] }) # Check to see if jvm_diagnostics information is desired config = self.cfgifc.config.get('plugins', {}) if self.name in config: if 'jvm_diagnostics' in config[self.name]: jvm_diag = config[self.name]['jvm_diagnostics'] if 'fields' in jvm_diag: self.jvm_stats = jvm_diag['fields']
def traffic(self, ip): # generate traffic via Apache Bench LOG.info("Generating traffic via ab...") clients = self.get_config().get_devices( kind=F5D.KIND_CLOUD_VCENTER_CLIENT) for client in clients: with SSHInterface(device=client) as sshifc: wait_args( SCMD.ssh.generic, func_args=[PING % ip, sshifc], condition=lambda x: PING_ERROR not in x.stdout, progress_cb=lambda _: "Waiting until %s is ping-able" % ip, timeout=30) for i in range(TRAFFIC_ATTEMPTS): try: resp = SCMD.ssh.generic(TRAFFIC % ip, ifc=sshifc) self.assertFalse( resp.stderr, "Failed to generate traffic:\n %s" % resp) break except: if i >= TRAFFIC_ATTEMPTS - 1: raise
def setup(self): self._pre_stats = AttrDict() for device in self.devices: with SSHInterface(device=device) as ifc: ssh = ifc.api self._pre_stats[device] = ssh.stat(self.filename) return self
def reset_all(self): for device in [self.default] + self.peers: with SSHInterface(device=device) as sshifc: LOG.info('Wiping storage on {0}'.format(device)) SCMD.ssh.generic(SCMD.bigiq.ha.HA_WIPE_COMMAND, ifc=sshifc) with EmapiInterface(device=device) as rstifc: RCMD.system.wait_restjavad([self.default] + self.peers, ifc=rstifc)
def _initialize_big3d(self): LOG.info('Initializing big3d...') sshifc = SSHInterface(address=self.address, port=self.options.ssh_port) with sshifc: ssh = sshifc.api ssh.run('bigstart stop big3d;' 'rm -f /shared/bin/big3d;' 'test -f /usr/sbin/big3d.default && cp -f /usr/sbin/big3d.default /usr/sbin/big3d;' 'bigstart start big3d')
def setup(self): with SSHInterface(device=self.options.device, address=self.address, timeout=self.options.timeout, username=self.options.username, password=self.options.password, port=self.options.port) as ssh: ssh.api.exchange_key() ssh.api.run( 'test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys' )
def setup(self): # Set the admin and root usernames and passwords self.set_defaults() if self.options.image: title = 'Installing custom base image on %s' % self.address else: title = 'Installing %s %s on %s' % (self.options.product, self.options.pversion, self.address) LOG.info(title) filename, hfiso = self._find_iso() iso_version = cm.version_from_metadata(filename) if self.options.copy_only: self.copy_only(filename, hfiso) return #LOG.info('Disabling the 14.0+ password policy...') #RCMD.system.DisablePasswordPolicy(address=self.address, # port=self.options.ssl_port).run_wait(timeout=self.options.timeout) if self.options.format_partitions or self.options.format_volumes: with SSHInterface(address=self.address, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port) as sshifc: version = SCMD.ssh.get_version(ifc=sshifc) else: with IcontrolInterface(address=self.address, username=self.options.admin_username, password=self.options.admin_password, port=self.options.ssl_port) as icifc: version = ICMD.system.get_version(ifc=icifc) if (iso_version.product.is_bigip and iso_version >= 'bigip 10.0.0' or iso_version.product.is_em and iso_version >= 'em 2.0.0' or iso_version.product.is_bigiq or iso_version.product.is_iworkflow): if self.options.format_partitions or self.options.format_volumes or \ (version.product.is_bigip and version < 'bigip 10.0.0' or version.product.is_em and version < 'em 2.0.0'): ret = self.by_image2disk(filename, hfiso) else: ret = self.by_icontrol(filename, hfiso) elif (iso_version.product.is_bigip and iso_version < 'bigip 9.6.0' or iso_version.product.is_em and iso_version < 'em 2.0.0'): assert self.options.em_address, "--em-address is needed for legacy installations." ret = self.by_em_api(filename, hfiso) else: raise VersionNotSupported('%s is not supported' % iso_version) LOG.debug('done') return ret
def _wait_after_reboot(self, essential): if essential: ssh = SSHInterface(address=self.address, port=self.options.ssh_port) else: ssh = SSHInterface(address=self.address, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port) timeout = self.options.timeout try: SCMD.ssh.GetPrompt(ifc=ssh).\ run_wait(lambda x: x not in ('INOPERATIVE', '!'), timeout=timeout, timeout_message="Timeout ({0}s) waiting for a non-inoperative prompt.") SCMD.ssh.FileExists('/var/run/mcpd.pid', ifc=ssh).\ run_wait(lambda x: x, progress_cb=lambda x: 'mcpd not up...', timeout=timeout) SCMD.ssh.FileExists('/var/run/mprov.pid', ifc=ssh).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'mprov still running...', timeout=timeout) SCMD.ssh.FileExists('/var/run/grub.conf.lock', ifc=ssh).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'grub.lock still running...', timeout=timeout) version = SCMD.ssh.get_version(ifc=ssh) finally: ssh.close() return version
def _initialize_em(self): LOG.info('Initializing EM...') sshifc = SSHInterface(address=self.address, port=self.options.ssh_port) timeout = self.options.timeout with sshifc: ssh = sshifc.api ssh.run('bigstart stop;' '/etc/em/f5em_db_setup.sh;' 'rm -f /shared/em/mysql_shared_db/f5em_extern/*;' '/etc/em/f5em_extern_db_setup.sh;' 'bigstart start') SCMD.ssh.FileExists('/etc/em/.initial_boot', ifc=sshifc).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'EM still not initialized...', timeout=timeout)
def do_delete(self): LOG.info('* Delete mode *') with SSHInterface(**self.emparams) as emsshifc: bpmgmt = self.bigipparams.address devices = SQL.query("SELECT uid,access_address FROM device WHERE mgmt_address='%s';" % bpmgmt, ifc=emsshifc) clones = devices[1:] uids = [x.uid for x in clones] with EMInterface(**self.emicparams) as emicifc: emapi = emicifc.api LOG.info('Deleting devices...') try: emapi.device.delete_device(deviceIds=uids) #LOG.info('Enabling auto-refresh on EM...') #EMAPI.device.set_config(auto_refresh=True, ifc=emicifc) except ParsingError, e: LOG.warning(e)
def callback(): ret = AttrDict() self._post_stats = AttrDict() for device in self.devices: with SSHInterface(device=device) as ifc: ssh = ifc.api self._post_stats[device] = ssh.stat(self.filename) size_before = self._pre_stats[device].st_size size_after = self._post_stats[device].st_size delta = size_after - size_before LOG.debug('delta: %d', delta) resp = ssh.run('tail --bytes={0} {1}'.format(delta, self.filename)) ret[device] = resp.stdout return self.testcb(ret, self._post_stats)
def wait_after_reboot(essential, address, ssh_port, root_username, root_password, timeout, interval): """ Wait for the device to be in a valid state after rebooting. The reboot command is assumed to have already been sent. @param essential: If True, this is an essential config case. @param address: IP address of the device. @param ssh_port: Port to use for SSH communications. @param root_username: Name of the root user. @param root_password: Password for the root user. @param timeout: Timeout value (in seconds) to wait for the reboot to be complete. An exception will be thrown if all operations are not completed in this time. @param interval: The frequency (in seconds) of how often the status is checked. @return: The new version information of the device after rebooting. """ if essential: ssh = SSHInterface(address=address, port=ssh_port, username=ROOT_USERNAME, password=ROOT_PASSWORD) else: ssh = SSHInterface(address=address, username=root_username, password=root_password, port=ssh_port) try: SCMD.ssh.GetPrompt(ifc=ssh).\ run_wait(lambda x: x not in ('INOPERATIVE', '!'), timeout=timeout, interval=interval, timeout_message="Timeout ({0}s) waiting for a non-inoperative prompt.") SCMD.ssh.FileExists('/var/run/mcpd.pid', ifc=ssh).\ run_wait(lambda x: x, interval=interval, progress_cb=lambda x: 'mcpd not up...', timeout=timeout) SCMD.ssh.FileExists('/var/run/mprov.pid', ifc=ssh).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'mprov still running...', timeout=timeout, interval=interval) SCMD.ssh.FileExists('/var/run/grub.conf.lock', ifc=ssh).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'grub.lock still running...', timeout=timeout, interval=interval) version = SCMD.ssh.get_version(ifc=ssh) finally: ssh.close() return version
def setup_ha(self): # Setting HA communication address for device in [self.default] + self.peers: LOG.info("Setting up HA Communication: {}".format(device)) with EmapiInterface(device=device) as rstifc: self_addr = netaddr.IPAddress(device.get_discover_address()) payload = NetworkDiscover() payload.discoveryAddress = self_addr.format(netaddr. ipv6_full) rstifc.api.put(NetworkDiscover.URI, payload=payload) with EmapiInterface(device=self.default) as rstifc: if self.options.ha_passive: LOG.info("Setting up Active/Passive HA") RCMD.system.setup_ha(self.peers, ifc=rstifc) else: LOG.info("Setting up Active/Active HA") RCMD.cloud.setup_ha(self.peers, ifc=rstifc) with SSHInterface(device=self.default) as sshifc: SCMD.bigiq.ha.wait_ha([self.default] + self.peers, timeout=self.options.timeout, ifc=sshifc)
def _wait_after_reboot(self, essential, iso_version): """ Wait until after the reboot is complete. """ if essential: ssh = SSHInterface(address=self.address, port=self.options.ssh_port, username=ROOT_USERNAME, password=ROOT_PASSWORD) else: ssh = SSHInterface(address=self.address, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port) timeout = self.options.timeout if essential and iso_version.product.is_bigip and iso_version >= 'bigip 14.0.0': LOG.info('Disabling the 14.0+ password policy...') #SCMD.ssh.DisablePasswordPolicy(address=self.address, # port=self.options.ssl_port).run_wait(timeout=timeout) # XXX: This is via REST not SSH! RCMD.system.DisablePasswordPolicy( address=self.address, port=self.options.ssl_port).run_wait(timeout=timeout) try: SCMD.ssh.GetPrompt(ifc=ssh).\ run_wait(lambda x: x not in ('INOPERATIVE', '!'), timeout=timeout, timeout_message="Timeout ({0}s) waiting for a non-inoperative prompt.", interval=15) SCMD.ssh.FileExists('/var/run/mcpd.pid', ifc=ssh).\ run_wait(lambda x: x, progress_cb=lambda x: 'mcpd not up...', timeout=timeout, interval=15) SCMD.ssh.FileExists('/var/run/mprov.pid', ifc=ssh).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'mprov still running...', timeout=timeout, interval=15) SCMD.ssh.FileExists('/var/run/grub.conf.lock', ifc=ssh).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'grub.lock still running...', timeout=timeout, interval=15) version = SCMD.ssh.get_version(ifc=ssh) finally: ssh.close() return version
def setup(self): mgmtips = [] for device in self.options.devices: if isinstance(device, str): mgmtips.append(device) elif isinstance(device, DeviceAccess): mgmtips.append(device.address) else: raise ValueError(device) assert mgmtips, 'No devices discovered on EM?' with SSHInterface(**self.sshparams) as sshifc: with EMInterface(**self.icparams) as emifc: emapi = emifc.api statuses = EMSQL.device.get_device_state(mgmtips, ifc=sshifc) LOG.debug(statuses) uids = [] for status in statuses: if status.status in ('big3d_below_minimum', 'big3d_update_required') \ and status.refresh_failed_at is None: uids.append(status.uid) if uids: LOG.info('Big3d install on device_uids: %s', uids) ret = emapi.big3d_install.big3dInstallCreate(self.options.task_name, uids, 'true', 'true') job = int(ret['jobUid']) task = EMSQL.device.GetBig3dTask(job, ifc=sshifc) \ .run_wait(lambda x: x['status'] != 'started', timeout=300, progress_cb=lambda x:'big3d install: %d%%' % x.progress_percent) assert task.error_count == 0, 'Errors in big3d task: %s' % task return task else: LOG.info('No devices need big3d install')
def prep(self): self.sshifc = SSHInterface(**self.sshparams) self.api = self.sshifc.open()
class ConfigPlacer(Macro): SystemConfig = SystemConfig NetworkConfig = NetworkConfig LTMConfig = LTMConfig def __init__(self, options, address=None, *args, **kwargs): self.context = O() self.options = O(options) self.options.setifnone('node_count', DEFAULT_NODES) self.options.setifnone('pool_count', DEFAULT_POOLS) self.options.setifnone('pool_members', DEFAULT_MEMBERS) self.options.setifnone('vip_count', DEFAULT_VIPS) self.options.setifnone('node_start', DEFAULT_NODE_START) self.options.setifnone('partitions', DEFAULT_PARTITIONS) self.options.setifnone('timeout', DEFAULT_TIMEOUT) if self.options.device: self.device = ConfigInterface().get_device(options.device) self.address = self.device.address else: self.device = None self.address = address self.options.setifnone('username', DEFAULT_ROOT_USERNAME) self.options.setifnone('password', DEFAULT_ROOT_PASSWORD) # can.* shortcuts to check for certain features based on the version self.can = O() def can_tmsh(v): return (v.product.is_bigip and v >= 'bigip 11.0.0' or v.product.is_em and v >= 'em 2.0.0' or v.product.is_bigiq) self.can.tmsh = can_tmsh def can_provision(v): return (v.product.is_bigip and v >= 'bigip 10.0.1' or v.product.is_em and v >= 'em 2.0.0' or v.product.is_bigiq) self.can.provision = can_provision def can_lvm(sshifc): return not sshifc('/usr/lib/install/lvmtest').status self.can.lvm = can_lvm self.has = O() def has_asm(s): return bool(SCMD.tmsh.get_provision(ifc=s).asm) self.has.asm = has_asm def has_lvm(s): return not s('/usr/lib/install/lvmtest').status self.has.lvm = has_lvm super(ConfigPlacer, self).__init__(*args, **kwargs) def csv_provider(self, mgmtip): """ Get the static data for a device with a given mgmtip from a CSV file. """ venv_path = os.environ['VIRTUAL_ENV'] csv_location = venv_path + self.options.csv data = O() with open(csv_location, 'rb') as csvfile: reader = csv.DictReader(csvfile) device_row = None for row in reader: # Clean up whitespaces row = {x.strip(): y.strip() for x, y in row.iteritems()} if IPAddress(mgmtip) == IPNetwork(row['mgmtip']).ip: device_row = row break if device_row: if 'hostname' in device_row and device_row['hostname']: data.hostname = device_row['hostname'] data.licenses = {} if 'reg_key' in device_row and device_row['reg_key']: data.licenses.reg_key = [device_row['reg_key']] data.selfip = {} if 'internal' in device_row and device_row['internal']: data.selfip.internal = IPNetwork(device_row['internal']) if 'external' in device_row and device_row['external']: data.selfip.external = IPNetwork(device_row['external']) if 'gateway' in device_row and device_row['gateway']: data.gateway = IPAddress(device_row['gateway']) data.mgmtip = IPNetwork(device_row['mgmtip']) else: LOG.warning("No devices with mgmtip=%s found in CSV." % mgmtip) LOG.debug(data) return data def irack_provider(self, address, username, apikey, mgmtip, timeout=120): """Get the static data for a device with a given mgmtip from iRack.""" data = O() with IrackInterface(address=address, timeout=timeout, username=username, password=apikey, proto='http') as irack: params = dict(address_set__address__in=mgmtip, address_set__type=4) # Locate the static bag for the F5Asset with mgmtip ret = irack.api.staticbag.filter(asset__type=1, **params) if ret.data.meta.total_count == 0: LOG.warning("No devices with mgmtip=%s found in iRack." % mgmtip) return data if ret.data.meta.total_count > 1: raise ValueError("More than one device with mgmtip=%s found in iRack." % mgmtip) bag = ret.data.objects[0] bagid = bag['id'] # Get the hostname ret = irack.api.staticsystem.filter(bag=bagid) assert ret.data.meta.total_count == 1, "No StaticSystem entries for bagid=%s" % bagid data.hostname = ret.data.objects[0].hostname # Get all reg_keys ret = irack.api.staticlicense.filter(bag=bagid) assert ret.data.meta.total_count >= 1, "No StaticLicense entries for bagid=%s" % bagid data.licenses = {} data.licenses.reg_key = [x.reg_key for x in ret.data.objects] # Get all VLAN -> self IPs pairs ret = irack.api.staticaddress.filter(bag=bagid, type=1) data.selfip = {} for o in ret.data.objects: vlan = o.vlan.split('/')[-1] # TODO: IPv6 support...someday? if IPAddress(o.address).version == 4 \ and not int(o.floating): data.selfip[vlan] = IPNetwork("{0.address}/{0.netmask}".format(o)) # Get all mgmt ips ret = irack.api.staticaddress.filter(bag=bagid, type=0) assert ret.data.meta.total_count >= 1, "No StaticAddress entries for bagid=%s" % bagid data.mgmtip = {} for o in ret.data.objects: if IPAddress(o.address).version == 4: data.mgmtip = IPNetwork("{0.address}/{0.netmask}".format(o)) # GW ret = irack.api.staticaddress.filter(bag=bagid, type=3) assert ret.data.meta.total_count >= 1, "No StaticAddress entries for bagid=%s" % bagid for o in ret.data.objects: if IPAddress(o.address).version == 4: data.gateway = IPAddress(o.address) LOG.debug(data) return data def make_context(self): ctx = self.context version_data = SCMD.ssh.get_version(ifc=self.sshifc) ctx.version = version_data ctx.project = SCMD.ssh.parse_keyvalue_file('/VERSION', ifc=self.sshifc).get('project') ctx.platform = SCMD.ssh.get_platform(ifc=self.sshifc)['platform'] ctx.is_cluster = SCMD.ssh.is_cluster(ifc=self.sshifc) ctx.is_vcmp = ctx.platform == 'Z101' ctx.status = SCMD.ssh.GetPrompt(ifc=self.sshifc)\ .run_wait(lambda x: x not in ('INOPERATIVE',), progress_cb=lambda x: 'Still %s...' % x, timeout_message="Timeout ({0}s) waiting for an non-inoperative prompt.", timeout=self.options.timeout) if self.can.provision(ctx.version): ctx.provision = {} modules = SCMD.tmsh.get_provision(ifc=self.sshifc) for k, v in modules.iteritems(): if v: ctx.provision[k] = v['level'] try: tokens = SCMD.ssh.parse_license(ifc=self.sshifc, tokens_only=True) ctx.modules = dict([(k[4:], v) for (k, v) in tokens.items() if k.startswith('mod_')]) ctx.features = dict([(k, v) for (k, v) in tokens.items() if not k.startswith('mod_')]) except SCMD.ssh.LicenseParsingError as e: ctx.modules = {} ctx.features = {} LOG.debug(e) LOG.warning('License file parsing failed. Harmless if this is a clean box.') LOG.info('Version: {0.product.to_tmos} {0.version} {0.build}'.format(ctx.version)) LOG.info('Platform: %s', ctx.platform) LOG.info('Licensed modules: %s', ', '.join(sorted(ctx.modules))) if self.can.provision(ctx.version): LOG.info('Current provisioning: %s', ', '.join(sorted(ctx.provision))) return ctx def prep(self): self.sshifc = SSHInterface(device=self.device, address=self.address, username=self.options.username, password=self.options.password, timeout=self.options.timeout, port=self.options.ssh_port) self.sshifc.open() def set_networking(self, o): ctx = self.context if self.options.mgmtip: o.mgmtip = IPNetwork(self.options.mgmtip) if o.mgmtip.prefixlen == 32: o.mgmtip.prefixlen = 24 LOG.warning('Assuming /24 to the management IP.') elif ctx.is_cluster: ret = SCMD.tmsh.list('sys cluster default', ifc=self.sshifc) o.mgmtip = IPNetwork(ret['sys cluster default']['address']) ret = SCMD.tmsh.list('sys management-route', ifc=self.sshifc) # Sometimes it's defined as "default" some other times it's "0.0.0.0" if ret: o.gateway = IPAddress(ret.values()[0]['gateway']) else: # Try to find the existing IP configuration on management interface. ret = self.sshifc("ethconfig --getcurrent") bits = ret.stdout.split() o.mgmtip = IPNetwork("{0[0]}/{0[1]}".format(bits)) o.gateway = IPAddress(bits[2]) # If None or IPAddress('0.0.0.0') if not o.gateway: o.gateway = IPAddress(self.options.mgmtgw) if self.options.mgmtgw \ else o.mgmtip.broadcast - 1 # Preserve the DHCP flag for the management interface if self.can.tmsh(ctx.version): ret = SCMD.tmsh.list('sys db dhclient.mgmt', ifc=self.sshifc) o.dhcp = ret['sys db dhclient.mgmt']['value'] == 'enable' def set_provisioning(self, o): ctx = self.context if not self.can.provision(ctx.version): LOG.info('Provision not supported on the target') return o.provision = {} provision = [] if self.options.get('provision'): for prov in self.options['provision'].split(','): if prov: bits = prov.lower().split(':', 1) if len(bits) > 1: module, level = bits assert level in ('minimum', 'nominal', 'dedicated') else: module = bits[0].strip() level = 'nominal' if module == 'ltm' else 'minimum' provision.append((module, level)) if provision: v = ctx.version for module, level in provision: if module == 'afm' and not (v.product.is_bigip and v >= 'bigip 11.3.0'): LOG.warning('AFM cannot be provisioned on this target.') continue o.provision[module] = level else: # Preserve provisioning. modules = SCMD.tmsh.get_provision(ifc=self.sshifc) for module, level_kv in modules.iteritems(): if level_kv: level = level_kv['level'] o.provision[module] = level def set_users(self, o): ctx = self.context v = ctx.version o.users = {} o.users[OUR_ADMIN_USERNAME] = 'admin' if v.product.is_bigip or v.product.is_em: o.users.g = 'guest' o.users.o = 'operator' o.users.ae = 'application-editor' o.users.m = 'manager' o.users.um = 'user-manager' o.users.ra = 'resource-admin' if v.product.is_bigip and v >= 'bigip 11.3.0': o.users.cm = 'certificate-manager' o.users.im = 'irule-manager' o.users.au = 'auditor' o.users.asa = 'web-application-security-administrator' o.users.ase = 'web-application-security-editor' o.users.fm = 'firewall-manager' if v.product.is_bigiq: o.users.g = 'guest' o.users.fw = 'firewall-manager' def set_vlans(self, o): ctx = self.context lacp = self.options.trunks_lacp if ctx.platform.startswith('A'): o.trunks = {} eth1 = 'internal' # Trunk names eth2 = 'external' elif ctx.platform.startswith('Z'): # VEs can may have a variable number of TMM interfaces. # Adjusting accordingly. ret = SCMD.tmsh.list('net interface', ifc=self.sshifc) interfaces = sorted([x.split()[2] for x in ret.keys() if x.split()[2] != 'mgmt']) eth1, eth2 = (interfaces + [None, None])[:2] else: eth1 = '1.1' eth2 = '1.2' o.vlans = {} if ctx.is_vcmp: # No trunks. They will be automatically generated on VCMP guests. eth1 = eth2 = None ret = SCMD.tmsh.list('net vlan', ifc=self.sshifc) for key, obj in ret.items(): s = Mirror(key, obj) o.vlans[s.name] = s if ctx.platform == 'A100': o.trunks.internal = O(interfaces=['1/2.1', '2/2.1', '3/2.1', '4/2.1'], lacp=lacp) o.trunks.external = O(interfaces=['1/2.2', '2/2.2', '3/2.2', '4/2.2'], lacp=lacp) elif ctx.platform in ('A107', 'A109', 'A111'): o.trunks.internal = O(interfaces=['1/1.1', '2/1.1', '3/1.1', '4/1.1'], lacp=lacp) o.trunks.external = O(interfaces=['1/1.2', '2/1.2', '3/1.2', '4/1.2'], lacp=lacp) elif ctx.platform == 'A108': o.trunks.internal = O(interfaces=['1/1.1', '2/1.1', '3/1.1', '4/1.1', '5/1.1', '6/1.1', '7/1.1', '8/1.1'], lacp=lacp) o.trunks.external = O(interfaces=['1/1.2', '2/1.2', '3/1.2', '4/1.2', '5/1.2', '6/1.2', '7/1.2', '8/1.2'], lacp=lacp) def parse_vlan_options(kind): specs = O(tagged=[], untagged=[]) if kind == 'internal': tags = self.options.vlan_internal if not tags: specs.untagged.append(eth1) if kind == 'external': tags = self.options.vlan_external if not tags: specs.untagged.append(eth2) if tags: for tag in re.split('\s+', tags): key, value = tag.split('=') if key == 'tag': specs.tag = value else: specs[key].append(value) return specs if eth1: o.vlans.internal = O(**parse_vlan_options('internal')) if eth2: o.vlans.external = O(**parse_vlan_options('external')) # def fix_cmi(self): # """ # The main goal of this method is to empty all device groups, otherwise # the config load will fail. There should be a better way of doing this. # """ # ctx = self.context # if self.can.tmsh(ctx.version): # LOG.debug('Emptying CM device groups...') # ret = SCMD.tmsh.list('cm device-group', ifc=self.sshifc) # for key, value in ret.iteritems(): # devices = value.get('devices') # if devices: # ret = self.sshifc('tmsh modify %s devices delete { %s }' % # (key, ' '.join(devices.keys()))) # if ret.status: # LOG.warning(ret) def dump(self, tree, ctx, func=None): f = sys.stdout # @UndefinedVariable LOG.info('Rendering configuration file...') f.write(HEADER) tree.render(stream=f, func=func) def load(self, tree, ctx, func=None): with self.sshifc.api.sftp().open(SCF_FILENAME, 'w') as f: LOG.info('Rendering configuration file...') f.write(HEADER) tree.render(stream=f, func=func) if self.can.tmsh(ctx.version): if self.options.verify: LOG.info('Verifying configuration...') ret = self.sshifc('tmsh load sys config file %s verify' % SCF_FILENAME) LOG.info(ret.stdout) else: LOG.info('Loading configuration...') SCMD.ssh.generic('tmsh load sys config file %s' % SCF_FILENAME, ifc=self.sshifc) else: LOG.info('Loading configuration...') SCMD.ssh.generic('b import %s' % SCF_FILENAME, ifc=self.sshifc) def save(self, ctx): LOG.info('Saving configuration...') if self.can.tmsh(ctx.version): SCMD.ssh.generic('tmsh save sys config partitions all', ifc=self.sshifc) else: SCMD.ssh.generic('b save all', ifc=self.sshifc) def reset_trust(self): v = self.context.version if v.product.is_bigiq and v > 'bigiq 4.0': return if self.can.tmsh(v): LOG.info('Resetting trust...') SCMD.ssh.Generic('tmsh delete cm trust-domain all', ifc=self.sshifc).\ run_wait(lambda x: x.status == 0, progress_cb=lambda x: 'delete trust-domain retry...', timeout=self.options.timeout) def ready_wait(self): ctx = self.context if self.options.get('dry_run'): return LOG.info('Waiting for reconfiguration...') timeout = self.options.timeout SCMD.ssh.FileExists('/var/run/mprov.pid', ifc=self.sshifc)\ .run_wait(lambda x: x is False, progress_cb=lambda x: 'mprov still running...', timeout=timeout) if (self.can.provision(ctx.version) and self.has.asm(self.sshifc) and self.has.lvm(self.sshifc)): SCMD.ssh.FileExists('/var/lib/mysql/.moved.to.asmdbvol', ifc=self.sshifc)\ .run_wait(lambda x: x, progress_cb=lambda x: 'ASWADB still not there...', timeout=timeout) # Wait for ASM config server to come up. if self.can.provision(ctx.version) and self.has.asm(self.sshifc): LOG.info('Waiting for ASM config server to come up...') SCMD.ssh.Generic(r'netstat -anp|grep -P ":9781\s+(0\.0\.0\.0|::):\*\s+LISTEN"|wc -l', ifc=self.sshifc).run_wait(lambda x: int(x.stdout), progress_cb=lambda x: 'ASM cfg server not up...', timeout=timeout) LOG.info('Waiting for Active prompt...') s = SCMD.ssh.GetPrompt(ifc=self.sshifc)\ .run_wait(lambda x: x in ('Active', 'Standby', 'ForcedOffline', 'RESTART DAEMONS', 'REBOOT REQUIRED'), progress_cb=lambda x: 'Still not active (%s)...' % x, timeout_message="Timeout ({0}s) waiting for an 'Active' prompt.", timeout=timeout) if s == 'RESTART DAEMONS': LOG.info('Restarting daemons...') self.call('bigstart restart') SCMD.ssh.GetPrompt(ifc=self.sshifc)\ .run_wait(lambda x: x in ('Active', 'Standby', 'ForcedOffline'), progress_cb=lambda x: 'Still not active (%s)...' % x, timeout=timeout) elif s == 'REBOOT REQUIRED': LOG.warn('A manual reboot is required.') if SCMD.ssh.file_exists('/var/run/grub.conf.lock', ifc=self.sshifc): self.ssh.api.remove('/var/run/grub.conf.lock') def ssh_key_exchange(self): if self.options.get('no_sshkey'): return o = O() o.device = self.device o.username = self.options.username o.password = self.options.password o.port = self.options.ssh_port cs = KeySwap(o, address=self.address) cs.run() def ssl_signedcert_install(self, hostname): # BUG: Sometimes user 'a' role gets downgraded to 'guest' at this point. # It's still unclear why this happens. if self.context.version >= 'bigip 11.6' or self.context.version >= 'bigiq 4.6': SCMD.ssh.generic('tmsh modify auth user %s partition-access modify {all-partitions {role admin}}' % OUR_ADMIN_USERNAME, ifc=self.sshifc) else: SCMD.ssh.generic('tmsh modify auth user %s role admin' % OUR_ADMIN_USERNAME, ifc=self.sshifc) o = O() o.admin_username = OUR_ADMIN_USERNAME o.root_username = self.options.username o.ssh_port = self.options.ssh_port o.ssl_port = self.options.ssl_port o.verbose = self.options.verbose o.timeout = self.options.timeout LOG.info('Pushing the key/certificate pair') # The special a:a admin user is used here. It should be included in the # remote_config yaml config in the users.administrator section. o.admin_password = OUR_ADMIN_PASSWORD o.root_password = self.options.password if hostname: o.alias = [hostname] cs = WebCert(o, address=self.address) cs.run() def bigiq_special_selfip_handling(self, tree, ctx): "Because the use of tmsh has become frown upon starting with 4.2" v = ctx.version if v.product.is_bigiq and v >= 'bigiq 4.2.0': # XXX: Because sometimes user 'a' set via tmsh is not picked up. self.call('bigstart restart restjavad') with EmapiInterface(username=OUR_ADMIN_USERNAME, password=OUR_ADMIN_PASSWORD, port=self.options.ssl_port, address=self.address) as rstifc: payload = O(selfIpAddresses=[]) for self_ip in enumerate_stamps(tree, SelfIP, include_common=False): # XXX: BIGIQ 4.2 API only supports untagged. for iface in self_ip.vlan.untagged: payload.selfIpAddresses.append(O(address=str(self_ip.address), vlan=self_ip.vlan.name, iface=iface)) LOG.debug('EasySetup payload: %s', payload) wait_args(rstifc.api.patch, func_args=(EasySetup.URI, payload), timeout=180, interval=5, timeout_message="Can't patch selfIPs ready after {0}s") # def bigiq_special_selfip_handling(self, tree, ctx): # "Because the use of tmsh has become frown upon starting with 4.2" # v = ctx.version # if v.product.is_bigiq and v >= 'bigiq 4.2.0': # # XXX: Because sometimes user 'a' set via tmsh is not picked up. # self.call('bigstart restart restjavad') # with EmapiInterface(username=OUR_ADMIN_USERNAME, # password=OUR_ADMIN_PASSWORD, # port=self.options.ssl_port, # address=self.address) as rstifc: # LOG.info('Waiting for restjavad to come up...') # vlans = wait_args(rstifc.api.get, func_args=(NetworkVlan.URI,), # timeout=60, interval=5, # timeout_message="Can't get VLANs in {0}s") # vlans_by_name = dict(((x.name, x) for x in vlans['items'])) # for self_ip in enumerate_stamps(tree, SelfIP, include_common=False): # # XXX: BIGIQ 4.2 API only supports untagged. # # And only one interface per VLAN. # payload = NetworkSelfip() # payload.name = self_ip.name # payload.address = str(self_ip.address) # payload.vlanReference = vlans_by_name[self_ip.vlan.name] # rstifc.api.post(NetworkSelfip.URI, payload=payload) def calculate_vip_start(self, vip_start, mgmtip, selfip_external): if not selfip_external and not vip_start: LOG.info('Skipping auto VIP generation.') return if selfip_external: if not isinstance(selfip_external, IPNetwork): selfip_external = IPNetwork(selfip_external) if selfip_external.prefixlen == 32: LOG.info('Self IP external has no prefix. Assuming /16.') selfip_external.prefixlen = 16 if vip_start is None: # '1.1.1.1/24' -> '1.1.1.0/24' cidr = "{0.network}/{0.prefixlen}".format(mgmtip) host_id = mgmtip.ip.value - mgmtip.network.value subnet_index = SUBNET_MAP.get(cidr) if subnet_index is None: LOG.warning('The %s subnet was not found! ' 'Skipping Virtual Servers.' % cidr) return # Start from 10.11.50.0 - 10.11.147.240 (planned for 10 subnets, 8 VIPs each) offset = 1 + 50 * 256 + DEFAULT_VIPS * (256 * subnet_index + host_id) vip_start = selfip_external.network + offset else: vip_start = IPAddress(vip_start) return vip_start def call(self, *args, **kwargs): ret = SCMD.ssh.generic(command=args[0], ifc=self.sshifc) if ret and ret.status: LOG.warn(ret) else: LOG.debug(ret) return ret def license(self, ctx, regkey): if ctx.status == 'NO LICENSE' and not regkey: raise Exception('The box needs to be relicensed first.' 'Provide --license <regkey>') # Status could be '' in some situations when the license is invalid. if regkey and (ctx.status in ['LICENSE INOPERATIVE', 'NO LICENSE', ''] or self.options.get('force_license')): LOG.info('Licensing...') self.call('SOAPLicenseClient --verbose --basekey %s' % regkey) # We need to re-set the modules based on the new license tokens = SCMD.ssh.parse_license(ifc=self.sshifc, tokens_only=True) ctx.modules = dict([(k[4:], v) for (k, v) in tokens.items() if k.startswith('mod_')]) ctx.features = dict([(k, v) for (k, v) in tokens.items() if not k.startswith('mod_')]) # Tomcat doesn't like the initial licensing through CLI if ctx.version.product.is_em: ret = self.call('bigstart restart tomcat') elif ctx.status in ['LICENSE EXPIRED', 'REACTIVATE LICENSE']: LOG.info('Re-Licensing...') ret = self.call('SOAPLicenseClient --verbose --basekey `grep ' '"Registration Key" /config/bigip.license|' 'cut -d: -f2`') LOG.debug("SOAPLicenseClient returned: %s", ret) else: LOG.info('Skipping licensing.') def load_default_config(self): ctx = self.context if self.can.tmsh(ctx.version): LOG.info('Importing default config...') self.call('tmsh load sys config default') else: LOG.info('Importing default config...') self.call('b import default') if ctx.version.product.is_bigiq: self.call(WIPE_STORAGE) def setup(self): provider = O() if not self.options.no_irack and not self.options.csv: LOG.info("Using data from iRack") provider = self.irack_provider(address=self.options.irack_address, username=self.options.irack_username, apikey=self.options.irack_apikey, mgmtip=self.address, timeout=self.options.timeout) elif self.options.csv: LOG.info("Using data from CSV: %s" % self.options.csv) provider = self.csv_provider(mgmtip=self.address) ctx = self.make_context() # System o = O() o.partitions = self.options.partitions o.nameservers = DNS_SERVERS if self.options.dns_servers is None else \ self.options.dns_servers o.suffixes = DNS_SUFFIXES if self.options.dns_suffixes is None else \ self.options.dns_suffixes o.ntpservers = NTP_SERVERS if self.options.ntp_servers is None else \ self.options.ntp_servers o.smtpserver = 'mail.f5net.com' o.hostname = self.options.hostname or provider.get('hostname') o.timezone = self.options.timezone self.set_networking(o) self.set_provisioning(o) self.set_users(o) if provider.mgmtip and (o.mgmtip.ip != provider.mgmtip.ip or o.mgmtip.cidr != provider.mgmtip.cidr): LOG.warning('Management address mismatch. iRack/CSV has {0} but found {1}. iRack/CSV will take precedence.'.format(provider.mgmtip, o.mgmtip)) o.mgmtip = provider.mgmtip mgmtip = o.mgmtip if provider.gateway and o.gateway != provider.gateway: LOG.warning('Default gateway address mismatch. iRack/CSV has {0} but found {1}. iRack/CSV will take precedence.'.format(provider.gateway, o.gateway)) o.gateway = provider.gateway tree = self.SystemConfig(self.context, **o).run() if self.options.clean: self.load_default_config() if not self.options.stdout: self.load(tree, ctx, func=lambda x: not isinstance(x, Partition)) if not self.options.stdout: # XXX: Add any given DNS before attempting to relicense. # Licensing may need to resolve the license server hostname. if self.can.tmsh(ctx.version): self.call('tmsh modify sys dns name-servers add { %s }' % ' '.join(o.nameservers)) self.license(ctx, self.options.license or provider and provider.licenses.reg_key[0]) if self.options.clean: self.reset_trust() return # Network o = O() o.tree = tree self.set_vlans(o) o.selfips = {} selfip_internal = self.options.selfip_internal or provider and provider.selfip.internal selfip_external = self.options.selfip_external or provider and provider.selfip.external if selfip_internal: o.selfips.internal = [O(address=selfip_internal)] # o.selfips.internal.append(O(address=ip4to6(selfip_internal), name='int_6')) o.selfips.internal.append(O(address=ip4to6(selfip_internal))) if selfip_external: o.selfips.external = [O(address=selfip_external)] # o.selfips.external.append(O(address=ip4to6(selfip_external), name='ext_6')) o.selfips.external.append(O(address=ip4to6(selfip_external))) tree = self.NetworkConfig(self.context, **o).run() # LTM o = O() o.tree = tree o.nodes = self.options.node_count o.pools = self.options.pool_count o.members = self.options.pool_members o.vips = self.options.vip_count o.node1 = self.options.node_start o.vip1 = self.calculate_vip_start(self.options.vip_start, mgmtip, selfip_external) o.with_monitors = not self.options.no_mon tree = self.LTMConfig(self.context, **o).run() if self.options.stdout: self.dump(tree, ctx) return self.load(tree, ctx) self.reset_trust() self.ready_wait() self.save(ctx) self.ssh_key_exchange() self.ssl_signedcert_install(o.hostname) self.bigiq_special_selfip_handling(tree, ctx) def cleanup(self): self.sshifc.close()
def push_certificate(self, pkey, cert): icifc = IcontrolInterface(device=self.options.device, address=self.address, username=self.options.admin_username, password=self.options.admin_password, port=self.options.ssl_port, debug=self.options.verbose) ic = icifc.open() key_pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey) cert_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) try: ic.Management.KeyCertificate.certificate_delete( mode='MANAGEMENT_MODE_WEBSERVER', cert_ids=['server']) ic.Management.KeyCertificate.key_delete( mode='MANAGEMENT_MODE_WEBSERVER', key_ids=['server']) except: LOG.warning('Exception occurred while deleting cert/key') ic.Management.KeyCertificate.certificate_import_from_pem( mode='MANAGEMENT_MODE_WEBSERVER', cert_ids=['server'], pem_data=[cert_pem], overwrite=1) ic.Management.KeyCertificate.key_import_from_pem( mode='MANAGEMENT_MODE_WEBSERVER', key_ids=['server'], pem_data=[key_pem], overwrite=1) icifc.close() # XXX: Unfortunately we can't reinit httpd through iControl. It's a KI # http://devcentral.f5.com/Default.aspx?tabid=53&forumid=1&postid=1170498&view=topic # # action = pc.System.Services.typefactory.\ # create('System.Services.ServiceAction').\ # SERVICE_ACTION_REINIT # service = pc.System.Services.typefactory.\ # create('System.Services.ServiceType').\ # SERVICE_HTTPD # pc.System.Services.set_service(services = [service], \ # service_action = action) # pc.System.Services.get_service_status([service]) with SSHInterface(device=self.options.device, address=self.address, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port) as sshifc: version = SCMD.ssh.get_version(ifc=sshifc) if version >= 'bigiq 4.4' or version < 'bigiq 4.0' or \ version >= 'iworkflow 2.0': sshifc.api.run('bigstart reinit webd') elif version >= 'bigiq 4.3' and version < 'bigiq 4.4': sshifc.api.run('bigstart reinit nginx') else: # all BPs if version < 'bigip 11.5.0': x = sshifc.api.run('bigstart reinit httpd') LOG.debug("pushcert (res: bigstart reinit httpd): {0}".format(x)) else: # See BZ553787 (Matt Davey: restjavad must be restarted after pushing a cert) # was for > 12.0 only but the "fix" made it to all new HFs LOG.debug("pushcert (res: bigstart restart)...(because of issue with restajavad and/or httpd on certain platforms)") # sshifc.api.run('bigstart restart') sshifc.api.run('bigstart restart httpd') self.wait_for_available(version) return True
def prep(self): self.sshifc = SSHInterface(**self.sshparams) self.api = self.sshifc.open() self.api.run('rm -rf {0} && mkdir -p {0}'.format(UCS_TMP))
def prep(self): if not self.options.conf: self.sshifc = SSHInterface(**self.sshparams) self.api = self.sshifc.open() else: self.sshifc = None
class Extractor(Macro): def __init__(self, options, address=None, params=None): self.options = Options(options) self.params = ' '.join(params or []) self.sshparams = Options(device=self.options.device, address=address, timeout=self.options.timeout, username=self.options.username, password=self.options.password) super(Extractor, self).__init__() def prep(self): if not self.options.conf: self.sshifc = SSHInterface(**self.sshparams) self.api = self.sshifc.open() else: self.sshifc = None def cleanup(self): if self.sshifc: self.sshifc.close() def dump_config(self, filename): LOG.info('Dumping configuration...') if self.options.ucs: LOG.info('Extracting ucs %s...', self.options.ucs) self.api.run('mkdir -p /var/local/ucs/tmp') self.api.run('cd /var/local/ucs/tmp && tar xf ../{}'.format(self.options.ucs)) self.api.run('cp /var/local/ucs/tmp/config/bigip.conf {}'.format(filename)) self.api.run('rm -rf /var/local/ucs/tmp') text = self.api.run('cat {}'.format(filename)).stdout elif self.options.conf: with open(self.options.conf) as f: text = f.read() else: if self.sshifc.version > 'bigip 12.1.0': text = self.api.run('tmsh save sys config file {0} no-passphrase && cat {0}'.format(filename)).stdout else: text = self.api.run('tmsh save sys config file {0} && cat {0}'.format(filename)).stdout return text def setup(self): output = SCF_OUTPUT if self.options.cache: if self.api.exists(output): text = self.api.run('cat {0}'.format(output)).stdout else: text = self.dump_config(output) else: text = self.dump_config(output) if self.sshifc and not self.options.cache: self.api.run('rm -f {0}*'.format(output)).stdout LOG.info('Parsing...') config = tmsh.parser(text) LOG.debug(config) all_keys = list(config.keys()) LOG.info('Last key: %s', all_keys[-1]) all_ids = {} for x in all_keys: k = shlex.split(x)[-1] if k in all_ids: all_ids[k].append(config.glob(x)) else: all_ids[k] = [config.glob(x)] vip = config.match("^{}$".format(self.params)) if not vip: raise Exception('No objects found matching "%s"' % self.params) def rec2(root, deps=OrderedDict()): deps.update(root) def guess_key(k, d): this_id = re.split('[:]', k, 1)[0] if not this_id.startswith('/'): this_id = '/Common/%s' % this_id if k.startswith('/'): folder = k.rsplit('/', 1)[0] if folder in all_ids: for x in all_ids[folder]: d.update(x) if this_id in all_ids: for x in all_ids[this_id]: d.update(x) # ipv6.port if re.search(':.*\.[^\.]*$', k): this_id = re.split('[\.]', k, 1)[0] this_id = '/Common/%s' % this_id if this_id in all_ids: for x in all_ids[this_id]: d.update(x) def rec(root, deps=None): if deps is None: deps = {} if isinstance(root, dict): for k, v in root.items(): guess_key(k, deps) rec(v, deps) elif isinstance(root, (set, list, tuple)): for v in root: rec(v, deps) else: root = str(root) assert isinstance(root, str), root guess_key(root, deps) return deps d = rec(root) if d: # Try to avoid circular dependencies list(map(lambda x: d.pop(x), [x for x in d if x in deps])) deps.update(d) rec2(d, deps) return deps ret = rec2(vip) # Sort keys if self.options.sort: ret = tmsh.GlobDict(sorted(iter(ret.items()), key=lambda x: x[0])) return tmsh.dumps(ret)
class ScaleCheck(Macro): def __init__(self, options, address=None, address_iq=None, *args, **kwargs): self.context = O() self.options = O(options) self.options.setifnone('timeout', DEFAULT_TIMEOUT) self.options.setifnone('skip_ping', False) if self.options.device: self.device = ConfigInterface().get_device(options.device) self.address = self.device.address else: self.device = None self.address = address self.options.setifnone('username', DEFAULT_ROOT_USERNAME) self.options.setifnone('password', DEFAULT_ROOT_PASSWORD) self.options.setifnone('admin_username', DEFAULT_ADMIN_USERNAME) self.options.setifnone('admin_password', DEFAULT_ADMIN_PASSWORD) if self.options.device_biq: self.device_biq = ConfigInterface().get_device(options.device_biq) self.address_biq = self.device_biq.address else: self.device_biq = None self.address_biq = address_iq self.options.setifnone('username_iq', DEFAULT_ROOT_USERNAME) self.options.setifnone('password_iq', DEFAULT_ROOT_PASSWORD) self.sshifc = None self.sshifc_biq = None super(ScaleCheck, self).__init__(*args, **kwargs) def prep(self): self.sshifc = SSHInterface(device=self.device, address=self.address, username=self.options.username, password=self.options.password, timeout=self.options.timeout, port=self.options.ssh_port) self.sshifc.open() self.sshifc_biq = SSHInterface(device=self.options.device_biq, address=self.address_biq, username=self.options.username_iq, password=self.options.password_iq, timeout=self.options.timeout, port=self.options.ssh_port) self.sshifc_biq.open() def wait_prompt(self): return SCMD.ssh.GetPrompt(ifc=self.sshifc)\ .run_wait(lambda x: x not in ('INOPERATIVE',), progress_cb=lambda x: 'Still %s...' % x, timeout=self.options.timeout, interval=10) def make_context(self): ctx = self.context ctx.version = SCMD.ssh.get_version(ifc=self.sshifc) ctx.status = self.wait_prompt() LOG.info('Version: {0.product.to_tmos} {0.version} {0.build}'.format( ctx.version)) return ctx def call(self, command): ret = SCMD.ssh.generic(command=command, ifc=self.sshifc) if ret and ret.status: LOG.warn(ret) else: LOG.debug(ret) return ret def clean_storage(self, ctx): has_restjavad = None with EmapiInterface(device=self.device, username=self.options.admin_username, password=self.options.admin_password, port=self.options.ssl_port, address=self.address) as rstifc: try: if ctx.version < "bigip 11.5.0": has_restjavad = rstifc.api.get(DeviceResolver.URI) except EmapiResourceError: LOG.warning("This pre 11.5.0 device hasn't had latest" " REST Framework upgrades") pass self.call("rm -rf /var/log/rest*") self.call("find /var/log -name '*.gz' -exec rm {} \;") # # Remove all files that are: blabla.1.blabla or blabla.1 # self.call("find /var/log -regex '.*[.][1-9].*' -exec rm '{}' \\;") self.call(WIPE_STORAGE) # Because of a bug where restjavad not knowing about icrd, BZ504333. if self.sshifc.version.product.is_bigip: self.call("bigstart restart icrd") with EmapiInterface(device=self.device, username=self.options.admin_username, password=self.options.admin_password, port=self.options.ssl_port, address=self.address) as rstifc: if has_restjavad: wait_args( rstifc.api.get, func_args=[DeviceResolver.URI], progress_message="Waiting for restjavad...", timeout=300, timeout_message="restjavad never came back up after {0}s") def relicense(self, ctx): if ctx.status != 'NO LICENSE': license_date = self.call('grep "License end" %s | cut -d: -f2' % LICENSE_FILE).stdout.strip() license_key = self.call( 'grep "Registration Key" %s | cut -d: -f2' % LICENSE_FILE).stdout.strip() date_format = "%Y%m%d" expire_date = datetime.datetime.strptime(license_date, date_format) delta = expire_date - datetime.datetime.now() if delta > datetime.timedelta(days=15): LOG.debug("%s is NOT within 15 days of being expired. " "Expiration date: %s" % (self.device, license_date)) else: LOG.info("Re-licensing %s. Expiration date: %s" % (self.device, license_date)) ret = self.call('SOAPLicenseClient --verbose --basekey %s' % license_key) LOG.debug("SOAPLicenseClient returned: %s", ret) else: raise MacroError("%s does not have a license. Expect BIG-IP to be " "functional" % self.device) def ping_check(self): bip_selfips = SCMD.tmsh.list("net self", ifc=self.sshifc) self_ips = [x['address'].split('/')[0] for x in bip_selfips.values()] for self_ip in self_ips: self_ip = IPAddress(self_ip) if self_ip.version == 4: COMMAND = "ping -c 1 %s" % self_ip.format(ipv6_full) elif self_ip.version == 6: COMMAND = "ping6 -c 1 %s" % self_ip.format(ipv6_full) # TODO: Find out why we can't ping using ipv6 address on Lowell's BIG-IQs continue else: LOG.info( "You got some weird IP address that isn't ipv4 or ipv6") LOG.info("Ping %s from %s" % (self_ip, self.options.device_biq if self.options.device_biq else self.address_biq)) resp = SCMD.ssh.generic(COMMAND, ifc=self.sshifc_biq) if '100% packet loss' in resp.stdout: LOG.info("device: %s not reachable" % self.device) LOG.debug("device: %s - %s" % (self.device, resp)) raise Exception("device: %s not reachable" % self.device) if self.device: self_ip = IPAddress(self.device.get_discover_address()) LOG.info("Verify given %s from yaml matches one on BIG-IP" % self_ip) for a in bip_selfips.values(): bip_ip = IPAddress(a['address'].split('/')[0]) if a['vlan'] == 'internal' and \ self_ip.version == bip_ip.version: internal_ip = bip_ip break if self_ip.format(ipv6_full) != internal_ip.format(ipv6_full): LOG.info("Internal mismatch: %s. %s != %s." % (self.device, self_ip, internal_ip)) else: LOG.info( "This isn't ran as stages so skipping internal selfip check") def setup(self): ctx = self.make_context() LOG.info("Deleting rest logs, *.gz files, and wiping storage") if ctx.version.product.is_bigip: self.clean_storage(ctx) # Check license and re-license if almost expired self.relicense(ctx) # Check if BIG-IQ can reach BIG-IP if not self.options.skip_ping: self.ping_check() # bigstart restart if BIG-IP is something else other than 'Active' LOG.info("State: %s" % ctx.status) if ctx.status != 'Active': LOG.info("bigstart restart on {0}..".format( self.device if self.device else self.address_biq)) self.call("bigstart restart") self.wait_prompt() def cleanup(self): if self.sshifc is not None: self.sshifc.close() if self.sshifc_biq is not None: self.sshifc_biq.close()
def push_certificate(self, pkey, cert): icifc = IcontrolInterface(device=self.options.device, address=self.address, username=self.options.admin_username, password=self.options.admin_password, port=self.options.ssl_port, debug=self.options.verbose) ic = icifc.open() key_pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey) cert_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) try: ic.Management.KeyCertificate.certificate_delete( mode='MANAGEMENT_MODE_WEBSERVER', cert_ids=['server']) ic.Management.KeyCertificate.key_delete( mode='MANAGEMENT_MODE_WEBSERVER', key_ids=['server']) except: LOG.warning('Exception occurred while deleting cert/key') ic.Management.KeyCertificate.certificate_import_from_pem( mode='MANAGEMENT_MODE_WEBSERVER', cert_ids=['server'], pem_data=[cert_pem], overwrite=1) ic.Management.KeyCertificate.key_import_from_pem( mode='MANAGEMENT_MODE_WEBSERVER', key_ids=['server'], pem_data=[key_pem], overwrite=1) icifc.close() # XXX: Unfortunately we can't reinit httpd through iControl. It's a KI # http://devcentral.f5.com/Default.aspx?tabid=53&forumid=1&postid=1170498&view=topic # # action = pc.System.Services.typefactory.\ # create('System.Services.ServiceAction').\ # SERVICE_ACTION_REINIT # service = pc.System.Services.typefactory.\ # create('System.Services.ServiceType').\ # SERVICE_HTTPD # pc.System.Services.set_service(services = [service], \ # service_action = action) # pc.System.Services.get_service_status([service]) with SSHInterface(device=self.options.device, address=self.address, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port) as sshifc: version = SCMD.ssh.get_version(ifc=sshifc) if version >= 'bigiq 4.4': sshifc.api.run('bigstart reinit webd') elif version >= 'bigiq 4.3' and version < 'bigiq 4.4': sshifc.api.run('bigstart reinit nginx') else: sshifc.api.run('bigstart reinit httpd') return True
def by_image2disk(self, filename, hfiso=None): iso_version = cm.version_from_metadata(filename) if hfiso: hfiso_version = cm.version_from_metadata(hfiso) reboot = False else: hfiso_version = None reboot = True LOG.debug('iso: %s', iso_version) base = os.path.basename(filename) essential = self.options.essential_config timeout = self.options.timeout if self.options.format_partitions or self.options.format_volumes: reboot = True with SSHInterface(address=self.address, username=self.options.root_username, password=self.options.root_password, timeout=timeout, port=self.options.ssh_port) as sshifc: ssh = sshifc.api version = SCMD.ssh.get_version(ifc=sshifc) LOG.info('running on %s', version) if version > 'bigip 9.6.0': try: ret = SCMD.tmsh.list('sys cluster', ifc=sshifc) except: ret = None if ret: raise NotImplementedError('Due to potential complications image2disk ' 'installations on clustered ' 'systems are not supported by this tool ' 'and should be done by hand. Sorry!') if not essential and abs(iso_version) < abs(version) or \ iso_version.product != version.product: LOG.warning('Enforcing --esential-config') essential = True if essential: lines = ssh.run('ls ' + SHARED_IMAGES).stdout.split() images = [x for x in lines if '.iso' in x] hfbase = os.path.basename(hfiso) if hfiso else None for image in images: if base != image and hfbase != image: LOG.info('Deleting image: %s' % image) ssh.run('rm -rf %s/%s' % (SHARED_IMAGES, image)) # XXX: Image checksum is not verified!! if (base not in ssh.run('ls ' + SHARED_IMAGES).stdout.split()): LOG.info('Importing iso %s', filename) SCMD.ssh.scp_put(ifc=sshifc, source=filename, nokex=False) filename = os.path.join(SHARED_IMAGES, base) if self.options.format_volumes: fmt = 'lvm' elif self.options.format_partitions: fmt = 'partitions' else: fmt = None def log_progress(stdout, stderr): output = '' if stdout: output += stdout if stderr: output += '\n' output += stderr # An in-house grep. for line in output.splitlines(): line = line.strip() if line and not line.startswith('info: '): LOG.debug(line) try: audit = SCMD.ssh.audit_software(version=version, ifc=sshifc) volume = get_inactive_volume(audit) except: volume = 'HD1.1' LOG.warning('Assuming destination slot %s', volume) LOG.info('Installing %s on %s...', iso_version, volume) SCMD.ssh.install_software(version=version, ifc=sshifc, repository=filename, format=fmt, essential=essential, volume=volume, progress_cb=log_progress, reboot=reboot, repo_version=iso_version) if reboot: # Grab a new iControl handle that uses the default admin credentials. self._wait_after_reboot(essential) if hfiso: if essential: sshifc = SSHInterface(address=self.address, timeout=timeout, port=self.options.ssh_port) else: sshifc = SSHInterface(address=self.address, timeout=timeout, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port) with sshifc: version = SCMD.ssh.get_version(ifc=sshifc) LOG.info('running on %s', version) if reboot: audit = SCMD.ssh.audit_software(version=version, ifc=sshifc) volume = get_inactive_volume(audit) LOG.info('Installing image on %s...', volume) SCMD.ssh.install_software(version=version, ifc=sshifc, repository=filename, reboot=False, essential=essential, volume=volume, progress_cb=log_progress, repo_version=iso_version) hfbase = os.path.basename(hfiso) if (hfbase not in sshifc.api.run('ls ' + SHARED_IMAGES).stdout.split()): LOG.info('Importing hotfix %s', hfiso) SCMD.ssh.scp_put(ifc=sshifc, source=hfiso, nokex=not reboot) hfiso = os.path.join(SHARED_IMAGES, hfbase) LOG.info('Installing hotfix on %s...', volume) SCMD.ssh.install_software(version=version, ifc=sshifc, repository=hfiso, is_hf=True, essential=essential, volume=volume, progress_cb=log_progress, repo_version=hfiso_version, reboot=False) LOG.info('Rebooting...') SCMD.ssh.switchboot(ifc=sshifc, volume=volume) SCMD.ssh.reboot(ifc=sshifc) # Grab a new iControl handle that uses the default admin credentials. current_version = self._wait_after_reboot(essential) expected_version = hfiso_version or iso_version if expected_version != current_version: raise InstallFailed('Version expected: %s but found %s' % (expected_version, current_version)) if essential: self._initialize_big3d() if essential and current_version.product.is_em: self._initialize_em() self.has_essential_config = essential
def by_api(self): o = self.options timeout = o.timeout identifier = self.options.pversion build = self.options.pbuild if identifier: identifier = str(identifier) if build: build = str(build) if self.options.image: filename = self.options.image else: filename = cm.isofile(identifier=identifier, build=build, product=o.product, root=o.build_path) if self.options.hfimage: hfiso = self.options.hfimage elif self.options.phf: hfiso = cm.isofile(identifier=identifier, build=build, hotfix=o.phf, product=o.product, root=o.build_path) else: hfiso = None iso_version = cm.version_from_metadata(filename) if (iso_version.product.is_bigip and iso_version >= 'bigip 10.0.0' or iso_version.product.is_em and iso_version >= 'em 2.0.0'): raise VersionNotSupported('Only legacy images supported through EMInstaller.') emifc = EMInterface(device=o.device, address=o.address, username=o.admin_username, password=o.admin_password) emifc.open() with SSHInterface(device=o.device, address=o.address, username=o.root_username, password=o.root_password, port=self.options.ssh_port) as ssh: status = SCMD.ssh.get_prompt(ifc=ssh) if status in ['LICENSE EXPIRED', 'REACTIVATE LICENSE']: SCMD.ssh.relicense(ifc=ssh) elif status in ['LICENSE INOPERATIVE', 'NO LICENSE']: raise MacroError('Device at %s needs to be licensed.' % ssh) reachable_devices = [x['access_address'] for x in EMSQL.device.get_reachable_devices(ifc=ssh)] for x in self.devices: x.address = net.resolv(x.address) to_discover = [x for x in self.devices if x.address not in reachable_devices] if to_discover: uid = EMAPI.device.discover(to_discover, ifc=emifc) task = EMSQL.device.GetDiscoveryTask(uid, ifc=ssh) \ .run_wait(lambda x: x['status'] != 'started', timeout=timeout, progress_cb=lambda x: 'discovery: %d%%' % x.progress_percent) assert task['error_count'] == 0, 'Discovery failed: %s' % task targets = [] for device in self.devices: mgmtip = device.address version = EMSQL.device.get_device_version(mgmtip, ifc=ssh) if not o.essential_config and abs(iso_version) < abs(version): LOG.warning('Enforcing --esential-config') o.essential_config = True device_info = EMSQL.device.get_device_info(mgmtip, ifc=ssh) active_slot = EMSQL.device.get_device_active_slot(mgmtip, ifc=ssh) targets.append(dict(device_uid=device_info['uid'], slot_uid=active_slot['uid'])) image_list = EMSQL.software.get_image_list(ifc=ssh) if iso_version not in image_list: base = os.path.basename(filename) destination = '%s.%d' % (os.path.join(SHARED_TMP, base), os.getpid()) LOG.info('Importing base iso %s', base) SCMD.ssh.scp_put(device=o.device, address=o.address, destination=destination, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port, source=filename, nokex=False) imuid = EMAPI.software.import_image(destination, ifc=emifc) else: imuid = image_list[iso_version] LOG.info('Image already imported: %d', imuid) if hfiso: hf_list = EMSQL.software.get_hotfix_list(ifc=ssh) hfiso_version = cm.version_from_metadata(hfiso) if hfiso_version not in hf_list: hfbase = os.path.basename(hfiso) destination = '%s.%d' % (os.path.join(SHARED_TMP, hfbase), os.getpid()) LOG.info('Importing hotfix iso %s', hfbase) SCMD.ssh.scp_put(device=o.device, address=o.address, destination=destination, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port, source=hfiso, nokex=False) hfuid = EMAPI.software.import_image(destination, ifc=emifc) else: hfuid = hf_list[hfiso_version] else: hfuid = None EMSQL.software.get_hotfix_list(ifc=ssh) EMSQL.device.CountActiveTasks(ifc=ssh) \ .run_wait(lambda x: x == 0, timeout=timeout, progress_cb=lambda x: 'waiting for other tasks') LOG.info('Installing %s...', iso_version) ret = EMAPI.software.install_image(targets, imuid, hfuid, o, ifc=emifc) ret = EMSQL.software.GetInstallationTask(ret['uid'], ifc=ssh).\ run_wait(lambda x: x['status'] != 'started', progress_cb=lambda x: 'install: %d%%' % x.progress_percent, timeout=o.timeout) LOG.info('Deleting %d device(s)...', len(targets)) EMAPI.device.delete(uids=[x['device_uid'] for x in targets], ifc=emifc) emifc.close() messages = [] for d in ret['details']: if int(d['error_code']): messages.append("%(display_device_address)s:%(error_message)s" % d) if int(d['hf_error_code'] or 0): messages.append("%(display_device_address)s:%(hf_error_message)s" % d) if messages: raise InstallFailed('Install did not succeed: %s' % ', '.join(messages)) self.has_essential_config = o.essential_config return ret
def by_icontrol(self, filename, hfiso=None): iso_version = cm.version_from_metadata(filename) timeout = self.options.timeout if hfiso: hfiso_version = cm.version_from_metadata(hfiso) else: hfiso_version = None LOG.debug('iso: %s', iso_version) icifc = IcontrolInterface(address=self.address, username=self.options.admin_username, password=self.options.admin_password, port=self.options.ssl_port) ic = icifc.open() running_volume = ICMD.software.get_active_volume(ifc=icifc) assert running_volume != self.options.volume, \ "Can't install on the active volume" version = ICMD.system.get_version(ifc=icifc) base = os.path.basename(filename) LOG.debug('running: %s', version) essential = self.options.essential_config if not essential and abs(iso_version) < abs(version): LOG.warning('Enforcing --esential-config') essential = True LOG.info('Setting the global DB vars...') ic.Management.Partition.set_active_partition(active_partition='Common') ic.Management.DBVariable.modify(variables=[ {'name': 'LiveInstall.MoveConfig', 'value': essential and 'disable' or 'enable'}, {'name': 'LiveInstall.SaveConfig', 'value': essential and 'disable' or 'enable'} ]) # ======================================================================= # Copy the ISO over to the device in /shared/images if it's not already # in the software repository. # ======================================================================= images = ICMD.software.get_software_image(ifc=icifc) haz_it = any(filter(lambda x: x['verified'] and x['product'] == iso_version.product.to_tmos and x['version'] == iso_version.version and x['build'] == iso_version.build, images)) volume = self.options.volume or ICMD.software.get_inactive_volume(ifc=icifc) LOG.info('Preparing volume %s...', volume) ICMD.software.clear_volume(volume=volume, ifc=icifc) def is_available(items): all_count = len(items) return sum(bool(x['verified']) for x in items) == all_count is_clustered = ic.System.Cluster.is_clustered_environment() LOG.info('Timeout: %d', timeout) if essential: with SSHInterface(address=self.address, username=self.options.root_username, password=self.options.root_password, timeout=timeout, port=self.options.ssh_port) as sshifc: ssh = sshifc.api lines = ssh.run('ls ' + SHARED_IMAGES).stdout.split() images = [x for x in lines if '.iso' in x] hfbase = os.path.basename(hfiso) if hfiso else None for image in images: if base != image and hfbase != image: # If the image is a hotfix image if 'hotfix' in image.lower(): LOG.info('Deleting hotfix image: %s' % image) ICMD.software.delete_software_image(image, is_hf=True, ifc=icifc) # Otherwise assume it is a base image else: LOG.info('Deleting base image: %s' % image) ICMD.software.delete_software_image(image, ifc=icifc) if not haz_it: LOG.info('Importing base iso %s', base) SCMD.ssh.scp_put(address=self.address, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port, source=filename, nokex=False, timeout=timeout) LOG.info('Wait for image to be imported %s', base) ICMD.software.GetSoftwareImage(filename=base, ifc=icifc) \ .run_wait(is_available, timeout=timeout, timeout_message="Timeout ({0}s) while waiting for the software image to be imported.") if hfiso: images = ICMD.software.get_software_image(ifc=icifc, is_hf=True) haz_it = any(filter(lambda x: x['verified'] and x['product'] == hfiso_version.product.to_tmos and x['version'] == hfiso_version.version and x['build'] == hfiso_version.build, images)) if not haz_it: hfbase = os.path.basename(hfiso) LOG.info('Importing hotfix iso %s', hfiso) SCMD.ssh.scp_put(address=self.address, username=self.options.root_username, password=self.options.root_password, port=self.options.ssh_port, source=hfiso, nokex=False) LOG.info('Wait for image to be imported %s', hfbase) ICMD.software.GetSoftwareImage(filename=hfbase, ifc=icifc, is_hf=True) \ .run_wait(is_available, timeout_message="Timeout ({0}s) while waiting for the hotfix image to be imported.") def is_still_removing(items): return not any(filter(lambda x: x['status'].startswith('removing'), items)) def is_still_installing(items): return not any(filter(lambda x: x['status'].startswith('installing') or x['status'].startswith('waiting') or x['status'].startswith('testing') or x['status'] in ('audited', 'auditing', 'upgrade needed'), items)) volumes = ICMD.software.get_software_status(ifc=icifc) assert is_still_installing(volumes), "An install is already in " \ "progress on another slot: %s" % volumes ICMD.software.GetSoftwareStatus(volume=volume, ifc=icifc) \ .run_wait(is_still_removing, # CAVEAT: tracks progress only for the first blade progress_cb=lambda x: x[0]['status'], timeout=timeout) LOG.info('Installing %s...', iso_version) ICMD.software.install_software(hfiso_version or iso_version, volume=volume, ifc=icifc) ret = ICMD.software.GetSoftwareStatus(volume=volume, ifc=icifc) \ .run_wait(is_still_installing, # CAVEAT: tracks progress only for the first blade progress_cb=lambda x: x[0]['status'], timeout=timeout, timeout_message="Timeout ({0}s) while waiting software install to finish.", stabilize=10) LOG.info('Resetting the global DB vars...') ic.Management.DBVariable.modify(variables=[ {'name': 'LiveInstall.MoveConfig', 'value': essential and 'enable' or 'disable'}, {'name': 'LiveInstall.SaveConfig', 'value': essential and 'enable' or 'disable'} ]) if sum(x['status'] == 'complete' for x in ret) != len(ret): raise InstallFailed('Install did not succeed: %s' % ret) LOG.info('Setting the active boot location %s.', volume) if is_clustered: # =================================================================== # Apparently on chassis systems the device is rebooted automatically # upon setting the active location, just like `b software desired # HD1.N active enable`. # =================================================================== uptime = ic.System.SystemInfo.get_uptime() ic.System.SoftwareManagement.set_cluster_boot_location(location=volume) time.sleep(60) else: ic.System.SoftwareManagement.set_boot_location(location=volume) LOG.info('Rebooting...') uptime = ICMD.system.reboot(ifc=icifc) # Grab a new iControl handle that uses the default admin credentials. if essential: icifc.close() icifc = IcontrolInterface(address=self.address, port=self.options.ssl_port) icifc.open() if uptime: ICMD.system.HasRebooted(uptime, ifc=icifc).run_wait(timeout=timeout) LOG.info('Device is rebooting...') LOG.info('Wait for box to be ready...') ICMD.system.IsServiceUp('MCPD', ifc=icifc).\ run_wait(timeout=timeout, timeout_message="Timeout ({0}s) while waiting for MCPD to come up") ICMD.system.IsServiceUp('TMM', ifc=icifc).\ run_wait(timeout_message="Timeout ({0}s) while waiting for TMM to come up") ICMD.management.GetDbvar('Configsync.LocalConfigTime', ifc=icifc).\ run_wait(lambda x: int(x) > 0, progress_cb=lambda x: 'waiting configsync...', timeout=timeout) ICMD.system.FileExists('/var/run/mprov.pid', ifc=icifc).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'mprov still running...', timeout=timeout) ICMD.system.FileExists('/var/run/grub.conf.lock', ifc=icifc).\ run_wait(lambda x: x is False, progress_cb=lambda x: 'grub.lock still present...', timeout=timeout) current_version = ICMD.system.get_version(ifc=icifc) expected_version = hfiso_version or iso_version try: if expected_version != current_version: raise InstallFailed('Version expected: %s but found %s' % (expected_version, current_version)) finally: icifc.close() # Will use SSH! if essential: self._initialize_big3d() if essential and current_version.product.is_em: self._initialize_em() self.has_essential_config = essential