From 84fd513f3d5341bd354ff7507904c076a14b3421 Mon Sep 17 00:00:00 2001 From: XuWei Date: Mon, 5 Mar 2018 03:48:11 -0500 Subject: [PATCH 1/2] rspconfig set ip/netmask/gateway/vlan in python --- .../lib/python/agent/common/utils.py | 14 +++ .../agent/hwctl/executor/openbmc_bmcconfig.py | 103 ++++++++++++++++-- .../lib/python/agent/hwctl/openbmc_client.py | 84 +++++++++++--- 3 files changed, 176 insertions(+), 25 deletions(-) diff --git a/xCAT-openbmc-py/lib/python/agent/common/utils.py b/xCAT-openbmc-py/lib/python/agent/common/utils.py index e4112741b..5df571320 100644 --- a/xCAT-openbmc-py/lib/python/agent/common/utils.py +++ b/xCAT-openbmc-py/lib/python/agent/common/utils.py @@ -82,6 +82,20 @@ def sort_string_with_numbers(origin_list): new_list.sort() return [string for __,string in new_list] +def exchange_mask(mask): + count_bit = lambda bin_str: len([i for i in bin_str if i=='1']) + mask_splited = mask.split('.') + mask_count = [count_bit(bin(int(i))) for i in mask_splited] + return sum(mask_count) + +def exchange_maskint(mask_int): + bin_arr = ['0' for i in range(32)] + for i in range(mask_int): + bin_arr[i] = '1' + tmpmask = [''.join(bin_arr[i * 8:i * 8 + 8]) for i in range(4)] + tmpmask = [str(int(tmpstr, 2)) for tmpstr in tmpmask] + return '.'.join(tmpmask) + class Messager(object): def __init__(self, name=None): self.logger = logging.getLogger(name or 'xcatagent') diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py index f1b648ede..46045c361 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py @@ -167,7 +167,7 @@ class OpenBMCBmcConfigTask(ParallelNodesCommand): if (20-i) % 8 == 0: self.callback.info('%s: Still waiting for dump %s to be generated... ' % (node, dump_id)) - sleep( 15 ) + gevent.sleep( 15 ) if flag: self._dump_download(obmc, node, str(dump_id), flag_dump_process=True) @@ -296,7 +296,7 @@ rmdir \"/tmp/$userid\" \n") return else: self._set_netinfo(netinfo_dict['ip'], netinfo_dict['netmask'], - netinfo_dict['gateway'], netinfo_dict['vlan']) + netinfo_dict['gateway'], netinfo_dict['vlan'], **kw) def _set_hostname(self, hostname, **kw): node = kw['node'] @@ -324,6 +324,10 @@ rmdir \"/tmp/$userid\" \n") if not netinfo: return self.callback.error('No network information get', node) + if 'error' in netinfo: + self.callback.info('%s: %s' % (node, netinfo['error'])) + return + bmcip = node_info['bmcip'] nic = self._get_facing_nic(bmcip, netinfo) if not nic: @@ -394,12 +398,91 @@ rmdir \"/tmp/$userid\" \n") result = '%s: %s: %s' % (node, openbmc.RSPCONFIG_APIS[key]['display_name'], str_value.split('.')[-1]) self.callback.info(result) - def _set_netinfo(self, ip, netmask, gateway, vlan=False, **kw): + def _print_bmc_netinfo(self, node, ip, netmask, gateway, vlan): + + self.callback.info('%s: BMC IP: %s'% (node, ip)) + self.callback.info('%s: BMC Netmask: %s' % (node, netmask)) + self.callback.info('%s: BMC Gateway: %s' % (node, gateway)) if vlan: - result = "set net(%s, %s, %s) for vlan %s" % (ip, netmask, gateway, vlan) - else: - result = "set net(%s, %s, %s) for eth0" % (ip, netmask, gateway) - return self.callback.info("set_netinfo %s" % result) + self.callback.info('%s: BMC VLAN ID: %s' % (node, vlan)) + + def _set_netinfo(self, ip, netmask, gateway, vlan=False, **kw): + + node = kw['node'] + node_info = kw['nodeinfo'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=node_info, messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + try: + obmc.login() + netinfo = obmc.get_netinfo() + except (SelfServerException, SelfClientException) as e: + self.callback.error(e.message, node) + return + + if not netinfo: + return self.callback.error("No network information get", node) + if 'error' in netinfo: + return self.callback.info('%s: %s' % (node, netinfo['error'])) + + bmcip = node_info['bmcip'] + origin_nic = nic = self._get_facing_nic(bmcip, netinfo) + if not nic: + return self.callback.error('Can not get facing NIC for %s' % bmcip, node) + + if (ip == netinfo[nic]['ip'] and netmask == utils.exchange_maskint(netinfo[nic]['netmask']) and + gateway == netinfo[nic]['gateway']): + if not vlan or vlan == str(netinfo[nic]['vlanid']): + self._print_bmc_netinfo(node, ip, netmask, gateway, vlan) + return + + origin_type = netinfo[origin_nic]['ipsrc'] + origin_ip_obj = netinfo[origin_nic]['ipobj'] + + if vlan: + pre_nic = nic.split('_')[0] + try: + obmc.set_vlan(pre_nic, vlan) + gevent.sleep( 15 ) + except (SelfServerException, SelfClientException) as e: + self.callback.error(e.message, node) + return + nic = pre_nic + '_' + vlan + + prefix = int(utils.exchange_mask(netmask)) + try: + obmc.set_netinfo(nic, ip, prefix, gateway) + gevent.sleep( 5 ) + nic_netinfo = obmc.get_nic_netinfo(nic) + except (SelfServerException, SelfClientException) as e: + self.callback.error(e.message, node) + return + + if not netinfo: + return self.callback.error('Did not get info for NIC %s' % nic, node) + + set_success = False + for net_id, attr in nic_netinfo.items(): + if attr['ip'] == ip: + attr_netmask = utils.exchange_maskint(attr["netmask"]) + if (attr_netmask == netmask and + attr['gateway'] == gateway): + set_success = True + + if not set_success: + return self.callback.error('Config BMC IP failed', node) + + try: + if origin_type == 'DHCP': + obmc.disable_dhcp(origin_nic) + elif origin_type == 'Static': + obmc.delete_ip_object(origin_nic, origin_ip_obj) + else: + self.callback.error('Get wrong Origin type %s for NIC %s IP object %s' % (origin_type, nic, origin_ip_obj), node) + except (SelfServerException, SelfClientException) as e: + self.callback.error(e.message, node) + + self. _print_bmc_netinfo(node, ip, netmask, gateway, vlan) def _get_netinfo(self, ip=False, ipsrc=False, netmask=False, gateway=False, vlan=False, hostname=False, ntpservers=False, **kw): node = kw['node'] @@ -424,6 +507,10 @@ rmdir \"/tmp/$userid\" \n") if hostname: self.callback.info("%s: BMC Hostname: %s" %(node, bmchostname)) + + if 'error' in netinfo: + return self.callback.info('%s: %s' % (node, netinfo['error'])) + dic_length = len(netinfo) netinfodict = {'ip':[], 'netmask':[], 'gateway':[], 'vlan':[], 'ipsrc':[], 'ntpservers':[]} @@ -432,7 +519,7 @@ rmdir \"/tmp/$userid\" \n") if dic_length > 1: addon_string = " for %s" % nic netinfodict['ip'].append("BMC IP"+addon_string+": %s" % attrs["ip"]) - netinfodict['netmask'].append("BMC Netmask"+addon_string+": %s" % attrs["netmask"]) + netinfodict['netmask'].append("BMC Netmask"+addon_string+": %s" % utils.exchange_maskint(attrs["netmask"])) netinfodict['gateway'].append("BMC Gateway"+addon_string+": %s (default: %s)" % (attrs["gateway"], defaultgateway)) netinfodict['vlan'].append("BMC VLAN ID"+addon_string+": %s" % attrs["vlanid"]) netinfodict['ipsrc'].append("BMC IP Source"+addon_string+": %s" % attrs["ipsrc"]) diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py index 8b1dc1605..f3ff6c00f 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py @@ -162,11 +162,14 @@ FIRM_URLS = { } RSPCONFIG_NETINFO_URL = { + 'delete_ip_object': "/network/#NIC#/ipv4/#OBJ#", + 'disable_dhcp': "/network/#NIC#/attr/DHCPEnabled", 'get_netinfo': "/network/enumerate", - 'nic_ip': "/network/#NIC#/action/IP", - 'vlan': "/network/action/VLAN", + 'get_nic_netinfo': "/network/#NIC#/ipv4/enumerate", 'ipdhcp': "/network/action/Reset", + 'nic_ip': "/network/#NIC#/action/IP", 'ntpservers': "/network/#NIC#/attr/NTPServers", + 'vlan': "/network/action/VLAN", } PASSWD_URL = '/user/root/action/SetPassword' @@ -285,7 +288,7 @@ class OpenBMCRest(object): code = resp.status_code if code != requests.codes.ok: description = ''.join(data['data']['description']) - error = 'Error: [%d] %s' % (code, description) + error = '[%d] %s' % (code, description) self._print_record_log(error, cmd) raise SelfClientException(error, code) @@ -308,12 +311,12 @@ class OpenBMCRest(object): response = self.session.request(method, url, httpheaders, data=data) return self.handle_response(response, cmd=cmd) except SelfServerException as e: - e.message = 'Error: BMC did not respond. ' \ + e.message = 'BMC did not respond. ' \ 'Validate BMC configuration and retry the command.' self._print_record_log(e.message, cmd) raise except ValueError: - error = 'Error: Received wrong format response: %s' % response + error = 'Received wrong format response: %s' % response self._print_record_log(error, cmd) raise SelfServerException(error) @@ -360,13 +363,13 @@ class OpenBMCRest(object): try: data = json.loads(response) except ValueError: - error = 'Error: Received wrong format response when running command \'%s\': %s' % \ + error = 'Received wrong format response when running command \'%s\': %s' % \ (request_cmd, response) self._print_record_log(error, cmd=cmd) raise SelfServerException(error) if data['message'] != '200 OK': - error = 'Error: Failed to upload update file %s : %s-%s' % \ + error = 'Failed to upload update file %s : %s-%s' % \ (files, data['message'], \ ''.join(data['data']['description'])) self._print_record_log(error, cmd=cmd) @@ -392,7 +395,7 @@ class OpenBMCRest(object): chassis_stat = states[PROJECT_URL + '/state/chassis0']['CurrentPowerState'] return {'host': host_stat.split('.')[-1], 'chassis': chassis_stat.split('.')[-1]} except KeyError: - error = 'Error: Received wrong format response: %s' % states + error = 'Received wrong format response: %s' % states raise SelfServerException(error) def set_power_state(self, state): @@ -406,7 +409,7 @@ class OpenBMCRest(object): try: return {'bmc': state.split('.')[-1]} except KeyError: - error = 'Error: Received wrong format response: %s' % state + error = 'Received wrong format response: %s' % state raise SelfServerException(error) def reboot_bmc(self, optype='warm'): @@ -448,7 +451,7 @@ class OpenBMCRest(object): boot_state = BOOTSOURCE_GET_STATE.get(boot_source.split('.')[-1], error) return boot_state except KeyError: - error = 'Error: Received wrong format response: %s' % states + error = 'Received wrong format response: %s' % states raise SelfServerException(error) def get_beacon_info(self): @@ -462,7 +465,7 @@ class OpenBMCRest(object): beacon_dict[key_id] = value['State'].split('.')[-1] return beacon_dict except KeyError: - error = 'Error: Received wrong format response: %s' % beacon_data + error = 'Received wrong format response: %s' % beacon_data raise SelfServerException(error) def set_beacon_state(self, state): @@ -494,7 +497,7 @@ class OpenBMCRest(object): return sensor_dict except KeyError: - error = 'Error: Received wrong format response: %s' % sensor_data + error = 'Received wrong format response: %s' % sensor_data raise SelfServerException(error) def get_inventory_info(self): @@ -531,7 +534,7 @@ class OpenBMCRest(object): return inverntory_dict except KeyError: - error = 'Error: Received wrong format response: %s' % inventory_data + error = 'Received wrong format response: %s' % inventory_data raise SelfServerException(error) def list_firmware(self): @@ -586,7 +589,7 @@ class OpenBMCRest(object): eventlog_dict[str(id)] = event_log_line return eventlog_dict except KeyError: - error = 'Error: Received wrong format response: %s' % eventlog_data + error = 'Received wrong format response: %s' % eventlog_data raise SelfServerException(error) # Parse a single eventlog entry and return data in formatted string @@ -717,7 +720,7 @@ class OpenBMCRest(object): return dump_dict except KeyError: - error = 'Error: Received wrong format response: %s' % dump_data + error = 'Received wrong format response: %s' % dump_data raise SelfServerException(error) def download_dump(self, download_id, file_path): @@ -732,6 +735,50 @@ class OpenBMCRest(object): url = HTTP_PROTOCOL + self.bmcip + GARD_CLEAR_URL return self.request('POST', url, payload=payload, cmd='clear_gard') + def set_vlan(self, nic, vlan_id): + + payload = { "data": [nic, vlan_id] } + return self.request('POST', RSPCONFIG_NETINFO_URL['vlan'], payload=payload, cmd='set_vlan') + + def set_netinfo(self, nic, ip, netmask, gateway): + + payload = { "data": ["xyz.openbmc_project.Network.IP.Protocol.IPv4", ip, netmask, gateway] } + path = RSPCONFIG_NETINFO_URL['nic_ip'].replace('#NIC#', nic) + return self.request('POST', path, payload=payload, cmd='set_netinfo') + + def disable_dhcp(self, nic): + + payload = { "data": 0 } + path = RSPCONFIG_NETINFO_URL['disable_dhcp'].replace('#NIC#', nic) + return self.request('PUT', path, payload=payload, cmd='disable_dhcp') + + def delete_ip_object(self, nic, ip_object): + + path = RSPCONFIG_NETINFO_URL['delete_ip_object'].replace('#OBJ#', ip_object).replace('#NIC#', nic) + return self.request('DELETE', path, cmd='delete_ip_object') + + def get_nic_netinfo(self, nic): + + path = RSPCONFIG_NETINFO_URL['get_nic_netinfo'].replace('#NIC#', nic) + data = self.request('GET', path, cmd='get_nic_netinfo') + + try: + netinfo = {} + for k, v in data.items(): + dev,match,netid = k.partition("/ipv4/") + if 'LinkLocal' in v["Origin"] or v["Address"].startswith("169.254"): + msg = "Found LinkLocal address %s for interface %s, Ignoring..." % (v["Address"], dev) + self._print_record_log(msg, 'get_netinfo') + continue + utils.update2Ddict(netinfo, netid, 'ip', v['Address']) + utils.update2Ddict(netinfo, netid, 'ipsrc', v['Origin'].split('.')[-1]) + utils.update2Ddict(netinfo, netid, 'netmask', v['PrefixLength']) + utils.update2Ddict(netinfo, netid, 'gateway', v['Gateway']) + return netinfo + except KeyError: + error = 'Received wrong format response: %s' % data + raise SelfServerException(error) + def get_netinfo(self): data = self.request('GET', RSPCONFIG_NETINFO_URL['get_netinfo'], cmd="get_netinfo") try: @@ -755,11 +802,14 @@ class OpenBMCRest(object): if 'ip' in netinfo[nicid]: msg = "%s: Another valid ip %s found." % (node, v["Address"]) self._print_record_log(msg, 'get_netinfo') - continue + del netinfo[nicid] + netinfo['error'] = 'Interfaces with multiple IP addresses are not supported' + break utils.update2Ddict(netinfo, nicid, "ipsrc", v["Origin"].split('.')[-1]) utils.update2Ddict(netinfo, nicid, "netmask", v["PrefixLength"]) utils.update2Ddict(netinfo, nicid, "gateway", v["Gateway"]) utils.update2Ddict(netinfo, nicid, "ip", v["Address"]) + utils.update2Ddict(netinfo, nicid, "ipobj", netid) if dev in data: info = data[dev] utils.update2Ddict(netinfo, nicid, "vlanid", info.get("Id", "Disable")) @@ -771,7 +821,7 @@ class OpenBMCRest(object): utils.update2Ddict(netinfo, nicid, "ntpservers", ntpservers) return netinfo except KeyError: - error = 'Error: Received wrong format response: %s' % data + error = 'Received wrong format response: %s' % data raise SelfServerException(error) From 5ff300df35e5d60de77469db9183f77f52065e32 Mon Sep 17 00:00:00 2001 From: XuWei Date: Thu, 15 Mar 2018 23:01:00 -0400 Subject: [PATCH 2/2] modified depending on comments --- .../lib/python/agent/common/utils.py | 12 ++++------ .../agent/hwctl/executor/openbmc_bmcconfig.py | 24 +++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/xCAT-openbmc-py/lib/python/agent/common/utils.py b/xCAT-openbmc-py/lib/python/agent/common/utils.py index 5df571320..d05768a7d 100644 --- a/xCAT-openbmc-py/lib/python/agent/common/utils.py +++ b/xCAT-openbmc-py/lib/python/agent/common/utils.py @@ -82,19 +82,15 @@ def sort_string_with_numbers(origin_list): new_list.sort() return [string for __,string in new_list] -def exchange_mask(mask): +def mask_str2int(mask): count_bit = lambda bin_str: len([i for i in bin_str if i=='1']) mask_splited = mask.split('.') mask_count = [count_bit(bin(int(i))) for i in mask_splited] return sum(mask_count) -def exchange_maskint(mask_int): - bin_arr = ['0' for i in range(32)] - for i in range(mask_int): - bin_arr[i] = '1' - tmpmask = [''.join(bin_arr[i * 8:i * 8 + 8]) for i in range(4)] - tmpmask = [str(int(tmpstr, 2)) for tmpstr in tmpmask] - return '.'.join(tmpmask) +def mask_int2str(mask_int): + mask_num = (0x1 << 32) - (0x1 << (32 - mask_int)) + return "%s.%s.%s.%s" % (str((mask_num >> 24) & 0xff), str((mask_num >>16)&0xff), str((mask_num >> 8) & 0xff), str(mask_num & 0xff)) class Messager(object): def __init__(self, name=None): diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py index 46045c361..1c278e70d 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py @@ -167,7 +167,7 @@ class OpenBMCBmcConfigTask(ParallelNodesCommand): if (20-i) % 8 == 0: self.callback.info('%s: Still waiting for dump %s to be generated... ' % (node, dump_id)) - gevent.sleep( 15 ) + sleep( 15 ) if flag: self._dump_download(obmc, node, str(dump_id), flag_dump_process=True) @@ -430,7 +430,9 @@ rmdir \"/tmp/$userid\" \n") if not nic: return self.callback.error('Can not get facing NIC for %s' % bmcip, node) - if (ip == netinfo[nic]['ip'] and netmask == utils.exchange_maskint(netinfo[nic]['netmask']) and + prefix = int(utils.mask_str2int(netmask)) + + if (ip == netinfo[nic]['ip'] and prefix == netinfo[nic]['netmask'] and gateway == netinfo[nic]['gateway']): if not vlan or vlan == str(netinfo[nic]['vlanid']): self._print_bmc_netinfo(node, ip, netmask, gateway, vlan) @@ -443,31 +445,29 @@ rmdir \"/tmp/$userid\" \n") pre_nic = nic.split('_')[0] try: obmc.set_vlan(pre_nic, vlan) - gevent.sleep( 15 ) + sleep( 15 ) except (SelfServerException, SelfClientException) as e: self.callback.error(e.message, node) return nic = pre_nic + '_' + vlan - prefix = int(utils.exchange_mask(netmask)) try: obmc.set_netinfo(nic, ip, prefix, gateway) - gevent.sleep( 5 ) + sleep( 5 ) nic_netinfo = obmc.get_nic_netinfo(nic) except (SelfServerException, SelfClientException) as e: self.callback.error(e.message, node) return - if not netinfo: + if not nic_netinfo: return self.callback.error('Did not get info for NIC %s' % nic, node) set_success = False for net_id, attr in nic_netinfo.items(): - if attr['ip'] == ip: - attr_netmask = utils.exchange_maskint(attr["netmask"]) - if (attr_netmask == netmask and - attr['gateway'] == gateway): - set_success = True + if (attr['ip'] == ip and + attr["netmask"] == prefix and + attr['gateway'] == gateway): + set_success = True if not set_success: return self.callback.error('Config BMC IP failed', node) @@ -519,7 +519,7 @@ rmdir \"/tmp/$userid\" \n") if dic_length > 1: addon_string = " for %s" % nic netinfodict['ip'].append("BMC IP"+addon_string+": %s" % attrs["ip"]) - netinfodict['netmask'].append("BMC Netmask"+addon_string+": %s" % utils.exchange_maskint(attrs["netmask"])) + netinfodict['netmask'].append("BMC Netmask"+addon_string+": %s" % utils.mask_int2str(attrs["netmask"])) netinfodict['gateway'].append("BMC Gateway"+addon_string+": %s (default: %s)" % (attrs["gateway"], defaultgateway)) netinfodict['vlan'].append("BMC VLAN ID"+addon_string+": %s" % attrs["vlanid"]) netinfodict['ipsrc'].append("BMC IP Source"+addon_string+": %s" % attrs["ipsrc"])