Commit bb66f52e authored by Leigh Stoller's avatar Leigh Stoller

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()) {
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`;
chomp($ext_ctrlip);
if ($ext_ctrlip !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
......
......@@ -117,8 +117,10 @@ my $debug = 0;
my $JAILCTRLNET = "172.16.0.0";
my $JAILCTRLNETMASK = "255.240.0.0";
my $USE_NETEM = 0;
my $USE_NETEM = 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
......@@ -956,7 +958,8 @@ sub vz_vnodeCreate {
mysystem("mount /dev/openvz/$vnode_id /mnt/$vnode_id");
mysystem("mkdir -p /mnt/$vnode_id/root /mnt/$vnode_id/private");
TBDebugTimeStampWithDate("untaring to /mnt/$vnode_id");
if (0) {
if (! -e "/mnt/$image/private") {
# Backwards compat.
mysystem("tar -xzf $imagepath -C /mnt/$vnode_id/private");
}
else {
......@@ -1466,17 +1469,18 @@ sub vz_vnodePreConfigControlNetwork {
# add the control net iface
my $cnet_veth = "veth${vmid}.${CONTROL_IFNUM}";
my $cnet_mac = macAddSep($mac);
my $ext_vethmac = $cnet_mac;
if ($isroutable) {
my ($cnet_mac,$ext_vethmac) = build_fake_macs($mac);
if (!defined($cnet_mac)) {
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
# address. I do not know why it does this, but according
# to the xen equivalent code, this is what ya do.
$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
......@@ -1490,7 +1494,7 @@ sub vz_vnodePreConfigControlNetwork {
# When the ip is routable, we need to use a bridge. Must tell
# vznetinit script to do this differently.
#
if ($isroutable) {
if ($USE_CTRLBR || $isroutable) {
$lines{"ELABCTRLBR"} = '"vzbr0"';
}
editContainerConfigFile($vmid,\%lines);
......
......@@ -28,6 +28,7 @@ $JAILIPMASK = "@JAILIPMASK@";
use libdb;
use libtestbed;
use emutil;
use event;
use English;
use Socket;
......@@ -2347,7 +2348,7 @@ sub CreateVnodes($$$)
"port" => 1,
"iface" => "eth0",
"role" => TBDB_IFACEROLE_CONTROL(),
"MAC" => "000000000000",
"MAC" => GenFakeMac(),
"IP" => $jailip,
"mask" => $jailmask,
"type" => "generic",
......@@ -2460,6 +2461,7 @@ sub SetNodeHistory($$$$)
my $exptidx = $experiment->idx();
my $nodeid = $self->node_id();
my $cnet_ip;
my $cnet_mac;
my $phys_nodeid;
my $now = time();
my $uid;
......@@ -2530,11 +2532,14 @@ sub SetNodeHistory($$$$)
"*** SetNodeHistory: No control interface for $self\n";
}
else {
$cnet_ip = $interface->IP();
$cnet_ip = $interface->IP();
$cnet_mac = $interface->mac()
if (defined($interface->mac()));
if (!defined($cnet_ip) || $cnet_ip eq "") {
print STDERR
"*** SetNodeHistory: No control IP for $interface\n";
$cnet_ip = undef;
$cnet_ip = undef;
}
}
$phys_nodeid = $self->phys_nodeid();
......@@ -2551,7 +2556,8 @@ sub SetNodeHistory($$$$)
return DBQueryWarn("insert into node_history set ".
" history_id=0, node_id='$nodeid', op='$op', ".
" 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'," : "").
" stamp=$now, exptidx=$exptidx");
}
......
......@@ -15,7 +15,7 @@ use vars qw(@ISA @EXPORT);
@EXPORT = qw(TBDB_CHECKDBSLOT_NOFLAGS TBDB_CHECKDBSLOT_WARN
TBDB_CHECKDBSLOT_ERROR TBcheck_dbslot TBFieldErrorString
TBGetUniqueIndex ParRun VersionInfo UpdateVersionInfo
SpanningTree BackTraceOnWarning PassWordHash);
SpanningTree GenFakeMac BackTraceOnWarning PassWordHash);
use emdb;
use English;
......@@ -630,5 +630,30 @@ sub PassWordHash($)
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...
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 vlantag history table.
#
use strict;
use libdb;
......
......@@ -27,7 +27,7 @@ use Lan;
use OSinfo;
use Port;
use English;
use emutil qw(SpanningTree);
use emutil qw(SpanningTree GenFakeMac);
use Data::Dumper;
use Carp;
use POSIX;
......@@ -9042,29 +9042,4 @@ sub AddToSwitchPath($$)
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;
......@@ -52,7 +52,7 @@ sub usage {
exit(1);
}
my $optlist = "ARS:aln:rswvd:i:ct:x:";
my $optlist = "ARS:aln:rswvd:i:ct:x:m:";
my $warnme = 0;
my $verbose = 0;
my $showall = 0;
......@@ -66,6 +66,7 @@ my $datetime;
my $datelimit;
my $startrecord;
my $ip;
my $mac;
#
# Sort stuff. sortby value should correspond to record field format:
......@@ -160,13 +161,18 @@ if (defined($options{"i"})) {
$ip = $options{"i"};
$summary = 0;
}
if (defined($options{"m"})) {
$mac = $options{"m"};
$mac =~ s/://g;
$summary = 0;
}
if (defined($options{"w"})) {
$warnme = 1;
}
if (defined($options{"v"})) {
$verbose = 1;
}
if (!$showall && @ARGV == 0 && !defined($ip)) {
if (!$showall && @ARGV == 0 && !(defined($ip) || defined($mac))) {
usage();
}
my @nodes = @ARGV;
......@@ -201,6 +207,9 @@ if ($datetime) {
elsif (defined($ip)) {
$querymod = " AND h1.cnet_ip='$ip'";
}
elsif (defined($mac)) {
$querymod = " AND h1.cnet_mac='$mac'";
}
my $query_result =
DBQueryFatal("SELECT h1.node_id,h1.op,h1.stamp,h1.uid,h1.exptidx,".
......@@ -271,6 +280,9 @@ if (@nodes) {
elsif (defined($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?
# Note that the combo field order by is strictly to convince mysql
......
......@@ -65,7 +65,23 @@ class Node
$query_result =
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 . "'");
if (mysql_num_rows($query_result) == 0) {
......@@ -1349,7 +1365,8 @@ class Node
#
function ShowNodeHistory($node_id = null, $record = null,
$count = 200, $showall = 0,
$date = null, $IP = null, $node_opt = "") {
$date = null, $IP = null, $mac = null,
$node_opt = "") {
global $TBSUEXEC_PATH;
global $PROTOGENI;
$atime = 0;
......@@ -1369,11 +1386,15 @@ function ShowNodeHistory($node_id = null, $record = null,
elseif ($record) {
$opt .= " -x " . escapeshellarg($record);
}
if ($node_id || $IP) {
if ($node_id || $IP || $mac) {
if ($IP) {
$opt .= " -i " . escapeshellarg($IP);
$nodestr = "<th>Node</th>";
}
if ($mac) {
$opt .= " -m " . escapeshellarg($mac);
$nodestr = "<th>Node</th>";
}
else {
$arg = escapeshellarg($node_id);
}
......
......@@ -27,6 +27,7 @@ $optargs = OptionalPageArguments("showall", PAGEARG_BOOLEAN,
"count", PAGEARG_INTEGER,
"when", PAGEARG_STRING,
"IP", PAGEARG_STRING,
"mac", PAGEARG_STRING,
# To allow for pcvm search, since they are
# transient and will not map to a node.
"node_id", PAGEARG_STRING);
......@@ -96,21 +97,48 @@ if (isset($IP)) {
else {
$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)) {
$node_opt = "node_id=$node_id";
$form_opt = "<input type=hidden name=node_id value=$node_id>";
$IP = null;
$mac = null;
}
else if (isset($IP)) {
$node_opt = "IP=$IP";
$form_opt = "<input type=hidden name=IP value=$IP>";
$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 {
$node_opt = "";
$form_opt = "";
$IP = null;
$node_id = null;
$mac = null;
}
$opts="$node_opt$dateopt";
......@@ -189,9 +217,21 @@ echo "<tr><form action=shownodehistory.php3 method=get>
<td class=stealth>
<b><input type=submit name=search3 value=Search></b></td>\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";
ShowNodeHistory($node_id, $record, $count, $showall, $datetime, $IP, $node_opt);
ShowNodeHistory($node_id, $record, $count, $showall, $datetime,
$IP, $mac, $node_opt);
#
# 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