Commit bb66f52e authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Add tracking of control net mac addresses in the node_history.

For InstaGeni, need to record and be able to search for history by
control net mac address. We now record this in the node_history table,
with corresponding change to the ShowNodeHistory web page.

The backend changes required are that we 1) actually generate a mac
address for VMs and stick it into the interfaces record, 2) return
that mac from tmcd in the jailconfig, and 3) have the openvz library
create the control net interface using that mac.

On the openvz image, needed to switch to using a control network
bridge for all interfaces (not just routable ones) so that traffic
leaves the node with the correct mac.
parent b787ecab
...@@ -541,7 +541,8 @@ if (StoreState()) { ...@@ -541,7 +541,8 @@ if (StoreState()) {
MyFatal("Could not store container state to disk"); MyFatal("Could not store container state to disk");
} }
my $cnet_mac = ipToMac(VNCONFIG('CTRLIP')); my $cnet_mac = (defined(VNCONFIG('CTRLMAC')) ?
VNCONFIG('CTRLMAC') : ipToMac(VNCONFIG('CTRLIP')));
my $ext_ctrlip = `cat $CTRLIPFILE`; my $ext_ctrlip = `cat $CTRLIPFILE`;
chomp($ext_ctrlip); chomp($ext_ctrlip);
if ($ext_ctrlip !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { if ($ext_ctrlip !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
......
...@@ -117,8 +117,10 @@ my $debug = 0; ...@@ -117,8 +117,10 @@ my $debug = 0;
my $JAILCTRLNET = "172.16.0.0"; my $JAILCTRLNET = "172.16.0.0";
my $JAILCTRLNETMASK = "255.240.0.0"; my $JAILCTRLNETMASK = "255.240.0.0";
my $USE_NETEM = 0; my $USE_NETEM = 0;
my $USE_MACVLAN = 0; my $USE_MACVLAN = 0;
# Use control network bridging for all containers.
my $USE_CTRLBR = 1;
# #
# If we are using a modern kernel, use netem instead of our own plr/delay # If we are using a modern kernel, use netem instead of our own plr/delay
...@@ -956,7 +958,8 @@ sub vz_vnodeCreate { ...@@ -956,7 +958,8 @@ sub vz_vnodeCreate {
mysystem("mount /dev/openvz/$vnode_id /mnt/$vnode_id"); mysystem("mount /dev/openvz/$vnode_id /mnt/$vnode_id");
mysystem("mkdir -p /mnt/$vnode_id/root /mnt/$vnode_id/private"); mysystem("mkdir -p /mnt/$vnode_id/root /mnt/$vnode_id/private");
TBDebugTimeStampWithDate("untaring to /mnt/$vnode_id"); TBDebugTimeStampWithDate("untaring to /mnt/$vnode_id");
if (0) { if (! -e "/mnt/$image/private") {
# Backwards compat.
mysystem("tar -xzf $imagepath -C /mnt/$vnode_id/private"); mysystem("tar -xzf $imagepath -C /mnt/$vnode_id/private");
} }
else { else {
...@@ -1466,17 +1469,18 @@ sub vz_vnodePreConfigControlNetwork { ...@@ -1466,17 +1469,18 @@ sub vz_vnodePreConfigControlNetwork {
# add the control net iface # add the control net iface
my $cnet_veth = "veth${vmid}.${CONTROL_IFNUM}"; my $cnet_veth = "veth${vmid}.${CONTROL_IFNUM}";
my $cnet_mac = macAddSep($mac); my ($cnet_mac,$ext_vethmac) = build_fake_macs($mac);
my $ext_vethmac = $cnet_mac; if (!defined($cnet_mac)) {
if ($isroutable) { print STDERR "Could not construct veth/eth macs\n";
return -1;
}
($cnet_mac,$ext_vethmac) = (macAddSep($cnet_mac),macAddSep($ext_vethmac));
if ($USE_CTRLBR || $isroutable) {
# Must do this so that the bridge does not take on the # Must do this so that the bridge does not take on the
# address. I do not know why it does this, but according # address. I do not know why it does this, but according
# to the xen equivalent code, this is what ya do. # to the xen equivalent code, this is what ya do.
$ext_vethmac = "fe:ff:ff:ff:ff:ff"; $ext_vethmac = "fe:ff:ff:ff:ff:ff";
} }
elsif ($ext_vethmac =~ /^(00:00)(.*)$/) {
$ext_vethmac = "00:01$2";
}
# #
# we have to hack the VEID.conf file BEFORE calling --netif_add ... --save # we have to hack the VEID.conf file BEFORE calling --netif_add ... --save
...@@ -1490,7 +1494,7 @@ sub vz_vnodePreConfigControlNetwork { ...@@ -1490,7 +1494,7 @@ sub vz_vnodePreConfigControlNetwork {
# When the ip is routable, we need to use a bridge. Must tell # When the ip is routable, we need to use a bridge. Must tell
# vznetinit script to do this differently. # vznetinit script to do this differently.
# #
if ($isroutable) { if ($USE_CTRLBR || $isroutable) {
$lines{"ELABCTRLBR"} = '"vzbr0"'; $lines{"ELABCTRLBR"} = '"vzbr0"';
} }
editContainerConfigFile($vmid,\%lines); editContainerConfigFile($vmid,\%lines);
......
...@@ -28,6 +28,7 @@ $JAILIPMASK = "@JAILIPMASK@"; ...@@ -28,6 +28,7 @@ $JAILIPMASK = "@JAILIPMASK@";
use libdb; use libdb;
use libtestbed; use libtestbed;
use emutil;
use event; use event;
use English; use English;
use Socket; use Socket;
...@@ -2347,7 +2348,7 @@ sub CreateVnodes($$$) ...@@ -2347,7 +2348,7 @@ sub CreateVnodes($$$)
"port" => 1, "port" => 1,
"iface" => "eth0", "iface" => "eth0",
"role" => TBDB_IFACEROLE_CONTROL(), "role" => TBDB_IFACEROLE_CONTROL(),
"MAC" => "000000000000", "MAC" => GenFakeMac(),
"IP" => $jailip, "IP" => $jailip,
"mask" => $jailmask, "mask" => $jailmask,
"type" => "generic", "type" => "generic",
...@@ -2460,6 +2461,7 @@ sub SetNodeHistory($$$$) ...@@ -2460,6 +2461,7 @@ sub SetNodeHistory($$$$)
my $exptidx = $experiment->idx(); my $exptidx = $experiment->idx();
my $nodeid = $self->node_id(); my $nodeid = $self->node_id();
my $cnet_ip; my $cnet_ip;
my $cnet_mac;
my $phys_nodeid; my $phys_nodeid;
my $now = time(); my $now = time();
my $uid; my $uid;
...@@ -2530,11 +2532,14 @@ sub SetNodeHistory($$$$) ...@@ -2530,11 +2532,14 @@ sub SetNodeHistory($$$$)
"*** SetNodeHistory: No control interface for $self\n"; "*** SetNodeHistory: No control interface for $self\n";
} }
else { else {
$cnet_ip = $interface->IP(); $cnet_ip = $interface->IP();
$cnet_mac = $interface->mac()
if (defined($interface->mac()));
if (!defined($cnet_ip) || $cnet_ip eq "") { if (!defined($cnet_ip) || $cnet_ip eq "") {
print STDERR print STDERR
"*** SetNodeHistory: No control IP for $interface\n"; "*** SetNodeHistory: No control IP for $interface\n";
$cnet_ip = undef; $cnet_ip = undef;
} }
} }
$phys_nodeid = $self->phys_nodeid(); $phys_nodeid = $self->phys_nodeid();
...@@ -2551,7 +2556,8 @@ sub SetNodeHistory($$$$) ...@@ -2551,7 +2556,8 @@ sub SetNodeHistory($$$$)
return DBQueryWarn("insert into node_history set ". return DBQueryWarn("insert into node_history set ".
" history_id=0, node_id='$nodeid', op='$op', ". " history_id=0, node_id='$nodeid', op='$op', ".
" uid='$uid', uid_idx='$uid_idx', ". " uid='$uid', uid_idx='$uid_idx', ".
(defined($cnet_ip) ? "cnet_IP='$cnet_ip'," : ""). (defined($cnet_ip) ? "cnet_IP='$cnet_ip'," : "").
(defined($cnet_mac) ? "cnet_mac='$cnet_mac'," : "").
(defined($phys_nodeid) ? "phys_nodeid='$phys_nodeid'," : ""). (defined($phys_nodeid) ? "phys_nodeid='$phys_nodeid'," : "").
" stamp=$now, exptidx=$exptidx"); " stamp=$now, exptidx=$exptidx");
} }
......
...@@ -15,7 +15,7 @@ use vars qw(@ISA @EXPORT); ...@@ -15,7 +15,7 @@ use vars qw(@ISA @EXPORT);
@EXPORT = qw(TBDB_CHECKDBSLOT_NOFLAGS TBDB_CHECKDBSLOT_WARN @EXPORT = qw(TBDB_CHECKDBSLOT_NOFLAGS TBDB_CHECKDBSLOT_WARN
TBDB_CHECKDBSLOT_ERROR TBcheck_dbslot TBFieldErrorString TBDB_CHECKDBSLOT_ERROR TBcheck_dbslot TBFieldErrorString
TBGetUniqueIndex ParRun VersionInfo UpdateVersionInfo TBGetUniqueIndex ParRun VersionInfo UpdateVersionInfo
SpanningTree BackTraceOnWarning PassWordHash); SpanningTree GenFakeMac BackTraceOnWarning PassWordHash);
use emdb; use emdb;
use English; use English;
...@@ -630,5 +630,30 @@ sub PassWordHash($) ...@@ -630,5 +630,30 @@ sub PassWordHash($)
return $passhash; return $passhash;
} }
#
# Generate a hopefully unique mac address that is suitable for use
# on a shared node where uniqueness matters.
#
sub GenFakeMac()
{
my $mac;
#
# Random number for lower 4 octets.
#
my $ran=`/bin/dd if=/dev/urandom count=32 bs=1 2>/dev/null | /sbin/md5`;
return undef
if ($?);
if ($ran =~ /^\w\w\w(\w\w\w\w\w\w\w\w\w\w)/) {
$mac = $1;
}
#
# Set the "locally administered" bit, good practice.
#
return "02" . $mac;
}
# _Always_ make sure that this 1 is at the end of the file... # _Always_ make sure that this 1 is at the end of the file...
1; 1;
#
# Convert to randomly generated mac addresses for container control interface.
#
use strict;
use libinstall;
use installvars;
use emdb;
use emutil;
sub InstallUpdate($$)
{
my ($version, $phase) = @_;
if ($phase eq "pre") {
my $query_result =
DBQueryFatal("select i.node_id,iface,mac from interfaces as i ".
"left join nodes as n on n.node_id=i.node_id ".
"left join node_types as t on t.type=n.type ".
"where t.isvirtnode=1 and i.role='ctrl'");
while (my ($node_id,$iface,$mac) = $query_result->fetchrow_array()) {
next
if ($mac ne "000000000000");
my $newmac = GenFakeMac();
DBQueryFatal("update interfaces set mac='$newmac' ".
"where node_id='$node_id' and iface='$iface'");
}
}
return 0;
}
1;
# Local Variables:
# mode:perl
# End:
# #
# Add more slots to node_history to allow recording dynamic virtual nodes. # Add more slots to node_history to allow recording dynamic virtual nodes.
# Add vlantag history table.
# #
use strict; use strict;
use libdb; use libdb;
......
...@@ -27,7 +27,7 @@ use Lan; ...@@ -27,7 +27,7 @@ use Lan;
use OSinfo; use OSinfo;
use Port; use Port;
use English; use English;
use emutil qw(SpanningTree); use emutil qw(SpanningTree GenFakeMac);
use Data::Dumper; use Data::Dumper;
use Carp; use Carp;
use POSIX; use POSIX;
...@@ -9042,29 +9042,4 @@ sub AddToSwitchPath($$) ...@@ -9042,29 +9042,4 @@ sub AddToSwitchPath($$)
return join(" ", @cur); return join(" ", @cur);
} }
#
# Generate a hopefully unique mac address that is suitable for use
# on a shared node where uniqueness matters.
#
sub GenFakeMac()
{
my $mac;
#
# Random number for lower 4 octets.
#
my $ran=`/bin/dd if=/dev/urandom count=32 bs=1 2>/dev/null | /sbin/md5`;
return undef
if ($?);
if ($ran =~ /^\w\w\w(\w\w\w\w\w\w\w\w)/) {
$mac = $1;
}
#
# Set the "locally administered" bit, good practice.
#
return ($PGENIRACK ? "0200" : "0000") . $mac;
}
1; 1;
...@@ -52,7 +52,7 @@ sub usage { ...@@ -52,7 +52,7 @@ sub usage {
exit(1); exit(1);
} }
my $optlist = "ARS:aln:rswvd:i:ct:x:"; my $optlist = "ARS:aln:rswvd:i:ct:x:m:";
my $warnme = 0; my $warnme = 0;
my $verbose = 0; my $verbose = 0;
my $showall = 0; my $showall = 0;
...@@ -66,6 +66,7 @@ my $datetime; ...@@ -66,6 +66,7 @@ my $datetime;
my $datelimit; my $datelimit;
my $startrecord; my $startrecord;
my $ip; my $ip;
my $mac;
# #
# Sort stuff. sortby value should correspond to record field format: # Sort stuff. sortby value should correspond to record field format:
...@@ -160,13 +161,18 @@ if (defined($options{"i"})) { ...@@ -160,13 +161,18 @@ if (defined($options{"i"})) {
$ip = $options{"i"}; $ip = $options{"i"};
$summary = 0; $summary = 0;
} }
if (defined($options{"m"})) {
$mac = $options{"m"};
$mac =~ s/://g;
$summary = 0;
}
if (defined($options{"w"})) { if (defined($options{"w"})) {
$warnme = 1; $warnme = 1;
} }
if (defined($options{"v"})) { if (defined($options{"v"})) {
$verbose = 1; $verbose = 1;
} }
if (!$showall && @ARGV == 0 && !defined($ip)) { if (!$showall && @ARGV == 0 && !(defined($ip) || defined($mac))) {
usage(); usage();
} }
my @nodes = @ARGV; my @nodes = @ARGV;
...@@ -201,6 +207,9 @@ if ($datetime) { ...@@ -201,6 +207,9 @@ if ($datetime) {
elsif (defined($ip)) { elsif (defined($ip)) {
$querymod = " AND h1.cnet_ip='$ip'"; $querymod = " AND h1.cnet_ip='$ip'";
} }
elsif (defined($mac)) {
$querymod = " AND h1.cnet_mac='$mac'";
}
my $query_result = my $query_result =
DBQueryFatal("SELECT h1.node_id,h1.op,h1.stamp,h1.uid,h1.exptidx,". DBQueryFatal("SELECT h1.node_id,h1.op,h1.stamp,h1.uid,h1.exptidx,".
...@@ -271,6 +280,9 @@ if (@nodes) { ...@@ -271,6 +280,9 @@ if (@nodes) {
elsif (defined($ip)) { elsif (defined($ip)) {
$querymod .= " AND cnet_ip='$ip'"; $querymod .= " AND cnet_ip='$ip'";
} }
elsif (defined($mac)) {
$querymod .= " AND cnet_mac='$mac'";
}
# XXX maybe it would be better to sort them here in perl, outside the DB? # XXX maybe it would be better to sort them here in perl, outside the DB?
# Note that the combo field order by is strictly to convince mysql # Note that the combo field order by is strictly to convince mysql
......
...@@ -65,7 +65,23 @@ class Node ...@@ -65,7 +65,23 @@ class Node
$query_result = $query_result =
DBQueryFatal("select i.node_id from interfaces as i ". DBQueryFatal("select i.node_id from interfaces as i ".
"where i.IP='$ip' and ". "where i.IP='$safe_ip' and ".
" i.role='" . TBDB_IFACEROLE_CONTROL . "'");
if (mysql_num_rows($query_result) == 0) {
return null;
}
$row = mysql_fetch_array($query_result);
return Node::Lookup($row["node_id"]);
}
# Lookup by Mac
function LookupByMac($mac) {
$safe_mac = addslashes($mac);
$query_result =
DBQueryFatal("select i.node_id from interfaces as i ".
"where i.mac='$safe_mac' and ".
" i.role='" . TBDB_IFACEROLE_CONTROL . "'"); " i.role='" . TBDB_IFACEROLE_CONTROL . "'");
if (mysql_num_rows($query_result) == 0) { if (mysql_num_rows($query_result) == 0) {
...@@ -1349,7 +1365,8 @@ class Node ...@@ -1349,7 +1365,8 @@ class Node
# #
function ShowNodeHistory($node_id = null, $record = null, function ShowNodeHistory($node_id = null, $record = null,
$count = 200, $showall = 0, $count = 200, $showall = 0,
$date = null, $IP = null, $node_opt = "") { $date = null, $IP = null, $mac = null,
$node_opt = "") {
global $TBSUEXEC_PATH; global $TBSUEXEC_PATH;
global $PROTOGENI; global $PROTOGENI;
$atime = 0; $atime = 0;
...@@ -1369,11 +1386,15 @@ function ShowNodeHistory($node_id = null, $record = null, ...@@ -1369,11 +1386,15 @@ function ShowNodeHistory($node_id = null, $record = null,
elseif ($record) { elseif ($record) {
$opt .= " -x " . escapeshellarg($record); $opt .= " -x " . escapeshellarg($record);
} }
if ($node_id || $IP) { if ($node_id || $IP || $mac) {
if ($IP) { if ($IP) {
$opt .= " -i " . escapeshellarg($IP); $opt .= " -i " . escapeshellarg($IP);
$nodestr = "<th>Node</th>"; $nodestr = "<th>Node</th>";
} }
if ($mac) {
$opt .= " -m " . escapeshellarg($mac);
$nodestr = "<th>Node</th>";
}
else { else {
$arg = escapeshellarg($node_id); $arg = escapeshellarg($node_id);
} }
......
...@@ -27,6 +27,7 @@ $optargs = OptionalPageArguments("showall", PAGEARG_BOOLEAN, ...@@ -27,6 +27,7 @@ $optargs = OptionalPageArguments("showall", PAGEARG_BOOLEAN,
"count", PAGEARG_INTEGER, "count", PAGEARG_INTEGER,
"when", PAGEARG_STRING, "when", PAGEARG_STRING,
"IP", PAGEARG_STRING, "IP", PAGEARG_STRING,
"mac", PAGEARG_STRING,
# To allow for pcvm search, since they are # To allow for pcvm search, since they are
# transient and will not map to a node. # transient and will not map to a node.
"node_id", PAGEARG_STRING); "node_id", PAGEARG_STRING);
...@@ -96,21 +97,48 @@ if (isset($IP)) { ...@@ -96,21 +97,48 @@ if (isset($IP)) {
else { else {
$IP = null; $IP = null;
} }
if (isset($mac)) {
if (! preg_match('/^[\w\:]+$/', $mac)) {
USERERROR("Does not look like a valid mac address.", 1);
}
$node = Node::LookupByMac($mac);
#
# Switch to a node_id if its a physical node. Otherwise,
# continue with the mac.
#
if ($node && !$node->IsVirtNode()) {
$node_id = $node->node_id();
$mac = null;
}
}
else {
$mac = null;
}
if (isset($node_id)) { if (isset($node_id)) {
$node_opt = "node_id=$node_id"; $node_opt = "node_id=$node_id";
$form_opt = "<input type=hidden name=node_id value=$node_id>"; $form_opt = "<input type=hidden name=node_id value=$node_id>";
$IP = null; $IP = null;
$mac = null;
} }
else if (isset($IP)) { else if (isset($IP)) {
$node_opt = "IP=$IP"; $node_opt = "IP=$IP";
$form_opt = "<input type=hidden name=IP value=$IP>"; $form_opt = "<input type=hidden name=IP value=$IP>";
$node_id = null; $node_id = null;
$mac = null;
}
else if (isset($mac)) {
$node_opt = "mac=$mac";
$form_opt = "<input type=hidden name=mac value=$mac>";
$node_id = null;
$IP = null;
} }
else { else {
$node_opt = ""; $node_opt = "";
$form_opt = ""; $form_opt = "";
$IP = null; $IP = null;
$node_id = null; $node_id = null;
$mac = null;
} }
$opts="$node_opt$dateopt"; $opts="$node_opt$dateopt";
...@@ -189,9 +217,21 @@ echo "<tr><form action=shownodehistory.php3 method=get> ...@@ -189,9 +217,21 @@ echo "<tr><form action=shownodehistory.php3 method=get>
<td class=stealth> <td class=stealth>
<b><input type=submit name=search3 value=Search></b></td>\n"; <b><input type=submit name=search3 value=Search></b></td>\n";
echo "</form></tr>\n"; echo "</form></tr>\n";
echo "<tr><form action=shownodehistory.php3 method=get>
<td class=stealth><b>Search for mac:</b>
<input type=text style=\"float:right\"
name=mac
size=12
value=\"$mac\"></td>
<input type=hidden name=showall value=$showall>
<input type=hidden name=when value=$when>
<td class=stealth>
<b><input type=submit name=search3 value=Search></b></td>\n";
echo "</form></tr>\n";
echo "</table><br>\n"; echo "</table><br>\n";
ShowNodeHistory($node_id, $record, $count, $showall, $datetime, $IP, $node_opt); ShowNodeHistory($node_id, $record, $count, $showall, $datetime,
$IP, $mac, $node_opt);
# #
# Standard Testbed Footer # Standard Testbed Footer
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment