Beispiel #1
0
 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()
Beispiel #2
0
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.')
Beispiel #3
0
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.')
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #6
0
    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']
Beispiel #7
0
    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
Beispiel #8
0
 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
Beispiel #9
0
    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)
Beispiel #10
0
    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')
Beispiel #11
0
 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'
         )
Beispiel #12
0
    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
Beispiel #13
0
    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
Beispiel #14
0
 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)
Beispiel #15
0
    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)
Beispiel #16
0
        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)
Beispiel #17
0
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
Beispiel #18
0
    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)
Beispiel #19
0
    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
Beispiel #20
0
 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')
Beispiel #21
0
 def prep(self):
     self.sshifc = SSHInterface(**self.sshparams)
     self.api = self.sshifc.open()
Beispiel #22
0
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()
Beispiel #23
0
    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
Beispiel #24
0
 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))
Beispiel #25
0
 def prep(self):
     if not self.options.conf:
         self.sshifc = SSHInterface(**self.sshparams)
         self.api = self.sshifc.open()
     else:
         self.sshifc = None
Beispiel #26
0
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)
Beispiel #27
0
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()
Beispiel #28
0
    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
Beispiel #29
0
    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
Beispiel #30
0
    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
Beispiel #31
0
    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