def from_snmp(self, host, community): assert host assert community # switch port names and numbers oid = '.1.3.6.1.2.1.31.1.1.1.1' for name, value in _snmp_walk(host, community, oid): self._add_switch_port(name[len(oid):], value) # mac addresses table oid = '.1.3.6.1.2.1.17.7.1.2.2.1.2' for name, value in _snmp_walk(host, community, oid): name_parts = name.split('.') mac_address = "".join( [("%02x" % int(name_parts[x])).upper() for x in range(len(name_parts) - 6, len(name_parts))]).upper() self._add_server_port(self.get_port_name(value), mac_address) # arp address table oid = '.1.3.6.1.2.1.4.22.1.2' for name, value in _snmp_walk(host, community, oid): if not value.startswith('0x'): logger.warning("Not valid ARP record: mac:%s <- ip:%s", (value, name)) continue name_parts = name.split('.') ip_address = ".".join([name_parts[x] for x in range(len(name_parts) - 4, len(name_parts))]) self._add_server_port_ip(value[2:].upper(), ip_address)
def _handle_switch(self, *args, **options): device_id = options['switch-id'] switch = Resource.objects.get(pk=device_id) query = dict(parent=switch) if options['port'] and len(options['port']) > 0: query['number__in'] = options['port'] port_link_data = [] for switch_port in SwitchPort.active.filter(**query).order_by('-name'): for port_connection in PortConnection.active.filter(parent=switch_port): linked_server_port = port_connection.linked_port if linked_server_port: if isinstance(linked_server_port, ServerPort): port_link_data.append([ switch_port.number, port_connection.link_speed_mbit, linked_server_port.parent.name, linked_server_port.typed_parent.label, port_connection.last_seen ]) else: logger.warning("PortConnection %s linked to missing ServerPort %s" % ( port_connection, linked_server_port.id)) continue if port_link_data: writer = ConsoleResourceWriter(port_link_data) writer.print_table(fields=['port_number', 'link_speed_mbit', 'server_name', 'label', 'last_seen'], sort_by='port_number') else: logger.info("not connected")
def _guess_hypervisor(self, switch_port, dry_run=False): """ Поиск портов, на которых 1 физический сервер и несколько виртуальных. Определяем его как потенциальный гипервизор. :param switch_port: :param dry_run: If True, then role is set for guessed hypervisor (when 1 physical + many VMs). :return: """ assert switch_port assert isinstance(switch_port, SwitchPort) result, pysical_srv, virtual_srv = CmdbAnalyzer.guess_hypervisor(switch_port) if result: logger.info("Found hypervisor: %s" % pysical_srv) if not dry_run: pysical_srv.set_option('role', 'hypervisor') for virtual_server in virtual_srv: virtual_server.parent = pysical_srv virtual_server.save() logger.warning(" role automatically set, virtual servers are linked to the hypervisor.") else: logger.info("Switch port: %s" % switch_port) logger.info(" physicals: %s, virtuals: %s." % (len(pysical_srv), len(virtual_srv))) logger.info("Physical servers:") for server in pysical_srv: logger.info(unicode(server)) logger.info("Virtual servers:") for vserver in virtual_srv: logger.info(unicode(vserver))
def from_snmp(self, host, community): assert host assert community # switch port names and numbers oid = '.1.3.6.1.2.1.31.1.1.1.1' for name, value in _snmp_walk(host, community, oid): self._add_switch_port(name[len(oid):], value) # mac addresses table oid = '.1.3.6.1.2.1.17.7.1.2.2.1.2' for name, value in _snmp_walk(host, community, oid): name_parts = name.split('.') mac_address = "".join([ ("%02x" % int(name_parts[x])).upper() for x in range(len(name_parts) - 6, len(name_parts)) ]).upper() self._add_server_port(self.get_port_name(value), mac_address) # arp address table oid = '.1.3.6.1.2.1.4.22.1.2' for name, value in _snmp_walk(host, community, oid): if not value.startswith('0x'): logger.warning("Not valid ARP record: mac:%s <- ip:%s", (value, name)) continue name_parts = name.split('.') ip_address = ".".join([ name_parts[x] for x in range(len(name_parts) - 4, len(name_parts)) ]) self._add_server_port_ip(value[2:].upper(), ip_address)
def _handle_rack(self, *args, **options): rack_ids = options['id'] query = {} if len(rack_ids) > 0: query['pk__in'] = rack_ids for rack in Rack.active.filter(**query): if options['set_size']: rack.size = options['set_size'] elif options['layout']: print "*** {:^44} ***".format( "%s::%s (id:%s)" % (rack.parent if rack.parent else 'global', rack, rack.id)) unsorted_servers = [] for rack_resource in rack: if RackMountable.is_rack_mountable(rack_resource): unsorted_servers.append(rack_resource) sorted_servers = sorted(unsorted_servers, key=lambda s: s.position, reverse=True) if len(sorted_servers) <= 0: continue # sorted_servers must be sorted by position in reverse order rack_layout_map = {} curr_position = rack.size # max position for sorted_server in sorted_servers: curr_pos = sorted_server.position if not curr_pos: logger.warning("Server %s position is not set." % sorted_server) if curr_pos in rack_layout_map: while curr_pos in rack_layout_map: curr_pos += 1 sorted_server.position = curr_pos rack_layout_map[sorted_server.position] = sorted_server if sorted_server.position > curr_position: curr_position = sorted_server.position while curr_position > 0: if curr_position in rack_layout_map: server = rack_layout_map[curr_position] print "[{:>3s}| {:<40s} |{:s}]".format(unicode(server.position), server, 'o' if server.on_rails else ' ') else: print "[{:>3s}|{:-^46s}]".format(unicode(curr_position), '') curr_position -= 1 # print free space while curr_position > 0: print "[{:>3s}|{:-^46s}]".format(unicode(curr_position), '') curr_position -= 1 print "\n"
def from_snmp(self, host, community): assert host assert community # load port id map oid = ObjectType(ObjectIdentity('1.3.6.1.2.1.17.1.4.1.2')) for name, value in _snmp_walk(host, community, oid): self.snmpid__port_num__map[value] = name.split('.')[-1] # switch port names and numbers oid = ObjectType(ObjectIdentity('1.3.6.1.2.1.31.1.1.1.1')) for name, value in _snmp_walk(host, community, oid): snmp_port_id = name.split('.')[-1] if snmp_port_id in self.snmpid__port_num__map: real_port_number = self.snmpid__port_num__map[snmp_port_id] self._add_switch_port(real_port_number, value) else: logger.warning("There is no mapping for the SNMP port ID: %s" % snmp_port_id) # mac addresses table oid = ObjectType(ObjectIdentity('1.3.6.1.2.1.17.7.1.2.2.1.2')) for name, value in _snmp_walk(host, community, oid): name_parts = name.split('.') mac_address = "".join( [("%02x" % int(name_parts[x])).upper() for x in range(len(name_parts) - 6, len(name_parts))]).upper() self._add_server_port(self.get_port_name(value), mac_address) # arp address table oid = ObjectType(ObjectIdentity('IP-MIB', 'ipNetToMediaPhysAddress')) for name, value in _snmp_walk(host, community, oid): ip_address = '.'.join(name.split('.')[-4:]) self._add_server_port_ip(value.upper(), ip_address)
def from_snmp(self, host, community): assert host assert community # load port id map oid = '.1.3.6.1.2.1.17.1.4.1.2' for name, value in _snmp_walk(host, community, oid): self.snmpid__port_num__map[value] = name[len(oid):] # switch port names and numbers oid = '.1.3.6.1.2.1.31.1.1.1.1' for name, value in _snmp_walk(host, community, oid): snmp_port_id = name[len(oid):] if snmp_port_id in self.snmpid__port_num__map: real_port_number = self.snmpid__port_num__map[snmp_port_id] self._add_switch_port(real_port_number, value) else: logger.warning("There is no mapping for the SNMP port ID: %s" % snmp_port_id) # mac addresses table oid = '.1.3.6.1.2.1.17.7.1.2.2.1.2' for name, value in _snmp_walk(host, community, oid): name_parts = name.split('.') mac_address = "".join( [("%02x" % int(name_parts[x])).upper() for x in range(len(name_parts) - 6, len(name_parts))]).upper() self._add_server_port(self.get_port_name(value), mac_address) # arp address table oid = '.1.3.6.1.2.1.4.22.1.2' for name, value in _snmp_walk(host, community, oid): name_parts = name.split('.') ip_address = ".".join([name_parts[x] for x in range(len(name_parts) - 4, len(name_parts))]) self._add_server_port_ip(value[2:].upper(), ip_address)
def _handle_daimport(self, *args, **options): cid = options['cid'] da_pool = IPAddressPoolFactory.from_name(name="Imported DirectAdmin") with open(options['tsv-file']) as tsv_file: for line in tsv_file: line = line.strip() if line == '': continue (lid_id, ipaddr, lic_status) = line.decode('utf-8').split(None, 3) logger.info("> Processing: %s %s %s" % (lid_id, ipaddr, lic_status)) try: ip_obj = GlobalIPManager.get_ip(ipaddr) except: # TODO: refactor IP creation ip_obj, created = IPAddressGeneric.objects.update_or_create( address=ipaddr, pool=da_pool) if lic_status == Resource.STATUS_FREE: if ip_obj.status == Resource.STATUS_FREE: license = DirectAdminLicense.register_license( pool=da_pool, cid=cid, lid=int(lid_id), ip_address=ip_obj) license.free() logger.info("LIC %s (%s). Added as FREE" % (lid_id, ipaddr)) else: logger.warning( "(!!) LIC %s (%s). You must change IP." % (lid_id, ipaddr)) else: if ip_obj.status == Resource.STATUS_FREE: license = DirectAdminLicense.register_license( pool=da_pool, cid=cid, lid=int(lid_id), ip_address=ip_obj) license.free() logger.warning( "(!) LIC %s (%s). Added as FREE (changed License status to FREE)" % (lid_id, ipaddr)) else: license = DirectAdminLicense.register_license( pool=da_pool, cid=cid, lid=int(lid_id), ip_address=ip_obj) license.use() logger.info("LIC %s (%s). Added as USED." % (lid_id, ipaddr))
def _handle_auto(self, *args, **options): # update via snmp query = dict() if options['switch_id']: query['pk'] = options['switch_id'] if not options['skip_arp']: for switch in (itertools.chain( GatewaySwitch.active.filter(**query), Switch.active.filter(**query))): logger.info("* Found switch: %s" % switch) if switch.has_option('snmp_provider_key'): snmp_provider_key = switch.get_option_value( 'snmp_provider_key') if snmp_provider_key in self.registered_providers: hostname = switch.get_option_value('snmp_host') community = switch.get_option_value('snmp_community') logger.info("host: %s" % hostname) provider = self.registered_providers[ snmp_provider_key]() provider.from_snmp(hostname, community) self.cmdb_importer.import_switch(switch.id, provider) else: logger.warning("Unknown SNMP data provider: %s" % snmp_provider_key) Resource.objects.rebuild() logger.info("Process hypervisors.") for switch in Switch.active.all(): for switch_port in SwitchPort.active.filter(parent=switch): self.cmdb_importer.process_hypervisors(switch_port) for switch in GatewaySwitch.active.all(): for switch_port in SwitchPort.active.filter(parent=switch): self.cmdb_importer.process_hypervisors(switch_port) logger.info("Process server mounts") link_unresolved_to_container, created = RegionResource.objects.get_or_create( name='Unresolved servers') self.cmdb_importer.process_servers( link_unresolved_to=link_unresolved_to_container) logger.info("Process virtual server mounts") link_unresolved_to_container, created = RegionResource.objects.get_or_create( name='Unresolved VPS') self.cmdb_importer.process_virtual_servers( link_unresolved_to=link_unresolved_to_container) Resource.objects.rebuild()
def _handle_pool_get_next(self, *args, **options): for pool_id in options['pool-id']: ip_set = Resource.active.get(pk=pool_id) ip_count = options['count'] beauty_idx = options['beauty'] for ip_address in ip_set.available(): if not ip_count: break if ip_address.beauty >= beauty_idx: self._print_address(ip_address) ip_count -= 1 if ip_count > 0: logger.warning("Pool '%s' have no such many IPs (%d IPs unavailable)" % (ip_set, ip_count + 1))
def _handle_auto(self, *args, **options): # update via snmp query = dict(type__in=[GatewaySwitch.__name__, Switch.__name__]) if options['switch_id']: query['pk'] = options['switch_id'] if not options['skip_arp']: for switch in Resource.active.filter(**query): logger.info("* Found switch: %s" % switch) if switch.has_option('snmp_provider_key'): snmp_provider_key = switch.get_option_value('snmp_provider_key') if snmp_provider_key in self.registered_providers: hostname = switch.get_option_value('snmp_host') community = switch.get_option_value('snmp_community') logger.info("host: %s" % hostname) provider = self.registered_providers[snmp_provider_key]() provider.from_snmp(hostname, community) self.cmdb_importer.import_switch(switch.id, provider) else: logger.warning("Unknown SNMP data provider: %s" % snmp_provider_key) Resource.objects.rebuild() logger.info("Process hypervisors.") for switch in Switch.active.all(): for switch_port in SwitchPort.active.filter(parent=switch): self.cmdb_importer.process_hypervisors(switch_port) for switch in GatewaySwitch.active.all(): for switch_port in SwitchPort.active.filter(parent=switch): self.cmdb_importer.process_hypervisors(switch_port) logger.info("Process server mounts") link_unresolved_to_container, created = RegionResource.objects.get_or_create(name='Unresolved servers') self.cmdb_importer.process_servers(link_unresolved_to=link_unresolved_to_container) logger.info("Process virtual server mounts") link_unresolved_to_container, created = RegionResource.objects.get_or_create(name='Unresolved VPS') self.cmdb_importer.process_virtual_servers(link_unresolved_to=link_unresolved_to_container) Resource.objects.rebuild()
def _get_server_by_ip_or_id(self, server_ip_id): """ Find server by IP or ID. If nothing found, return None :param server_ip_id: :return: """ assert server_ip_id server = None if server_ip_id.find('.') > -1: ips = IPAddress.active.filter(address=server_ip_id) if len(ips) > 0 and isinstance(ips[0].parent.typed_parent, AssetResource): print ips[0].parent.parent.type server = ips[0].parent.typed_parent else: logger.warning("IP %s is not assigned to any server." % server_ip_id) else: server = Resource.objects.get(pk=server_ip_id) return server
def import_switch(self, switch_cmdb_id, l3switch): """ Import data from layer 3 switch :param l3switch: L3Switch """ source_switch = Resource.active.get(pk=switch_cmdb_id) for l3port in l3switch.ports: if l3port.is_local: self._add_local_port(source_switch, l3port) else: self._add_foreign_port(l3port) # Import IP addresses for connected_mac in l3port.macs: server, server_port = self._add_server_and_port(connected_mac) for ip_address in l3port.switch.get_mac_ips(unicode(connected_mac)): self._add_ip(ip_address, parent=server_port) # There is only one connection from the single server port. logger.info("Clean extra PortConnections") for server_port in ServerPort.active.filter(): port_connections = PortConnection.active.filter(linked_port_id=server_port.id).order_by('-last_seen') if len(port_connections) > 1: logger.warning("Server port %s have >1 PortConnection" % server_port) deleted_poconn = 0 for port_connection in port_connections[1:]: logger.warning(" remove PortConnection %s" % port_connection) port_connection.delete() deleted_poconn += 1 logger.warning(" deleted %s" % deleted_poconn)
def _handle_switch(self, *args, **options): device_id = options['switch-id'] switch = Resource.objects.get(pk=device_id) query = dict(parent=switch) if options['port'] and len(options['port']) > 0: query['number__in'] = options['port'] port_link_data = [] for switch_port in SwitchPort.active.filter(**query).order_by('-name'): for port_connection in PortConnection.active.filter( parent=switch_port): linked_server_port = port_connection.linked_port if linked_server_port: if isinstance(linked_server_port, ServerPort): port_link_data.append([ switch_port.number, port_connection.link_speed_mbit, linked_server_port.parent.name, linked_server_port.typed_parent.label, port_connection.last_seen ]) else: logger.warning( "PortConnection %s linked to missing ServerPort %s" % (port_connection, linked_server_port.id)) continue if port_link_data: writer = ConsoleResourceWriter(port_link_data) writer.print_table(fields=[ 'port_number', 'link_speed_mbit', 'server_name', 'label', 'last_seen' ], sort_by='port_number') else: logger.info("not connected")
def _guess_hypervisor(self, switch_port, dry_run=False): """ Поиск портов, на которых 1 физический сервер и несколько виртуальных. Определяем его как потенциальный гипервизор. :param switch_port: :param dry_run: If True, then role is set for guessed hypervisor (when 1 physical + many VMs). :return: """ assert switch_port assert isinstance(switch_port, SwitchPort) result, pysical_srv, virtual_srv = CmdbAnalyzer.guess_hypervisor( switch_port) if result: logger.info("Found hypervisor: %s" % pysical_srv) if not dry_run: pysical_srv.set_option('role', 'hypervisor') for virtual_server in virtual_srv: virtual_server.parent = pysical_srv virtual_server.save() logger.warning( " role automatically set, virtual servers are linked to the hypervisor." ) else: logger.info("Switch port: %s" % switch_port) logger.info(" physicals: %s, virtuals: %s." % (len(pysical_srv), len(virtual_srv))) logger.info("Physical servers:") for server in pysical_srv: logger.info(unicode(server)) logger.info("Virtual servers:") for vserver in virtual_srv: logger.info(unicode(vserver))
def lease_ips(ip_pool_ids, count=1): """ Returns given number of IPs from different IP address pools. """ assert ip_pool_ids assert count > 0 pool_id_infinite_list = IPAddressPool.InfiniteList(ip_pool_ids) rented_ips = [] changed = False iterations = len(pool_id_infinite_list) for ip_pool_id in pool_id_infinite_list: if len(rented_ips) >= count: break if iterations <= 0 and not changed: raise Exception("There is no available IPs in pools: %s" % ip_pool_ids) iterations -= 1 ip_pool_resource = Resource.active.get(pk=ip_pool_id) if ip_pool_resource.usage >= 95: logger.warning("IP pool %s usage >95%%" % ip_pool_resource.id) try: ip = ip_pool_resource.available().next() ip.lock() ip.touch() logger.debug("Available IP found: %s" % ip) rented_ips.append(ip) changed = True except Exception, ex: logger.error("Exception %s while getting IP from IP pool: %s" % (ex.__class__.__name__, ex.message))
def from_snmp(self, host, community): assert host assert community # load port id map oid = ObjectType(ObjectIdentity('1.3.6.1.2.1.17.1.4.1.2')) for name, value in _snmp_walk(host, community, oid): self.snmpid__port_num__map[value] = name.split('.')[-1] # switch port names and numbers oid = ObjectType(ObjectIdentity('1.3.6.1.2.1.31.1.1.1.1')) for name, value in _snmp_walk(host, community, oid): snmp_port_id = name.split('.')[-1] if snmp_port_id in self.snmpid__port_num__map: real_port_number = self.snmpid__port_num__map[snmp_port_id] self._add_switch_port(real_port_number, value) else: logger.warning("There is no mapping for the SNMP port ID: %s" % snmp_port_id) # mac addresses table oid = ObjectType(ObjectIdentity('1.3.6.1.2.1.17.7.1.2.2.1.2')) for name, value in _snmp_walk(host, community, oid): name_parts = name.split('.') mac_address = "".join([ ("%02x" % int(name_parts[x])).upper() for x in range(len(name_parts) - 6, len(name_parts)) ]).upper() self._add_server_port(self.get_port_name(value), mac_address) # arp address table oid = ObjectType(ObjectIdentity('IP-MIB', 'ipNetToMediaPhysAddress')) for name, value in _snmp_walk(host, community, oid): ip_address = '.'.join(name.split('.')[-4:]) self._add_server_port_ip(value.upper(), ip_address)
def _handle_household(self, *args, **options): last_seen_31days = timezone.now() - datetime.timedelta(days=31) last_seen_15days = timezone.now() - datetime.timedelta(days=15) # Clean IP with parent=ip pool (free) with last_seen older that 31 days. It means that IP is not # used and can be released. logger.info("Clean missing IP addresses: %s" % last_seen_31days) for free_ip_pool in GlobalIPManager.find_pools( status=Resource.STATUS_FREE, version=4): logger.info(" pool %s" % free_ip_pool) for ip in GlobalIPManager.find_ips(status=Resource.STATUS_INUSE, last_seen__lt=last_seen_31days, pool=free_ip_pool): logger.warning( " used ip %s from the FREE IP pool is not seen for 31 days. Free it." % ip) ip.free() for ip in GlobalIPManager.find_ips(status=Resource.STATUS_LOCKED, last_seen__lt=last_seen_15days, pool=free_ip_pool): logger.warning( " locked ip %s from the FREE IP pool is not seen for 15 days. Free it." % ip) ip.free() logger.info("Clean missing virtual servers: %s" % last_seen_31days) for vm in VirtualServer.active.filter(last_seen__lt=last_seen_31days): logger.warning(" server %s not seen for 31 days. Removing..." % vm) for vm_child in vm: logger.info(" remove %s" % vm_child) vm_child.delete() vm.delete() logger.info("Clean unresolved PortConnections...") removed = 0 for connection in PortConnection.active.all(): if not connection.linked_port: connection.delete() removed += 1 logger.info(" removed: %s" % removed) logger.info("Clean missing PortConnections (not seen for 15 days)...") removed = 0 for connection in PortConnection.active.filter( last_seen__lt=last_seen_31days): connection.delete() removed += 1 logger.info(" removed: %s" % removed) Resource.objects.rebuild()
def _handle_household(self, *args, **options): last_seen_31days = timezone.now() - datetime.timedelta(days=31) last_seen_15days = timezone.now() - datetime.timedelta(days=15) # Clean IP with parent=ip pool (free) with last_seen older that 31 days. It means that IP is not # used and can be released. logger.info("Clean missing IP addresses: %s" % last_seen_31days) for free_ip_pool in Resource.active.filter(status=Resource.STATUS_FREE, type__in=IPAddressPool.ip_pool_types): logger.info(" pool %s" % free_ip_pool) for ip in IPAddress.active.filter( status=Resource.STATUS_INUSE, last_seen__lt=last_seen_31days, ipman_pool_id=free_ip_pool.id, version=4): logger.warning(" used ip %s from the FREE IP pool is not seen for 31 days. Free it." % ip) ip.free(cascade=True) for ip in IPAddress.active.filter( status=Resource.STATUS_LOCKED, last_seen__lt=last_seen_15days, ipman_pool_id=free_ip_pool.id, version=4): logger.warning(" locked ip %s from the FREE IP pool is not seen for 15 days. Free it." % ip) ip.free(cascade=True) logger.info("Clean missing virtual servers: %s" % last_seen_31days) for vm in VirtualServer.active.filter(last_seen__lt=last_seen_31days): logger.warning(" server %s not seen for 31 days. Removing..." % vm) for vm_child in vm: logger.info(" remove %s" % vm_child) vm_child.delete() vm.delete() logger.info("Clean unresolved PortConnections...") removed = 0 for connection in PortConnection.active.all(): if not connection.linked_port: connection.delete() removed += 1 logger.info(" removed: %s" % removed) logger.info("Clean missing PortConnections (not seen for 15 days)...") removed = 0 for connection in PortConnection.active.filter(last_seen__lt=last_seen_31days): connection.delete() removed += 1 logger.info(" removed: %s" % removed) Resource.objects.rebuild()
def import_switch(self, switch_cmdb_id, l3switch): """ Import data from layer 3 switch :param l3switch: L3Switch """ source_switch = Resource.active.get(pk=switch_cmdb_id) for l3port in l3switch.ports: if l3port.is_local: self._add_local_port(source_switch, l3port) else: self._add_foreign_port(l3port) # Import IP addresses for connected_mac in l3port.macs: server, server_port = self._add_server_and_port(connected_mac) for ip_address in l3port.switch.get_mac_ips( unicode(connected_mac)): self._add_ip(ip_address, parent=server_port) # There is only one connection from the single server port. logger.info("Clean extra PortConnections") for server_port in ServerPort.active.filter(): port_connections = PortConnection.active.filter( linked_port_id=server_port.id).order_by('-last_seen') if len(port_connections) > 1: logger.warning("Server port %s have >1 PortConnection" % server_port) deleted_poconn = 0 for port_connection in port_connections[1:]: logger.warning(" remove PortConnection %s" % port_connection) port_connection.delete() deleted_poconn += 1 logger.warning(" deleted %s" % deleted_poconn)
def _handle_rack(self, *args, **options): rack_ids = options['id'] query = {} if len(rack_ids) > 0: query['pk__in'] = rack_ids for rack in Rack.active.filter(**query): if options['set_size']: rack.size = options['set_size'] elif options['layout']: print "*** {:^44} ***".format( "%s::%s (id:%s)" % (rack.parent if rack.parent else 'global', rack, rack.id)) unsorted_servers = [] for rack_resource in rack: if RackMountable.is_rack_mountable(rack_resource): unsorted_servers.append(rack_resource) sorted_servers = sorted(unsorted_servers, key=lambda s: s.position, reverse=True) if len(sorted_servers) <= 0: continue # sorted_servers must be sorted by position in reverse order rack_layout_map = {} curr_position = rack.size # max position for sorted_server in sorted_servers: curr_pos = sorted_server.position if not curr_pos: logger.warning("Server %s position is not set." % sorted_server) if curr_pos in rack_layout_map: while curr_pos in rack_layout_map: curr_pos += 1 sorted_server.position = curr_pos rack_layout_map[sorted_server.position] = sorted_server if sorted_server.position > curr_position: curr_position = sorted_server.position while curr_position > 0: if curr_position in rack_layout_map: server = rack_layout_map[curr_position] print "[{:>3s}| {:<40s} |{:s}]".format( unicode(server.position), server, 'o' if server.on_rails else ' ') else: print "[{:>3s}|{:-^46s}]".format( unicode(curr_position), '') curr_position -= 1 # print free space while curr_position > 0: print "[{:>3s}|{:-^46s}]".format(unicode(curr_position), '') curr_position -= 1 print "\n"