def _set_power_state(self, state): """ Applies the requested power state on the system. :param str state: Supported values are On, ForceOff, ForceRestart, PushPowerButton :return: :bool True on success else False """ uri = self.client.get_resource_uris('ComputerSystem.')[0] # For Rack server, there would ideally be only one item. if self.client.type_path.defs.isgen9: r = self.client.post(uri['@odata.id'], {'Action': 'Reset', 'ResetType': state}) else: pr = self.client.get(uri['@odata.id']) if pr.status == 200: p = pr.dict['Actions']['#ComputerSystem.Reset']['target'] else: _ = 'Unable to gather resource path to perform {}'.format(state) logger.debug(_) return False r = self.client.post(p, {'Action': 'ComputerSystem.Reset', 'ResetType': state}) e = self.client.get_message(r) if e.get('error_code') == 'Success': return True elif e.get('error_code') == 'InvalidOperationForSystemState': # System was in the requested state hence passing it logger.info(e.get('error_info')) return True return False
def _validate_and_set_host(self, host): """ Verifies and adds the target information to the body :param dict host: Contains :return bool: True if the payload is updated with host information """ attrs = ['ipv4', 'user', 'password'] if not host.get('os'): log.debug('Missing required attribute: os') return False if not set(attrs).issubset(host.keys()): m = 'Found attributes: {}'.format(host.keys()) m += '\nRequired attributes: {}'.format(attrs) log.info(m) return False if host.get('datacenter'): if host.get('manager') is None: log.info('Missing required attribute: manager') return False self.body['host'] = deepcopy(host) log.debug('Host information found {}'.format(host)) return True
def _verify_host(self, host): """ Verifies all the required attributes of the host is available or not :param dict host: Key-value pair of host section :return bool: True when all exists else False """ attrs = ['ipv4', 'user', 'password'] if not set(attrs).issubset(host.keys()): m = 'Required attributes: {}'.format(attrs) m += '\nFound attributes: {}'.format(host.keys()) log.info(m) return False self.host = deepcopy(host) return True
def _verify_network_info(net): """ Verify the key-value pairs of the provided network address :param net: :return: """ if not net.get('dhcp'): if net.get('ipv4', None) is None and net.get('ipv6', None) is None: log.info('Keys: ipv4 or ipv6 is required') return False if net.get('vlan'): if net.get('vlan') < 0 or net.get('vlan') > 4095: log.info('VLAN ID: {} is invalid.'.format(net['vlan'])) return False return True
def _get_network_uri(self, name): """ Retrieve the networkUri from OneView based on the argument :param str name: Network name :return str: Network's URI corresponding to OV """ p = '?&filter="\'name\'==\'{}\'"'.format(name.lower()) r = self.fusion_api_get_ethernet_networks(param=p) if r.get('count') == 0: r = self.fusion_api_get_network_set(param=p) if r.get('count') == 0: log.info('{} not found in OneView'.format(name)) return None if r.get('count') > 1: log.info('Found more than one match for {}'.format(name)) return None log.debug('Retrieved the network uri for {}'.format(name)) return r.get('members')[0]['uri']
def configure_network(self, body): """ Calls the appropriate configure network method based on the OS type Example: @{r}= RG Configure Networks | <config> <config> is a dictionary and the supported key-value pairs are available in the respective functions. :param dict body: Key-value pairs containing network configuration :return list: List of network configuration status """ if not body.get('host'): log.info('Target system information is required') raise AssertionError('Host information dictionary is missing.') t = body.get('host').get('os', None) if t is None: log.info('Operating System type is required') raise AssertionError('Operating system type attribute missing') if "redhat" in t.lower() or "red hat" in t.lower() or \ "rhel" in t.lower(): return RedHatNetConfig().configure_network(body) elif "vmware" in t.lower() or "esx" in t.lower(): return VMwareNetConfig().configure_network(body) else: log.info('{} not supported'.format(body['host']['os'])) raise AssertionError('OS type not supported')
def _network_config_generator(self, sp_url, host_info, nets): """ Keyword to generate the required payload for configuring the target system networks Usage ${body}= Create Payload For OS Networks Configuration | ... <ov_server_profile_url> | ... <host_info> | ... <network_profiles> | [Returns] A dictionary which would be an input to 'Configure OS Networks' :param str sp_url: Target system's OneView server profile URL :param dict host_info: Target system's credentials and type :param list nets: List of network profiles :return: """ sp = self.fusion_api_get_server_profiles(uri=sp_url) if sp['status_code'] != 200: log.info("Invalid OneView Server Profile URL: {}".format(sp_url)) return self.body if int(sp['type'].split('V')[-1]) < 8: self._set_ports(sp.get('connections', list())) else: self._set_ports(sp.get('connectionSettings').get('connections')) if not self._validate_and_set_host(host_info): log.info("Invalid target server information: {}".format(host_info)) return self.body for net in nets: if self._add_network_profile(net): log.debug('{} network configuration created.'.format(net)) return self.body if len(self.body['config']) else dict()
def _add_network_profile(self, net_profile): """ In conjunction with ports, creates the configuration dictionary :param dict net_profile: Network profile information :return bool: True when the configuration is added to self.body """ net_types = list() if 'redhat' in self.body['host']['os'].lower() or \ 'red hat' in self.body['host']['os'] or \ 'rhel' in self.body['host']['os']: net_types = ['ethernet', 'bond', 'team'] elif 'vmware' in self.body['host']['os'].lower(): net_types = ['vss', 'vds'] if not net_profile.get('type') or \ not net_profile['type'].lower() in net_types: log.info('Unknown network config: {}'.format(net_profile['type'])) log.info('Supported network configurations: {}'.format(net_types)) return False net_uri = self._get_network_uri(net_profile.get('ov_network')) if net_uri is None: return False if net_uri not in self.ports: log.info('Network not part of server profile connections {}') return False self.net_profile = deepcopy(net_profile) if 'redhat' in self.body['host']['os'].lower() or \ 'red hat' in self.body['host']['os'] or \ 'rhel' in self.body['host']['os']: return self._add_rhel_config(net_profile['ov_network'], net_uri) elif 'vmware' in self.body['host']['os'].lower(): return self._add_vmware_config(net_uri) log.debug('Unknown network profile type {}'.format( net_profile['type'])) return False
def configure_network(self, body): """ Entry point for configuring the network interfaces. Based on the network type, the appropriate configurator is executed. Below are the supported options Input ------ [host] ipv4 -| Target system IPv4 address os -| Operating system installed on the target user -| Root privileged account password -| Password of the provided user [config] ports -| List of MAC addresses or device names - str type -| Network configuration type - ETHERNET, BOND or TEAM name | Name of the connection required for BOND & TEAM bond_opts | Bond options for BOND network type team_opts | Team options for TEAM network type team_port_cfg | Required for TEAM type - configuration values [networks] | List of items having the below configurations vlan | VLAN ID for the network dhcp -| True when address is received from DHCP server else ipv4 | IPv4 network address ipv6 | IPv6 network address netmask | Network mask -| keys are mandatory Output ------- List of { "port": '', "status": <Pass | Fail> } :param dict body: Dictionary containing key-values from the above :return dict: Status for each network configuration """ if len(body.get('config', list())) == 0: log.info('No configurations to be applied') return self.result if body.get('host', None) is None or not \ self._verify_host(body['host']): return self.result if not self._remote_cmd('modprobe bonding && modprobe 8021q'): log.info('VLAN and bonding modules missing.') return self.result if not self._remote_cmd('which nmcli'): log.info('Missing required utility: nmcli') return self.result for c in body['config']: if len(c.get('ports', [])) == 0 or len(c.get('networks', [])) == 0 \ or c.get('type', None) is None: log.info('Missing network information') self.result.append({ 'ports': c.get('ports', 'Unknown'), 'status': 'Fail' }) continue if c['type'].lower() == 'ethernet': self._configure_ethernet(c) elif c['type'].lower() == 'bond' or c['type'].lower() == "team": if len(c['ports']) != 2: log.info('Incorrect number of interfaces provided.') self.result.append({'ports': c['ports'], 'status': 'Fail'}) continue if c.get('name', None) is None: log.info('Missing required attribute: name') self.result.append({'ports': c['ports'], 'status': 'Fail'}) continue if c['type'].lower() == "bond": self._configure_bond(c) else: self._configure_team(c) else: log.info('Unknown network port type: {}'.format(c['ports'])) self.result.append({'ports': c['ports'], 'status': 'Fail'}) self._remote_cmd('systemctl restart network.service', False) return self.result
def _configure_team(self, cfg): """ Method to configure network connections of type TEAM :param dict cfg: Dict containing information of network connections :return: None """ if cfg.get('team_opts') is None: log.info('Missing required attribte: team_opts') self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return if cfg.get('team_port_cfg') is None or len(cfg['team_port_cfg']) != 2: log.info('Missing required attributes: team_port_cfg') self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return # Create Team Master cmd = 'nmcli connection add type team ifname {}'.format(cfg['name']) cmd += ' con-name {}'.format(cfg['name']) cmd += " team.options '{}'".format(cfg['team_opts']) cmd += ' ipv4.method disabled ipv6.method ignore' if self._remote_cmd(cmd): log.info('{} master configuration succeeded.'.format(cfg['name'])) else: log.info('{} master configuration failed.'.format(cfg['name'])) self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return for net in cfg['networks']: flag = False cname = cfg['name'] if self._verify_network_info(net): vlan_id = 0 if not net.get('vlan') else net['vlan'] cname += '.{}'.format(vlan_id) cmd = 'nmcli connection add type vlan' cmd += ' ifname {} con-name {}'.format(cname, cname) cmd += ' id {} dev {}'.format(vlan_id, cfg['name']) if not net.get('dhcp'): if net.get('ipv4'): cmd += ' ip4 {}'.format(net['ipv4']) else: cmd += ' ip6 {}'.format(net['ipv6']) if net.get('netmask'): b = sum([ bin(int(x)).count("1") for x in net['netmask'].split(".") ]) cmd += '/{}'.format(b) if self._remote_cmd(cmd): flag = True log.info('{} configuration succeeded'.format(cname)) else: log.info('{} configuration failed'.format(cname)) else: log.info('Incorrect network information {}'.format(net)) if flag: self.result.append({"ports": cname, 'status': 'Pass'}) else: self.result.append({"ports": cname, 'status': 'Fail'}) # Team master is not connect till all team-ports come on-line # This prevents existing connection drops. for i in range(len(cfg['ports'])): p = self._get_device_info(cfg['ports'][i]) if p is None: log.info('Invalid port: {}'.format(cfg['ports'][i])) self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return cmd = 'nmcli connection add team-slave' cmd += ' ifname {} con-name {}-{}'.format(p['device_name'], cfg['name'], p['device_name']) cmd += ' master {}'.format(cfg['name']) cmd += " team-port.options '{}'".format(cfg['team_port_cfg'][i]) if self._remote_cmd(cmd): log.info('{}-{} configuration passed'.format( cfg['name'], p['device_name'])) else: log.info('{}-{} configuration failed'.format( cfg['name'], p['device_name'])) self.result.append({ 'ports': p['device_name'], 'status': 'Fail' }) return _ = 'Network configuration completed for port: {}'.format(cfg['ports']) log.info(_)
def _configure_bond(self, cfg): """ Method to configure network connections of type BOND :param dict cfg: Dict containing information of network connections :return: None """ if cfg.get('bond_opts') is None: log.info('Missing required attribute: bond_opts') self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return # Create Bond master cmd = 'nmcli connection add type bond ifname {}'.format(cfg['name']) cmd += ' con-name {}'.format(cfg['name']) cmd += " bond.options '{}'".format(cfg['bond_opts']) cmd += ' ipv4.method disabled ipv6.method ignore' if not self._remote_cmd(cmd): log.info('{} configuration failed.'.format(cfg['name'])) self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return else: log.info('{} configuration succeed.'.format(cfg['name'])) for net in cfg['networks']: flag = False cname = cfg['name'] if self._verify_network_info(net): vlan_id = 0 if not net.get('vlan') else net['vlan'] cname += '.{}'.format(vlan_id) cmd = 'nmcli connection add type vlan' cmd += ' ifname {} con-name {}'.format(cname, cname) cmd += ' id {} dev {}'.format(vlan_id, cfg['name']) if not net.get('dhcp'): if net.get('ipv4'): cmd += ' ip4 {}'.format(net['ipv4']) else: cmd += ' ip6 {}'.format(net['ipv6']) if net.get('netmask'): b = sum([ bin(int(x)).count("1") for x in net['netmask'].split(".") ]) cmd += '/{}'.format(b) if self._remote_cmd(cmd): flag = True log.info('{} configuration succeeded'.format(cname)) else: log.info('{} configuration failed'.format(cname)) else: log.info('Incorrect network information {}'.format(net)) if flag: self.result.append({"ports": cname, 'status': 'Pass'}) else: self.result.append({"ports": cname, 'status': 'Fail'}) # Bond master is not up till bond-slaves come online. This prevents # existing connectivity drops for port in cfg['ports']: p = self._get_device_info(port) if p is None: log.info('Invalid port: {}'.format(port)) self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return cmd = 'nmcli connection add type bond-slave' cmd += ' ifname {} con-name {}-{}'.format(p['device_name'], cfg['name'], p['device_name']) cmd += ' master {}'.format(cfg['name']) if not self._remote_cmd(cmd): _ = '{} port configuration failed.'.format(p['device_name']) log.info(_) self.result.append({ 'ports': p['device_name'], 'status': 'Fail' }) return else: _ = '{} bond slave port configured.'.format(p['device_name']) log.info(_) _ = 'Network configuration completed for port: {}'.format(cfg['ports']) log.info(_)
def _configure_ethernet(self, cfg): """ Method to configure network connections of type ETHERNET :param dict cfg: Dict containing information of network connections :return: None """ if len(cfg['ports']) != 1: log.info('Invalid number of port provided for Ethernet network.') self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return p = self._get_device_info(cfg['ports'][0]) if p is None: log.info('Invalid port information: {}'.format(cfg['ports'])) self.result.append({'ports': cfg['ports'], 'status': 'Fail'}) return for net in cfg['networks']: flag = False cname = p['device_name'] if self._verify_network_info(net): cmd = 'nmcli connection add' if net.get('vlan'): cname += '.{}'.format(net['vlan']) cmd += ' type vlan con-name {}'.format(cname) cmd += ' dev {} id {}'.format(p['device_name'], net['vlan']) else: cmd += ' type ethernet con-name {}'.format(cname) cmd += ' ifname {}'.format(p['device_name']) if not net.get('dhcp'): if net.get('ipv4'): cmd += ' ip4 {}'.format(net['ipv4']) else: cmd += ' ip6 {}'.format(net['ipv6']) if net.get('netmask'): b = sum([ bin(int(x)).count("1") for x in net['netmask'].split(".") ]) cmd += '/{}'.format(b) if self._remote_cmd(cmd): flag = True log.info('Network {} configured.'.format(cname)) else: log.info('Network {} failed.'.format(cname)) else: log.info('Incorrect network information {}'.format(net)) if flag: self.result.append({"ports": cname, 'status': 'Pass'}) else: self.result.append({"ports": cname, 'status': 'Fail'}) _ = 'Network configuration completed for port: {}'.format(cfg['ports']) log.info(_)
def _add_vmware_config(self, uri): """ Creates the network configuration for VMware based systems :param str uri: OneView's networkUri :return bool: True when network configuration is appended to body """ if self.net_profile.get('name') is None: log.info('Virtual Switch name is required') return False if self.net_profile.get('type').lower() == 'vds': attrs = ['manager', 'datacenter', 'cluster'] if not set(attrs).issubset(self.body['host'].keys()): m = 'Missing required attributes.' m += "Additional required attributes are\t{}".format(attrs) log.info(m) return False cfg = dict({ 'name': self.net_profile['name'], 'type': self.net_profile['type'], 'ports': self.ports[uri] }) nets = list() if 'ethernet-network' in uri: n = self._get_network_addr(uri, True) if n is None: return False if self.net_profile.get('networks') and \ self.net_profile['networks'].get(n['vlan']): if 'policy' in self.net_profile['networks'][n['vlan']]: n['policy'] = \ self.net_profile['network'][n['vlan']]['policy'] if 'vmk' in self.net_profile['network'][n['vlan']]: n['vmk'] = self.net_profile['network'][n['vlan']]['vmk'] else: # No vmkernel port would be created n['vmk'] = False else: n['vmk'] = False # VLAN is configured on the downlink port del n['vlan'] nets.append(n) elif 'network-set' in uri: ns = self.fusion_api_get_network_set(uri=uri) if ns.get('nativeNetworkUri'): n = self._get_network_addr(ns['nativeNetworkUri'], True) if n is not None: if self.net_profile.get('networks') and \ self.net_profile['networks'].get(n['vlan']): if 'policy' in self.net_profile['networks'][n['vlan']]: n['policy'] = \ self.net_profile['networks'][n['vlan']]['policy'] if 'vmk' in self.net_profile['networks'][n['vlan']]: n['vmk'] = \ self.net_profile['networks'][n['vlan']]['vmk'] else: n['vmk'] = False else: n['vmk'] = False del n['vlan'] nets.append(n) for u in ns.get('networkUris', []): if u == ns.get('nativeNetworkUri'): log.debug('Ignoring this network as it is a native one.') continue n = self._get_network_addr(u, True) if n is None: continue if self.net_profile.get('networks') and \ self.net_profile['networks'].get(n['vlan']): if 'policy' in self.net_profile['networks'][n['vlan']]: n['policy'] = \ self.net_profile['networks'][n['vlan']]['policy'] if 'vmk' in self.net_profile['networks'][n['vlan']]: n['vmk'] = \ self.net_profile['networks'][n['vlan']]['vmk'] else: n['vmk'] = False else: n['vmk'] = False nets.append(n) cfg['active'] = self.net_profile.get('active', len(cfg['ports'])) else: log.info('Unsupported network connection {}'.format(uri)) log.info('Supported types: vss, vds') if nets: cfg['networks'] = nets self.body['config'].append(cfg) log.debug('Network config: {}'.format(cfg)) return True log.debug('No network information was retrieved... skipping') return False
def _add_rhel_config(self, name, uri): """ Creates the network configuration for RHEL based systems :param str name: Name of the network name :param str uri: OneView's networkUri :return bool: True when network configuration is appended to body """ port_count = len(self.ports[uri]) if self.net_profile['type'].lower() == 'ethernet' and port_count != 1: _ = 'Invalid number of ports {}'.format(name) log.info(_) return False if (self.net_profile['type'].lower() == 'bond' or self.net_profile['type'].lower() == 'team') and \ port_count != 2: _ = 'Invalid number of port for {}'.format(name) log.info(_) return False cfg = { 'name': self.net_profile.get('name', name), 'type': self.net_profile['type'], 'ports': self.ports[uri] } if self.net_profile['type'].lower() == 'bond': cfg.update({'bond_opts': self.net_profile['bond_opts']}) elif self.net_profile['type'].lower() == 'team': cfg.update({ 'team_opts': self.net_profile['team_opts'], 'team_port_cfg': self.net_profile['team_port_cfg'] }) nets = list() if 'ethernet-network' in uri: n = self._get_network_addr(uri) if n is not None: # VLAN is configured on the interconnect if n.get('vlan'): # Removing VLAN as it is a single network and Oneview # configures the same on the interconnect downlink port del n['vlan'] nets.append(n) elif 'network-set' in uri: r = self.fusion_api_get_network_set(uri=uri) if r.get('nativeNetworkUri'): n = self._get_network_addr(r['nativeNetworkUri']) if n is not None: if n.get('vlan'): # Removing VLAN as it is native to network set and # configured by Oneview on the interconnect downlink del n['vlan'] nets.append(n) for u in r.get('networkUris', []): if u == r.get('nativeNetworkUri'): log.debug('Ignoring this network as it is native') continue n = self._get_network_addr(u) if n is not None: nets.append(n) else: log.info('Unknown OneView network connection: {}'.format(uri)) log.info('Supported types: ethernet, network-set') return False if nets: cfg['networks'] = deepcopy(nets) self.body['config'].append(cfg) log.debug('Network config: {}'.format(cfg)) return True log.debug('No network information retrieved... skipping') return False