#!/usr/bin/env perl #--------------------------------------------------------- # Configure Ethnet Mellonax switches #--------------------------------------------------------- BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; $::XCATDIR = $ENV{'XCATDIR'} ? $ENV{'XCATDIR'} : '/etc/xcat'; } use lib "$::XCATROOT/lib/perl"; use strict; use Getopt::Long; use Expect; use Net::Ping; use xCAT::Usage; use xCAT::NodeRange; use xCAT::NetworkUtils; use xCAT::Utils; use xCAT::Table; use xCAT::MsgUtils; Getopt::Long::Configure("bundling"); $Getopt::Long::ignorecase = 0; # global variables my @nodes; my @filternodes; #--------------------------------------------------------- # Main # parse the options if ( !GetOptions( 'h|help' => \$::HELP, 'switches=s' => \$::SWITCH, 'config' => \$::CONFIG, 'ip' => \$::IP, 'name' => \$::NAME, 'snmp' => \$::SNMP, 'accessvlan=s' => \$::PVID, 'allowvlan=s' => \$::VID, 'port=s' => \$::PORT, 'mode=s' => \$::MODE, 'all' => \$::ALL, ) ) { &usage; exit(1); } # display the usage if -h or --help is specified if ($::HELP) { &usage; exit(0); } if ($::SWITCH) { my @filternodes = xCAT::NodeRange::noderange( $::SWITCH ); if (nodesmissed) { my $nodenotdefined = join(',', nodesmissed); xCAT::MsgUtils->message("I","The following nodes are not defined in xCAT DB: $nodenotdefined"); } # check switch type my $switchestab = xCAT::Table->new('switches'); my $switches_hash = $switchestab->getNodesAttribs(\@filternodes,['switchtype']); foreach my $fsw (@filternodes) { if (($switches_hash->{$fsw}->[0]->{switchtype}) =~ /Mellanox/) { push @nodes, $fsw; } else { xCAT::MsgUtils->message("E","The $fsw is not Mellanox switch, will not config"); } } unless (@nodes) { xCAT::MsgUtils->message("E","No Valid Switch to process"); exit(1); } } else { xCAT::MsgUtils->message("E","Invalid flag, please provide switches with --switches"); &usage; exit(1); } my $switches = join(",",@nodes); my $user; my $cmd; my $rc; my $master; my $vlan; my $port; my $mode; # set community string for switch my $community = "public"; my @snmpcs = xCAT::TableUtils->get_site_attribute("snmpc"); my $tmp = $snmpcs[0]; if (defined($tmp)) { $community = $tmp } # get master my @masters = xCAT::TableUtils->get_site_attribute("master"); my $master = $masters[0]; if (($::IP) || ($::ALL)) { config_ip(); } if (($::NAME) || ($::ALL)) { config_hostname(); } if (($::SNMP) || ($::ALL)) { config_snmp(); } if (($::CONFIG) || ($::ALL)) { run_rspconfig(); } if ( ($::PVID) || ($::VID) ) { config_vlan(); } sub config_ip { my @config_switches; my @discover_switches; # get host table for otherinterfaces my $nodetab = xCAT::Table->new('hosts'); my $nodehash = $nodetab->getNodesAttribs(\@nodes,['ip','otherinterfaces']); # get netmask from network table my $nettab = xCAT::Table->new("networks"); my @nets; if ($nettab) { @nets = $nettab->getAllAttribs('net','mask'); } foreach my $switch (@nodes) { my $dip= $nodehash->{$switch}->[0]->{otherinterfaces}; if (!$dip) { print "Add otherinterfaces attribute for discover ip: chdef $switch otherinterfaces=x.x.x.x\n"; next; } # Validate if this IP is reachable my $p = Net::Ping->new(); if (!$p->ping($dip)) { print "$dip is not reachable\n"; next; } my $static_ip= $nodehash->{$switch}->[0]->{ip}; # don't need to set if ip addresses are same if ($dip eq $static_ip) { print "static ip $static_ip and discovery ip $dip is same, will not process command for $switch\n"; next; } # get hostname my $dswitch = xCAT::NetworkUtils->gethostname($dip); # if hostnames are same, created different one for discovery name if ($dswitch eq $switch) { $dswitch=""; } # if not defined, need to create one for xdsh to use if (!$dswitch) { my $ip_str = $dip; $ip_str =~ s/\./\-/g; $dswitch = "switch-$ip_str"; } $cmd = "chdef -t node -o $dswitch groups=switch ip=$dip switchtype=Mellanox username=admin password=admin mgt=switch nodetype=switch"; $rc= xCAT::Utils->runcmd($cmd, 0); $cmd = "makehosts $dswitch"; $rc= xCAT::Utils->runcmd($cmd, 0); $cmd = "makedns $dswitch"; $rc= xCAT::Utils->runcmd($cmd, 0); $cmd="rspconfig $dswitch sshcfg=enable"; $rc= xCAT::Utils->runcmd($cmd, 0); if ($::RUNCMD_RC != 0) { xCAT::MsgUtils->message("E"," Failed to config ssh passwordless for $dip"); print "Failed to config ssh passwordless for $dswitch, $dip\n"; push (@discover_switches, $dswitch); next; } # verify if xdsh works $cmd = "xdsh $dswitch -l admin --devicetype IBSwitch::Mellanox 'enable;configure terminal;exit' "; $rc= xCAT::Utils->runcmd($cmd, 0); if ($::RUNCMD_RC != 0) { xCAT::MsgUtils->message("E","Couldn't communicate with $dip"); print "$cmd failed, Couldn't communicate with $dswitch, $dip\n"; push (@discover_switches, $dswitch); next; } # get netmask my $mask; foreach my $net (@nets) { if (xCAT::NetworkUtils::isInSameSubnet( $net->{'net'}, $static_ip, $net->{'mask'}, 0)) { $mask=$net->{'mask'}; } } $cmd="xdsh $dswitch -t 10 -l admin --devicetype IBSwitch::Mellanox 'enable;configure terminal;no interface mgmt0 dhcp;interface mgmt0 ip address $static_ip $mask;configuration write;exit;exit' "; $rc= xCAT::Utils->runcmd($cmd, 0); if (!$p->ping($static_ip)) { print "$switch: Failed to change ip address to $static_ip\n"; next; } push (@discover_switches, $dswitch); push (@config_switches, $switch); print "$switch: Changing IP address to static IP $static_ip\n"; } if (@config_switches) { # update switch status my $csw = join(",",@config_switches); $cmd = "chdef $csw status=ip_configed otherinterfaces="; $rc= xCAT::Utils->runcmd($cmd, 0); $cmd = "makehosts $csw"; $rc= xCAT::Utils->runcmd($cmd, 0); } if (@discover_switches) { my $dsw = join(",",@discover_switches); # remove discover switch from xCATdb and /etc/hosts $cmd = "makedns -d $dsw"; $rc= xCAT::Utils->runcmd($cmd, 0); $cmd = "makehosts -d $dsw"; $rc= xCAT::Utils->runcmd($cmd, 0); $cmd = "rmdef $dsw"; $rc= xCAT::Utils->runcmd($cmd, 0); } } sub config_hostname { my @config_switches; my $switchtab = xCAT::Table->new('switches'); my $switchhash = $switchtab->getNodesAttribs(\@nodes,['sshusername']); # get the site domain value my @domains = xCAT::TableUtils->get_site_attribute("domain"); my $sitedomain = $domains[0]; foreach my $switch (@nodes) { my $user= $switchhash->{$switch}->[0]->{sshusername}; if (!$user) { print "switch ssh username is not defined, add default one\n"; $cmd = "chdef $switch username=admin"; $rc= xCAT::Utils->runcmd($cmd, 0); $user="admin"; } $cmd = "makehosts $switch"; $rc= xCAT::Utils->runcmd($cmd, 0); $cmd="xdsh $switch -l $user --devicetype IBSwitch::Mellanox 'enable;configure terminal;hostname $switch;ip name-server $master;ip domain-list $sitedomain;ip default-gateway $master;configuration write' "; $rc= xCAT::Utils->runcmd($cmd, 0); if ($::RUNCMD_RC != 0) { xCAT::MsgUtils->message("E","Failed to setup hostname for $switch"); next; } push (@config_switches, $switch); print "$switch: Changing host name to $switch\n"; } if (@config_switches) { # update switch status my $csw = join(",",@config_switches); $cmd = "chdef $csw status=hostname_configed" ; $rc= xCAT::Utils->runcmd($cmd, 0); } } # setup secure SNMP v3 sub config_snmp { my $snmp_user; my $snmp_passwd; my $snmp_auth; my $snmp_version; my $cmd; my $switchtab = xCAT::Table->new('switches'); my $switchhash = $switchtab->getNodesAttribs(\@nodes,['snmpversion','sshusername','sshpassword','username','password','auth','privacy']); foreach my $switch (@nodes) { #enable snmp function on the switch $cmd=`rspconfig $switch snmpcfg=enable`; my $user = $switchhash->{$switch}->[0]->{sshusername}; if (!$user) { print "switch ssh username is not defined, use default one\n"; $user="admin"; } $snmp_version = $switchhash->{$switch}->[0]->{snmpversion}; $snmp_passwd = $switchhash->{$switch}->[0]->{password}; #config snmpv1 with community string defined in the switches table unless ($snmp_version =~ /3/) { if ($snmp_passwd) { $cmd=`rspconfig $switch community=$snmp_passwd`; print "config snmp community string $snmp_passwd\n"; next; } } #config snmpv3 defined in the switches table $snmp_user = $switchhash->{$switch}->[0]->{username}; if (!$snmp_user) { print "No snmp user defined for switch $switch. Will not configure snmpv3\n"; next; } $snmp_auth = $switchhash->{$switch}->[0]->{auth}; my $snmp_privacy = $switchhash->{$switch}->[0]->{privacy}; my $cmd_prefix = "xdsh $switch -l $user --devicetype IBSwitch::Mellanox"; my $cmd; # Build up the commands for easier readability $cmd = $cmd . "enable\;"; $cmd = $cmd . "configure terminal\;"; $cmd = $cmd . "snmp-server user $snmp_user v3 enable\;"; if ($snmp_privacy) { $cmd = $cmd . "snmp-server user $snmp_user v3 auth $snmp_auth $snmp_passwd priv $snmp_privacy $snmp_passwd\;"; } else { $cmd = $cmd . "snmp-server user $snmp_user v3 auth $snmp_auth $snmp_passwd\; no snmp-server user $snmp_user v3 require-privacy\;"; } print "$switch: snmpv3 configured\n"; $cmd = $cmd . "configuration write\;exit\;"; my $final_cmd = $cmd_prefix . " \"" . $cmd . "\""; `$final_cmd` } } sub run_rspconfig { my @config_switches; my $switchtab = xCAT::Table->new('switches'); my $switchhash = $switchtab->getNodesAttribs(\@nodes,['sshusername']); foreach my $switch (@nodes) { my $user= $switchhash->{$switch}->[0]->{sshusername}; # enable the snmp trap $cmd=`rspconfig $switch alert=enable`; # Logging destination: $cmd=`rspconfig $switch logdest=$master`; # config ntp $cmd = "xdsh $switch -l $user --devicetype IBSwitch::Mellanox 'enable;configure terminal;ntp enable;ntpdate $master; ntp server $master;configuration write;show ntp' "; $rc= xCAT::Utils->runcmd($cmd, 0); push (@config_switches, $switch); } if (@config_switches) { # update switch status my $csw = join(",",@config_switches); $cmd = "chdef $csw status=switch_configed" ; $rc= xCAT::Utils->runcmd($cmd, 0); } } sub config_vlan { my @ports; my $port_input; # checking for port number, switches is checked earlier if ($::PORT) { $port_input = $::PORT; foreach my $num_str (split /,/, $port_input) { if ($num_str =~ /-/) { my ($min, $max) = split (/-/,$num_str); while ($min <= $max) { push (@ports,$min); $min++; } } else { push (@ports,$num_str); } } } else { xCAT::MsgUtils->message("E"," When configuring VLAN, a port must be provided."); &usage; exit(1); } my $access_vlan = $::PVID; my $allowed_vlan = $::VID; # will default to trunk mode if ($::MODE) { $mode = $::MODE; if (!($mode =~ m/(access|trunk|hybrid|access-dcb|dot1q-tunnel)/) ) { xCAT::MsgUtils->message("E"," Please provided supported mode"); &usage; exit(1); } if ($mode =~ /hybrid/) { if (!$access_vlan) { print "NOTE: Hybrid mode will change access VLAN back to 1\n"; print "If other than 1, run the command again with access VLAN number: \n"; print " configMellanox --switches switchnames --port port --accessvlan vlan1 --allowvlan vlan2 --mode mode\n"; $access_vlan = 1; } } } else { $mode = "access"; } foreach my $switch (@nodes) { my $devicetype; # check if it is ethernet switch or ib switch my $ccmd = "snmpwalk -Os -v1 -c $community $switch 1.3.6.1.2.1.1.1"; my $result = xCAT::Utils->runcmd($ccmd, 0); # only supports MSX1410 and MSX1400 for Mellanox Ethernet switch now if ( $result =~ /MSX14/ ) { $devicetype = "EthSwitch::Mellanox"; }else { xCAT::MsgUtils->message("E","Config IB switch VLAN is not support yet"); $devicetype = "IBSwitch::Mellanox"; next; } print "Tagging access VLAN to $access_vlan and allowed VLAN to $allowed_vlan with $mode mode for $switch port $port_input\n"; my $cmd_prefix = "xdsh $switch --devicetype $devicetype"; foreach my $port (@ports) { my $cmd; # Build up the commands for easier readability $cmd = $cmd . "enable\;"; $cmd = $cmd . "configure terminal\;"; if ($access_vlan){ $cmd = $cmd . "vlan $access_vlan\;"; $cmd = $cmd . "exit\;"; } if ($allowed_vlan){ $cmd = $cmd . "vlan $allowed_vlan\;"; $cmd = $cmd . "exit\;"; } $cmd = $cmd . "interface ethernet 1/$port\;"; $cmd = $cmd . "switchport mode $mode\;"; if ($mode =~ /access/) { $cmd = $cmd . "switchport access vlan $access_vlan\;"; } elsif ($mode =~ /hybrid/) { $cmd = $cmd . "switchport $mode allowed-vlan $allowed_vlan\;"; $cmd = $cmd . "switchport access vlan $access_vlan\;"; } else { $cmd = $cmd . "switchport $mode allowed-vlan $allowed_vlan\;"; } $cmd = $cmd . "exit\;exit\;exit\;"; my $final_cmd = $cmd_prefix . " \"" . $cmd . "\""; `$final_cmd` } } } #--------------------------------------------------------- =head3 usage Displays message for -h option =cut #--------------------------------------------------------- sub usage { print "Usage: configMellanox -h│--help configMellanox --switches switchnames --ip configMellanox --switches switchnames --name configMellanox --switches switchnames --snmp To set the ip address, hostname, config snmp and run rspconfig command: configMellanox --switches switchnames --all To run rspconfig command: configMellanox --switches switchnames --config To configure VLAN on a specified port (Mellanox Ethernet switch ONLY): configMellanox --switches switchnames --port port --accessvlan vlan1 --allowvlan vlan2 --mode mode The following mode are supported for switchport: * access Only untagged ingress Ethernet packets are allowed * trunk Only tagged ingress Ethernet packets are allowed * hybrid Both tagged and untagged ingress Ethernet packets are allowed * access-dcb Only untagged ingress Ethernet packets are allowed. Egress packets will be priority tagged * dot1q-tunnel Both tagged and untagged ingress Ethernet packets are allowed. Egress packets are tagged with a second VLAN (802.1Q) header \n"; }