Commit 1862d460 authored by Alexander Schreiber's avatar Alexander Schreiber
Browse files

Allow instance MAC address to be set.

Allow the MAC address of an instance to be specified optionally during
instance creation and later to be changed via instance modify.

Reviewed-by: iustinp
parent bf6fe28b
......@@ -2837,7 +2837,7 @@ class LUCreateInstance(LogicalUnit):
_OP_REQP = ["instance_name", "mem_size", "disk_size", "pnode",
"disk_template", "swap_size", "mode", "start", "vcpus",
"wait_for_sync", "ip_check"]
"wait_for_sync", "ip_check", "mac"]
def BuildHooksEnv(self):
"""Build hooks env.
......@@ -3013,6 +3013,12 @@ class LUCreateInstance(LogicalUnit):
raise errors.OpPrereqError("IP %s of instance %s already in use" %
(hostname1.ip, instance_name))
# MAC address verification
if self.op.mac != "auto":
if not utils.IsValidMac(self.op.mac.lower()):
raise errors.OpPrereqError("invalid MAC address specified: %s" %
# bridge verification
bridge = getattr(self.op, "bridge", None)
if bridge is None:
......@@ -3037,7 +3043,12 @@ class LUCreateInstance(LogicalUnit):
instance = self.op.instance_name
pnode_name =
nic = objects.NIC(bridge=self.op.bridge, mac=self.cfg.GenerateMAC())
if self.op.mac == "auto":
nic = objects.NIC(bridge=self.op.bridge, mac=mac_address)
if self.inst_ip is not None:
nic.ip = self.inst_ip
......@@ -4073,8 +4084,9 @@ class LUSetInstanceParms(LogicalUnit):
self.mem = getattr(self.op, "mem", None)
self.vcpus = getattr(self.op, "vcpus", None)
self.ip = getattr(self.op, "ip", None)
self.mac = getattr(self.op, "mac", None)
self.bridge = getattr(self.op, "bridge", None)
if [self.mem, self.vcpus, self.ip, self.bridge].count(None) == 4:
if [self.mem, self.vcpus, self.ip, self.bridge, self.mac].count(None) == 5:
raise errors.OpPrereqError("No changes submitted")
if self.mem is not None:
......@@ -4096,6 +4108,12 @@ class LUSetInstanceParms(LogicalUnit):
self.do_ip = False
self.do_bridge = (self.bridge is not None)
if self.mac is not None:
if self.cfg.IsMacInUse(self.mac):
raise errors.OpPrereqError('MAC address %s already in use in cluster' %
if not utils.IsValidMac(self.mac):
raise errors.OpPrereqError('Invalid MAC address %s' % self.mac)
instance = self.cfg.GetInstanceInfo(
......@@ -4125,6 +4143,9 @@ class LUSetInstanceParms(LogicalUnit):
if self.bridge:
instance.nics[0].bridge = self.bridge
result.append(("bridge", self.bridge))
if self.mac:
instance.nics[0].mac = self.mac
result.append(("mac", self.mac))
......@@ -96,6 +96,18 @@ class ConfigWriter:
raise errors.ConfigurationError("Can't generate unique MAC")
return mac
def IsMacInUse(self, mac):
"""Predicate: check if the specified MAC is in use in the Ganeti cluster.
This only checks instances managed by this cluster, it does not
check for potential collisions elsewhere.
all_macs = self._AllMACs()
return mac in all_macs
def _ComputeAllLVs(self):
"""Compute the list of all LVs.
......@@ -160,7 +160,7 @@ class OpCreateInstance(OpCode):
__slots__ = ["instance_name", "mem_size", "disk_size", "os_type", "pnode",
"disk_template", "snode", "swap_size", "mode",
"vcpus", "ip", "bridge", "src_node", "src_path", "start",
"wait_for_sync", "ip_check"]
"wait_for_sync", "ip_check", "mac"]
class OpReinstallInstance(OpCode):
......@@ -257,7 +257,7 @@ class OpQueryInstanceData(OpCode):
class OpSetInstanceParms(OpCode):
"""Change the parameters of an instance."""
__slots__ = ["instance_name", "mem", "vcpus", "ip", "bridge"]
__slots__ = ["instance_name", "mem", "vcpus", "ip", "bridge", "mac"]
# OS opcodes
......@@ -1055,3 +1055,13 @@ def UniqueSequence(seq):
seen = set()
return [i for i in seq if i not in seen and not seen.add(i)]
def IsValidMac(mac):
"""Predicate to check if a MAC address is valid.
Checks wether the supplied MAC address is formally correct, only
accepts colon separated format.
mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
return mac_check.match(mac) is not None
......@@ -226,7 +226,7 @@ def AddInstance(opts, args):
snode=snode, vcpus=opts.vcpus,
ip=opts.ip, bridge=opts.bridge,
start=opts.start, ip_check=opts.ip_check,
wait_for_sync=opts.wait_for_sync, mac=opts.mac)
return 0
......@@ -624,15 +624,16 @@ def SetInstanceParms(opts, args):
Opts used:
memory - the new memory size
vcpus - the new number of cpus
mac - the new MAC address of the instance
if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac):
logger.ToStdout("Please give at least one of the parameters.")
return 1
op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
vcpus=opts.vcpus, ip=opts.ip,
bridge=opts.bridge, mac=opts.mac)
result = SubmitOpCode(op)
if result:
......@@ -701,6 +702,9 @@ add_opts = [
make_option("-i", "--ip", dest="ip",
help="IP address ('none' [default], 'auto', or specify address)",
default='none', type="string", metavar="<ADDRESS>"),
make_option("--mac", dest="mac",
help="MAC address ('auto' [default], or specify address)",
default='auto', type="string", metavar="<MACADDRESS>"),
make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
action="store_false", help="Don't wait for sync (DANGEROUS!)"),
make_option("-b", "--bridge", dest="bridge",
......@@ -808,6 +812,9 @@ commands = {
make_option("-b", "--bridge", dest="bridge",
help="Bridge to connect this instance to",
default=None, type="string", metavar="<bridge>"),
make_option("--mac", dest="mac",
help="MAC address", default=None,
type="string", metavar="<MACADDRESS>"),
"<instance>", "Alters the parameters of an instance"),
'shutdown': (ShutdownInstance, ARGS_ANY,
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