def get_fru(self): out = self.tool('fru', 'print') ipmi = parse.pairs(out) # remove (ID XX) from the top-level keys ipmi = dict((re.sub(r'\s*[(][^)]*[)]', '', k), v) for (k, v) in ipmi.iteritems()) return nullify(ipmi)
def _get_disk_share(ssh): """ Collect wwns from onstor server :param object ssh: ssh connection :returns list: List of dicts contains data for create share mount :rtype list: """ disk_shares = [] stdin, stdout, stderr = ssh.exec_command("scsi show all") for line in stdout.readlines(): splited_line = line.split() if splited_line and splited_line[-2] == 'OPENED': stdin_details, stdout_details, stderr_details = ssh.exec_command( "scsi show detail {0}".format(splited_line[1]) ) pairs = parse.pairs(lines=stdout_details.readlines()) disk_shares.append( { 'serial_number': _get_wwn(splited_line[1]), 'size': _convert_unit(pairs['CAPACITY']), 'volume': None, } ) return disk_shares
def _ssh_ssg(ip_address, user, password): ssh = _connect_ssh(ip_address, user, password) lines = ssh.ssg_command('get system') pairs = parse.pairs(lines=lines[:10]) name = pairs['Product Name'] version = pairs['Hardware Version'].split(',', 1)[0] model = '%s %s' % (name, version) mac = pairs['Base Mac'].replace('.', '').upper() sn = pairs['Serial Number'].split(',', 1)[0] result = { 'type': DeviceType.firewall.raw, 'model_name': model, 'mac_addresses': [MACAddressField.normalize(mac)], 'hostname': name, 'management_ip_addresses': [ip_address], 'parts': [{ 'boot_firmware': pairs['Software Version'].split(',', 1)[0], 'type': ComponentType.power.raw, }], } if sn not in SERIAL_BLACKLIST: result['serial_number'] = sn return result
def _recursive_add_dev(ssh, ip, dev_path, dev_id, components, parent=None, counts=None): if dev_path: full_path = '{}:{}'.format(dev_path, dev_id) else: full_path = dev_id if '[' in dev_id: dev_type = dev_id[0:dev_id.find('[')] else: dev_type = dev_id try: add_func = ADD_DEV[dev_type] except KeyError: return None lines = ssh.ibm_command('info -T {}'.format(full_path)) raw = '\n'.join(lines) pairs = parse.pairs(lines=lines) try: if dev_path: dev = add_func(pairs, parent, raw, counts, dev_id) else: dev = add_func(pairs, parent, raw, counts, dev_id, ip=ip) except DeviceError: pass else: for dev_info, components in components.iteritems(): if counts is None: counts = Counts() dev_id = dev_info.split(None, 1)[0] _recursive_add_dev(ssh, ip, full_path, dev_id, components, dev, counts) return dev
def get_running_vms(ssh): """Get a set of virtual machines running on the host.""" stdin, stdout, stderr = ssh.exec_command('sudo xe vm-list ' 'params=uuid,name-label,power-state,VCPUs-number,memory-actual') data = stdout.read() vms = set() for vm_data in data.split('\n\n'): info = parse.pairs(lines=[ line.replace('( RO)', '').replace('( RW)', '').replace('(MRO)', '').strip() for line in vm_data.splitlines()]) if not info: continue label = info['name-label'] if (label.startswith('Transfer VM for') or label.startswith('Control domain on host:')): # Skip the helper virtual machines continue power = info['power-state'] if power not in {'running'}: # Only include the running virtual machines continue cores = int(info['VCPUs-number']) memory = int(int(info['memory-actual'])/1024/1024) uuid = info['uuid'] vms.add((label, uuid, cores, memory)) return vms
def _get_running_vms(ssh, uuid, sudo_mode=False): stdin, stdout, stderr = ssh.exec_command( '{}xe vm-list resident-on={} ' 'params=uuid,name-label,power-state,VCPUs-number,memory-actual'.format( 'sudo ' if sudo_mode else '', uuid, )) data = stdout.read() vms = set() for vm_data in data.split('\n\n'): info = parse.pairs(lines=[ line.replace('( RO)', '').replace('( RW)', '').replace( '(MRO)', '').strip() for line in vm_data.splitlines() ]) if not info: continue label = info['name-label'] if (label.lower().startswith('Transfer VM for') or label.lower().startswith('Control domain on host:')): # Skip the helper virtual machines continue power = info['power-state'] if power not in {'running'}: # Only include the running virtual machines continue cores = int(info['VCPUs-number']) memory = int(int(info['memory-actual']) / 1024 / 1024) uuid = info['uuid'] vms.add((label, uuid, cores, memory)) return vms
def scan_address(ip_address, **kwargs): if 'nx-os' in (kwargs.get('snmp_name', '') or '').lower(): raise NoMatchError('Incompatible Nexus found.') kwargs['guessmodel'] = gvendor, gmodel = guessmodel.guessmodel(**kwargs) if gvendor != 'Cisco' or gmodel not in ('', ): raise NoMatchError('It is not Cisco.') ssh = _connect_ssh(ip_address) try: lines = ssh.asa_command( "show version | grep (^Hardware|Boot microcode|^Serial|address is)" ) finally: ssh.close() pairs = parse.pairs(lines=[line.strip() for line in lines]) sn = pairs.get('Serial Number', None) model, ram, cpu = pairs['Hardware'].split(',') boot_firmware = pairs['Boot microcode'] macs = [] for i in xrange(99): try: junk, label, mac = pairs['%d' % i].split(':') except KeyError: break mac = mac.split(',', 1)[0] mac = mac.replace('address is', '') mac = mac.replace('.', '').upper().strip() label = label.strip() if mac.replace(':', '').upper()[:6] not in MAC_PREFIX_BLACKLIST: macs.append(mac) ram_size = re.search('[0-9]+', ram).group() cpu_match = re.search('[0-9]+ MHz', cpu) cpu_speed = cpu_match.group()[:-4] cpu_model = cpu[:cpu_match.start()][4:].strip() result = get_base_result_template('ssh_cisco_asa') result.update({ 'status': 'success', 'device': { 'model_name': 'Cisco ' + model, 'type': str(DeviceType.firewall), 'mac_adresses': macs, 'boot_firmware': boot_firmware, 'management_ip_addresses': [ip_address], 'memory': [{ 'size': int(ram_size), }], 'processors': [{ 'model_name': cpu_model, 'speed': int(cpu_speed), 'family': cpu_model, }], }, }) if sn not in SERIAL_BLACKLIST: result['device']['serial_number'] = sn return result
def scan_address(ip_address, **kwargs): if 'nx-os' in (kwargs.get('snmp_name', '') or '').lower(): raise NoMatchError('Incompatible Nexus found.') kwargs['guessmodel'] = gvendor, gmodel = guessmodel.guessmodel(**kwargs) if gvendor != 'Cisco' or gmodel not in ('',): raise NoMatchError('It is not Cisco.') if not SSH_USER or not SSH_PASS: raise NotConfiguredError( "SSH not configured in plugin {}.".format(__name__), ) ssh = _connect_ssh(ip_address, SSH_USER, SSH_PASS) try: lines = ssh.asa_command( "show version | grep (^Hardware|Boot microcode|^Serial|address is)" ) finally: ssh.close() pairs = parse.pairs(lines=[line.strip() for line in lines]) sn = pairs.get('Serial Number', None) model, ram, cpu = pairs['Hardware'].split(',') boot_firmware = pairs['Boot microcode'] macs = [] for i in xrange(99): try: junk, label, mac = pairs['%d' % i].split(':') except KeyError: break mac = mac.split(',', 1)[0] mac = mac.replace('address is', '') mac = mac.replace('.', '').upper().strip() label = label.strip() if mac.replace(':', '').upper()[:6] not in MAC_PREFIX_BLACKLIST: macs.append(mac) ram_size = re.search('[0-9]+', ram).group() cpu_match = re.search('[0-9]+ MHz', cpu) cpu_speed = cpu_match.group()[:-4] cpu_model = cpu[:cpu_match.start()][4:].strip() result = get_base_result_template('ssh_cisco_asa') result.update({ 'status': 'success', 'device': { 'model_name': 'Cisco ' + model, 'type': str(DeviceType.firewall), 'mac_addresses': macs, 'boot_firmware': boot_firmware, 'management_ip_addresses': [ip_address], 'memory': [{ 'size': int(ram_size), }], 'processors': [{ 'model_name': cpu_model, 'speed': int(cpu_speed), 'family': cpu_model, }], }, }) if sn not in SERIAL_BLACKLIST: result['device']['serial_number'] = sn return result
def _get_mac_addresses(ipmitool, fru): data = parse.pairs(ipmitool.command('lan', 'print')) mac_addresses = {data.get('MAC Address')} index = 0 while True: ethernet = fru.get('MB/NET{}'.format(index)) if not ethernet: break mac_addresses.add(ethernet['Product Serial']) index += 1 return list(mac_addresses)
def run_ssh_asa(ip): ssh = _connect_ssh(ip) try: lines = ssh.asa_command( "show version | grep (^Hardware|Boot microcode|^Serial|address is)" ) raw_inventory = '\n'.join(ssh.asa_command("show inventory")) finally: ssh.close() pairs = parse.pairs(lines=[line.strip() for line in lines]) sn = pairs.get('Serial Number', None) model, ram, cpu = pairs['Hardware'].split(',') boot_firmware = pairs['Boot microcode'] ethernets = [] for i in xrange(99): try: junk, label, mac = pairs['%d' % i].split(':') except KeyError: break mac = mac.split(',', 1)[0] mac = mac.replace('address is', '') mac = mac.replace('.', '').upper().strip() label = label.strip() ethernets.append(Eth(label, mac, speed=None)) dev = Device.create(ethernets=ethernets, sn=sn, model_name=model, model_type=DeviceType.firewall, boot_firmware=boot_firmware) dev.save(update_last_seen=True) inventory = list(cisco_inventory(raw_inventory)) for inv in inventory: cisco_component(dev, inv) ipaddr, created = IPAddress.concurrent_get_or_create(address=ip) ipaddr.device = dev ipaddr.is_management = True ipaddr.save() for label, mac, speed in ethernets: eth, created = Ethernet.concurrent_get_or_create( mac=mac, defaults={'device': dev}, ) eth.label = label eth.device = dev eth.save() return model
def scan_address(ip_address, **kwargs): ssh = _connect_ssh(ip_address) try: lines = ssh.asa_command( "show version | grep (^Hardware|Boot microcode|^Serial|address is)" ) raw_inventory = '\n'.join(ssh.asa_command("show inventory")) finally: ssh.close() pairs = parse.pairs(lines=[line.strip() for line in lines]) sn = pairs.get('Serial Number', None) model, ram, cpu = pairs['Hardware'].split(',') boot_firmware = pairs['Boot microcode'] ethernets = [] macs = [] for i in xrange(99): try: junk, label, mac = pairs['%d' % i].split(':') except KeyError: break mac = mac.split(',', 1)[0] mac = mac.replace('address is', '') mac = mac.replace('.', '').upper().strip() label = label.strip() macs.append(mac) ram_size = re.search('[0-9]+', ram).group() cpu_match = re.search('[0-9]+ MHz', cpu) cpu_speed = cpu_match.group()[:-4] cpu_model = cpu[:cpu_match.start()][4:].strip() ret = { 'status': 'success', 'device': { 'model_name': 'Cisco ' + model, 'type': str(DeviceType.firewall), 'serial_number': sn, 'mac_adresses': macs, 'boot_firmware': boot_firmware, 'management_ip_addresses': [ip_address], 'memory': [{ 'size': int(ram_size), }], 'processors': [{ 'model_name': cpu_model, 'speed': int(cpu_speed), }], }, } tpl = get_base_result_template('ssh_cisco_asa') tpl.update(ret) return tpl
def _get_mac_addresses(ipmitool, fru): data = parse.pairs(ipmitool.command('lan', 'print')) mac_addresses = {data.get('MAC Address')} index = 0 while True: ethernet = fru.get('MB/NET{}'.format(index)) if not ethernet: break mac_addresses.add(ethernet['Product Serial']) index += 1 return [ mac for mac in mac_addresses if mac.replace(':', '').upper()[:6] not in MAC_PREFIX_BLACKLIST ]
def _prepare_devices( ssh, ip, dev_path, dev_id, components, parent=None, counts=None, ): if dev_path: full_path = '{}:{}'.format(dev_path, dev_id) else: full_path = dev_id if '[' in dev_id: dev_type = dev_id[0:dev_id.find('[')] else: dev_type = dev_id try: add_func = ADD_DEV[dev_type] except KeyError: return None lines = ssh.ibm_command('info -T {}'.format(full_path)) raw = '\n'.join(lines) pairs = parse.pairs(lines=lines) try: dev = add_func(ip, pairs, parent, raw, counts, dev_id) except DeviceError: return for dev_info, components in components.iteritems(): if counts is None: counts = Counts() dev_id = dev_info.split(None, 1)[0] subdev = _prepare_devices( ssh, ip, full_path, dev_id, components, dev, counts, ) if subdev and subdev != dev: if 'subdevices' not in dev: dev['subdevices'] = [] if subdev not in dev['subdevices']: dev['subdevices'].append(subdev) return dev
def _ssh_ssg(ip_address, user, password): ssh = _connect_ssh(ip_address, user, password) lines = ssh.ssg_command("get system") pairs = parse.pairs(lines=lines[:10]) name = pairs["Product Name"] version = pairs["Hardware Version"].split(",", 1)[0] model = "%s %s" % (name, version) mac = pairs["Base Mac"].replace(".", "").upper() sn = pairs["Serial Number"].split(",", 1)[0] result = { "type": DeviceType.firewall.raw, "model_name": model, "mac_addresses": [mac], "hostname": name, "management_ip_addresses": [ip_address], "parts": [{"boot_firmware": pairs["Software Version"].split(",", 1)[0], "type": ComponentType.power.raw}], } if sn not in SERIAL_BLACKLIST: result["serial_number"] = sn return result
def _ssh_onstor(ip_address, user, password): device_info = { 'type': DeviceType.storage.raw, 'management_ip_addresses': [ip_address], } ssh = _connect_ssh(ip_address, user, password) try: stdin, stdout, stderr = ssh.exec_command("system show summary") pairs = parse.pairs(lines=stdout.readlines()) model_name = pairs['--------']['Model number'] sn = pairs['--------']['System serial number'] mac = pairs['--------']['MAC addr'].upper().replace(':', '') device_info.update({ 'model_name': 'Onstor %s' % model_name, 'serial_number': sn, 'mac_addresses': [mac], }) finally: ssh.close() return device_info
def run_ssh_ssg(ip): ssh = _connect_ssh(ip) lines = ssh.ssg_command('get system') pairs = parse.pairs(lines=lines[:10]) name = pairs['Product Name'] version = pairs['Hardware Version'].split(',', 1)[0] model = '%s %s' % (name, version) mac = pairs['Base Mac'].replace('.', '').upper() sn = pairs['Serial Number'].split(',', 1)[0] dev = Device.create(ethernets=[Eth(label='Base MAC', mac=mac, speed=0)], model_name=model, model_type=DeviceType.firewall, sn=sn, name=name, raw='\n'.join(lines)) dev.boot_firmware = pairs['Software Version'].split(',', 1)[0] dev.save(update_last_seen=True) ipaddr, created = IPAddress.concurrent_get_or_create(address=ip) ipaddr.device = dev ipaddr.is_management = True ipaddr.save() return dev.name
def _recursive_add_dev(ssh, ip, dev_path, dev_id, components, parent=None, counts=None): if dev_path: full_path = '{}:{}'.format(dev_path, dev_id) else: full_path = dev_id if '[' in dev_id: dev_type = dev_id[0:dev_id.find('[')] else: dev_type = dev_id try: add_func = ADD_DEV[dev_type] except KeyError: return None lines = ssh.ibm_command('info -T {}'.format(full_path)) raw = '\n'.join(lines) pairs = parse.pairs(lines=lines) try: dev = add_func(ip, pairs, parent, raw, counts, dev_id) except DeviceError: pass else: for dev_info, components in components.iteritems(): if counts is None: counts = Counts() dev_id = dev_info.split(None, 1)[0] _recursive_add_dev( ssh, ip, full_path, dev_id, components, dev, counts, ) return dev
def _ssh_ssg(ip_address, user, password): ssh = _connect_ssh(ip_address, user, password) lines = ssh.ssg_command('get system') pairs = parse.pairs(lines=lines[:10]) name = pairs['Product Name'] version = pairs['Hardware Version'].split(',', 1)[0] model = '%s %s' % (name, version) mac = pairs['Base Mac'].replace('.', '').upper() sn = pairs['Serial Number'].split(',', 1)[0] return { 'type': DeviceType.firewall.raw, 'model_name': model, 'mac_addresses': [mac], 'serial_number': sn, 'hostname': name, 'management_ip_addresses': [ip_address], 'parts': [{ 'boot_firmware': pairs['Software Version'].split(',', 1)[0], 'type': ComponentType.power.raw, }], }
def _prepare_devices(ssh, ip, dev_path, dev_id, components, parent=None, counts=None,): if dev_path: full_path = '{}:{}'.format(dev_path, dev_id) else: full_path = dev_id if '[' in dev_id: dev_type = dev_id[0:dev_id.find('[')] else: dev_type = dev_id try: add_func = ADD_DEV[dev_type] except KeyError: return None lines = ssh.ibm_command('info -T {}'.format(full_path)) raw = '\n'.join(lines) pairs = parse.pairs(lines=lines) try: dev = add_func(ip, pairs, parent, raw, counts, dev_id) except DeviceError: return for dev_info, components in components.iteritems(): if counts is None: counts = Counts() dev_id = dev_info.split(None, 1)[0] subdev = _prepare_devices( ssh, ip, full_path, dev_id, components, dev, counts, ) if subdev and subdev != dev: if 'subdevices' not in dev: dev['subdevices'] = [] if subdev not in dev['subdevices']: dev['subdevices'].append(subdev) return dev
def _get_disk_share(ssh): """ Collect wwns from onstor server :param object ssh: ssh connection :returns list: List of dicts contains data for create share mount :rtype list: """ disk_shares = [] stdin, stdout, stderr = ssh.exec_command("scsi show all") for line in stdout.readlines(): splited_line = line.split() if splited_line and splited_line[-2] == 'OPENED': stdin_details, stdout_details, stderr_details = ssh.exec_command( "scsi show detail {0}".format(splited_line[1])) pairs = parse.pairs(lines=stdout_details.readlines()) disk_shares.append({ 'serial_number': _get_wwn(splited_line[1]), 'size': _convert_unit(pairs['CAPACITY']), 'volume': None, }) return disk_shares
def _ssh_onstor(ip_address, user, password): device_info = { 'type': DeviceType.storage.raw, 'management_ip_addresses': [ip_address], } ssh = _connect_ssh(ip_address, user, password) try: stdin, stdout, stderr = ssh.exec_command("system show summary") pairs = parse.pairs(lines=stdout.readlines()) model_name = pairs['--------']['Model number'] sn = pairs['--------']['System serial number'] mac = pairs['--------']['MAC addr'].upper().replace(':', '') device_info.update({ 'model_name': 'Onstor %s' % model_name, 'mac_addresses': [MACAddressField.normalize(mac)], }) if sn not in SERIAL_BLACKLIST: device_info['serial_number'] = sn disk_shares = _get_disk_share(ssh) if disk_shares: device_info['disk_shares'] = disk_shares finally: ssh.close() return device_info
def get_mc(self): out = self.tool('mc', 'info') return parse.pairs(out)
def _get_ipmi_fru(ipmitool): fru = parse.pairs(ipmitool.command('fru', 'print')) # remove (ID XX) from the top-level keys fru = dict((REMOVE_ID_REGEX.sub('', k), v) for (k, v) in fru.iteritems()) return nullify(fru)
def get_lan(self): out = self.tool('lan', 'print') return parse.pairs(out)
def _run_ssh_onstor(ip): ssh = _connect_ssh(ip) try: stdin, stdout, stderr = ssh.exec_command("system show summary") pairs = parse.pairs(lines=stdout.readlines()) name = pairs["Name"] model_name = pairs["--------"]["Model number"] sn = pairs["--------"]["System serial number"] mac = pairs["--------"]["MAC addr"].upper().replace(":", "") dev = _save_device(ip, name, model_name, sn, mac) first_ip = dev.ipaddress_set.order_by("address")[0] if ip != first_ip: raise SkipError("multipe addresses.") stdin, stdout, stderr = ssh.exec_command("lun show all -P1 -S10000") in_table = False luns = {} for line in stdout.readlines(): if not in_table: if line.startswith("-------------"): in_table = True continue else: lun_name, lun_type, raid, size, state, cluster, volume = line.split() luns[lun_name] = volume stdin, stdout, stderr = ssh.exec_command("vsvr show") in_table = False server_list = [] for line in stdout.readlines(): if not in_table: if line.startswith("======="): in_table = True continue else: no, state, server = line.split() if server.startswith("VS_MGMT"): continue server_list.append(server) mounts = collections.defaultdict(list) for server in server_list: channel = ssh.invoke_shell() _command(channel, "vsvr set %s" % server) lines = _command(channel, "nfs cache show mounts") channel.close() if not lines: continue if lines[0].startswith("No Mount information"): continue for line in lines: if line.strip().endswith(">") or not line.strip(): continue try: CLIENT, IP, ipaddr, SHARE, PATH, path = line.split(None, 6) except ValueError: continue if "/" in path: volume = path.split("/", 1)[1] else: volume = path mounts[volume].append(ipaddr) finally: ssh.close() _save_shares(dev, luns, mounts) return name
def _run_ssh_onstor(ip): ssh = _connect_ssh(ip) try: stdin, stdout, stderr = ssh.exec_command("system show summary") pairs = parse.pairs(lines=stdout.readlines()) name = pairs['Name'] model_name = pairs['--------']['Model number'] sn = pairs['--------']['System serial number'] mac = pairs['--------']['MAC addr'].upper().replace(':', '') dev = _save_device(ip, name, model_name, sn, mac) first_ip = dev.ipaddress_set.order_by('address')[0].address if ip != first_ip: raise SkipError('multiple addresses (will check %s).' % first_ip) stdin, stdout, stderr = ssh.exec_command("lun show all -P1 -S10000") in_table = False luns = {} for line in stdout.readlines(): if not in_table: if line.startswith('-------------'): in_table = True continue else: lun_name, lun_type, raid, size, state, cluster, volume = line.split() luns[lun_name] = volume stdin, stdout, stderr = ssh.exec_command("vsvr show") in_table = False server_list = [] for line in stdout.readlines(): if not in_table: if line.startswith('======='): in_table = True continue else: no, state, server = line.split() if server.startswith('VS_MGMT'): continue server_list.append(server) mounts = collections.defaultdict(list) for server in server_list: channel = ssh.invoke_shell() _command(channel, 'vsvr set %s' % server) lines = _command(channel, 'nfs cache show mounts') channel.close() if not lines: continue if lines[0].startswith('No Mount information'): continue for line in lines: if line.strip().endswith('>') or not line.strip(): continue try: CLIENT, IP, ipaddr, SHARE, PATH, path = line.split(None, 6) except ValueError: continue if '/' in path: volume = path.split('/', 1)[1] else: volume = path mounts[volume].append(ipaddr) finally: ssh.close() _save_shares(dev, luns, mounts) return name
def _run_ssh_onstor(ip): ssh = _connect_ssh(ip) try: stdin, stdout, stderr = ssh.exec_command("system show summary") pairs = parse.pairs(lines=stdout.readlines()) name = pairs['Name'] model_name = pairs['--------']['Model number'] sn = pairs['--------']['System serial number'] mac = pairs['--------']['MAC addr'].upper().replace(':', '') dev = _save_device(ip, name, model_name, sn, mac) first_ip = dev.ipaddress_set.order_by('address')[0].address if ip != first_ip: raise SkipError('multiple addresses (will check %s).' % first_ip) stdin, stdout, stderr = ssh.exec_command("lun show all -P1 -S10000") in_table = False luns = {} for line in stdout.readlines(): if not in_table: if line.startswith('-------------'): in_table = True continue else: ( lun_name, lun_type, raid, size, state, cluster, volume, ) = line.split() luns[lun_name] = volume stdin, stdout, stderr = ssh.exec_command("vsvr show") in_table = False server_list = [] for line in stdout.readlines(): if not in_table: if line.startswith('======='): in_table = True continue else: no, state, server = line.split() if server.startswith('VS_MGMT'): continue server_list.append(server) mounts = collections.defaultdict(list) for server in server_list: channel = ssh.invoke_shell() _command(channel, 'vsvr set %s' % server) lines = _command(channel, 'nfs cache show mounts') channel.close() if not lines: continue if lines[0].startswith('No Mount information'): continue for line in lines: if line.strip().endswith('>') or not line.strip(): continue try: CLIENT, IP, ipaddr, SHARE, PATH, path = line.split(None, 6) except ValueError: continue if '/' in path: volume = path.split('/', 1)[1] else: volume = path mounts[volume].append(ipaddr) finally: ssh.close() _save_shares(dev, luns, mounts) return name