[Kimchi-devel] [PATCH] Addition of custom mac address field for interfaces v4

bbaude at redhat.com bbaude at redhat.com
Thu Oct 2 19:47:35 UTC 2014


From: Brent Baude <bbaude at redhat.com>

Included review feedback
---
 docs/API.md                         |  1 +
 src/kimchi/API.json                 |  6 ++++++
 src/kimchi/control/vms.py           |  2 +-
 src/kimchi/exception.py             |  1 -
 src/kimchi/i18n.py                  |  4 ++++
 src/kimchi/model/vmifaces.py        | 38 +++++++++++++++++++++++++++++--------
 tests/test_model.py                 | 12 ++++++++++++
 ui/css/theme-default/guest-edit.css |  4 ++--
 ui/js/src/kimchi.guest_edit_main.js | 18 ++++++++++++------
 ui/pages/guest-edit.html.tmpl       | 11 ++++++++---
 10 files changed, 76 insertions(+), 21 deletions(-)

diff --git a/docs/API.md b/docs/API.md
index b679ce7..4f92eaa 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -221,6 +221,7 @@ Represents all network interfaces attached to a Virtual Machine.
               interface type is network.
     * type: The type of VM network interface that libvirt supports.
             Now kimchi just supports 'network' type.
+    * custommac: A custom MAC address for the interface
 
 ### Sub-Resource: Virtual Machine Network Interface
 
diff --git a/src/kimchi/API.json b/src/kimchi/API.json
index b8604d2..6cea55d 100644
--- a/src/kimchi/API.json
+++ b/src/kimchi/API.json
@@ -334,6 +334,12 @@
                     "type": "string",
                     "pattern": "^ne2k_pci|i82551|i82557b|i82559er|rtl8139|e1000|pcnet|virtio$",
                     "error": "KCHVMIF0006E"
+                },
+                "custommac": {
+                    "description": "optional custom mac address for the network interface",
+                    "type": "string",
+                    "pattern": "(^(?i)([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$)?",
+                    "error": "KCHVMIF0009E"
                 }
             }
         },
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index 88d8a81..627f1cc 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -36,7 +36,7 @@ class VM(Resource):
         super(VM, self).__init__(model, ident)
         self.role_key = 'guests'
         self.update_params = ["name", "users", "groups", "cpus", "memory",
-                              "graphics"]
+                              "graphics", "custommac"]
         self.screenshot = VMScreenShot(model, ident)
         self.uri_fmt = '/vms/%s'
         for ident, node in sub_nodes.items():
diff --git a/src/kimchi/exception.py b/src/kimchi/exception.py
index 039152a..7ab1878 100644
--- a/src/kimchi/exception.py
+++ b/src/kimchi/exception.py
@@ -46,7 +46,6 @@ class KimchiException(Exception):
             msg = self._get_translation()
         else:
             msg = _messages.get(code, code)
-
         msg = unicode(msg, 'utf-8') % args
         pattern = "%s: %s" % (code, msg)
         Exception.__init__(self, pattern)
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 9e66c68..4473a99 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -105,6 +105,10 @@ messages = {
     "KCHVMIF0006E": _("Invalid network model card specified for virtual machine interface"),
     "KCHVMIF0007E": _("Specify type and network to add a new virtual machine interface"),
     "KCHVMIF0008E": _("Specify type and network to update a virtual machine interface"),
+    "KCHVMIF0009E": _("Invalid MAC address format. Expected a 12 character hexadecimal value separated by colons(:)"),
+    "KCHVMIF0010E": _("The custom MAC address %(custommac)s is already in use on this guest"),
+    "KCHVMIF0011E": _("Libvirt requires a unicast MAC address which %(value)s is not."),
+
 
     "KCHTMPL0001E": _("Template %(name)s already exists"),
     "KCHTMPL0002E": _("Template %(name)s does not exist"),
diff --git a/src/kimchi/model/vmifaces.py b/src/kimchi/model/vmifaces.py
index 66b3827..ef67d34 100644
--- a/src/kimchi/model/vmifaces.py
+++ b/src/kimchi/model/vmifaces.py
@@ -18,6 +18,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
 import random
+import re
 
 import libvirt
 from lxml import etree, objectify
@@ -45,6 +46,20 @@ class VMIfacesModel(object):
                    random.randint(0x00, 0xff)]
             return ':'.join(map(lambda x: "%02x" % x, mac))
 
+        def checkduplicatemacs(vm, custommac):
+            macs = self.get_list(vm)
+            if custommac in macs:
+                raise InvalidParameter("KCHVMIF0010E", {'custommac': mac})
+
+        def validMac(mymac):
+            pattern = r'^(?i)([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$'
+            if bool(re.match(pattern, mac)) is not True:
+                raise InvalidParameter("KCHVMIF0009E", {'custommac': mac})
+            else:
+                # Checking for multicast
+                if (int(mymac.split(":")[0], 16) & 0x1) == 1:
+                    raise InvalidParameter("KCHVMIF0011E", {'custommac': mac})
+
         conn = self.conn.get()
         networks = conn.listNetworks() + conn.listDefinedNetworks()
 
@@ -56,14 +71,22 @@ class VMIfacesModel(object):
         if DOM_STATE_MAP[dom.info()[0]] != "shutoff":
             raise InvalidOperation("KCHVMIF0003E")
 
-        macs = (iface.mac.get('address')
-                for iface in self.get_vmifaces(vm, self.conn))
-
-        mac = randomMAC()
-        while True:
-            if mac not in macs:
-                break
+        if 'custommac' not in params:
+            macs = (iface.mac.get('address')
+                    for iface in self.get_vmifaces(vm, self.conn))
             mac = randomMAC()
+            while True:
+                if mac not in macs:
+                    break
+                mac = randomMAC()
+
+        else:
+            if len(params['custommac']) == 0:
+                mac = randomMAC()
+            else:
+                mac = params["custommac"]
+                validMac(mac)
+                checkduplicatemacs(vm, mac)
 
         children = [E.mac(address=mac)]
         ("network" in params.keys() and
@@ -75,7 +98,6 @@ class VMIfacesModel(object):
         xml = etree.tostring(E.interface(*children, **attrib))
 
         dom.attachDeviceFlags(xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT)
-
         return mac
 
     @staticmethod
diff --git a/tests/test_model.py b/tests/test_model.py
index ceedc6f..6d67398 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -219,6 +219,18 @@ class ModelTests(unittest.TestCase):
             self.assertEquals("default", iface['network'])
             self.assertEquals("e1000", iface["model"])
 
+            # custom MAC address test
+            iface_args = {"type": "network",
+                          "network": "test-network",
+                          "model": "virtio",
+                          "custommac": "52:54:00:17:cd:e7"}
+            mac = inst.vmifaces_create('kimchi-ifaces', iface_args)
+            rollback.prependDefer(inst.vmiface_delete, 'kimchi-ifaces', mac)
+            iface = inst.vmiface_lookup('kimchi-ifaces', mac)
+            self.assertEquals("52:54:00:17:cd:e7", iface['mac'])
+            self.assertRaises(InvalidParameter, inst.vmifaces_create,
+                              'kimchi-ifaces', iface_args)
+
     @unittest.skipUnless(utils.running_as_root(), 'Must be run as root')
     def test_vm_disk(self):
         disk_path = '/tmp/existent2.iso'
diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css
index 74c2237..f0e1bb0 100644
--- a/ui/css/theme-default/guest-edit.css
+++ b/ui/css/theme-default/guest-edit.css
@@ -112,7 +112,7 @@
 #form-guest-edit-storage .cell,
 .guest-edit-interface .cell {
     display: inline-block;
-    width: 250px;
+    width: 150px;
 }
 
 #form-guest-edit-storage .cell.dev {
@@ -130,7 +130,7 @@
 }
 
 .guest-edit-interface .body select {
-    width: 180px;
+    width: 150px;
     padding: 0px;
 }
 
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index c281289..7baf296 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -176,7 +176,10 @@ kimchi.guest_edit_main = function() {
         var toggleEdit = function(item, on){
             $("label", item).toggleClass("hide", on);
             $("select", item).toggleClass("hide", !on);
-            $(".action-area", item).toggleClass("hide");
+            $("input", item).toggleClass("hide", !on);
+            $(".action-area#editmode", item).toggleClass("hide", !on);
+            $(".action-area#viewmode", item).toggleClass("hide", on);
+
         };
         var addItem = function(data) {
             var itemNode = $.parseHTML(kimchi.substitute($('#interface-tmpl').html(),data));
@@ -215,20 +218,23 @@ kimchi.guest_edit_main = function() {
                 var item = $(this).parent().parent();
                 var interface = {
                     network: $("select", item).val(),
-                    type: "network"
+                    type: "network",
+                    custommac: $("input",item).val().toLowerCase()
                 };
-                var postUpdate = function(){
+                var postUpdate = function(mymac){
                     $("label", item).text(interface.network);
-                    toggleEdit(item, false);
+                    $("label#custommac", item).text(mymac);
+                    toggleEdit(item.prop, false);
                 };
                 if(item.prop("id")==""){
                     kimchi.createGuestInterface(kimchi.selectedGuest, interface, function(data){
                         item.prop("id", data.mac);
-                        postUpdate();
+                        $("label#custommac", item).text(data.mac)
+                        postUpdate(data.mac);
                     });
                 }else{
                     kimchi.updateGuestInterface(kimchi.selectedGuest, item.prop("id"), interface, function(){
-                        postUpdate();
+                        postUpdate(data.mac);
                     });
                 }
             });
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index 0b7dad3..0aa9f3b 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -108,7 +108,8 @@
                 <div class="header">
                     <span class="cell">$_("Network")</span>
                     <span class="cell">$_("Type")</span>
-                    <button class="add action-area"></button>
+                    <span class="cell">$_("MAC")</span>
+                    <button id="interfaceadd" class="add action-area"></button>
                 </div>
                 <div class="body"></div>
             </form>
@@ -192,10 +193,14 @@
         <span class="cell">
             <span>{type}</span>
         </span>
-        <span class="action-area {editMode}">
+        <span class="cell">
+            <label class="{viewMode}" id="custommac">{mac}</label>
+            <input type="text" id="insertmac" class="{editMode}"/>
+        </span>
+        <span class="action-area {editMode}" id="editmode">
             <button class="save"></button><button class="cancel"></button>
         </span>
-        <span class="action-area {viewMode}">
+        <span class="action-area {viewMode}" id="viewmode">
             <button class="edit"></button><button class="delete"></button>
         </span>
     <div>
-- 
1.9.3




More information about the Kimchi-devel mailing list