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

Paulo Ricardo Paz Vital pvital at linux.vnet.ibm.com
Wed Oct 1 14:15:52 UTC 2014


Howdy Brent,

there are some prints in the code that I supposed is not part of your
solution :-D

On Wed, 2014-10-01 at 08:39 -0500, bbaude at redhat.com wrote:
> From: Brent Baude <bbaude at redhat.com>
> 
> Add tests and regex to json per peer review.  Also changed the error type
> to InvalidParameter
> 
> ---
>  docs/API.md                         |  1 +
>  src/kimchi/API.json                 |  6 ++++++
>  src/kimchi/control/vms.py           |  2 +-
>  src/kimchi/exception.py             |  3 ++-
>  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, 78 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..e46aedc 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..ebe03ae 100644
> --- a/src/kimchi/exception.py
> +++ b/src/kimchi/exception.py
> @@ -46,7 +46,8 @@ class KimchiException(Exception):
>              msg = self._get_translation()
>          else:
>              msg = _messages.get(code, code)
> -
> +        print msg
> +        print args

Remove these prints ^

>          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..86f4e50 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": _("The custom MAC address is not properly formatted as 12 character hexadecimal value separated by colons(:)"),

Why not "Invalid MAC address format. Expected a 12 character hexadecimal
value separated by colons(:)" ?

> +    "KCHVMIF0010E": _("The custom MAC address is already in use on this guest"),
> +    "KCHVMIF0011E": _("Libvirt requires you use a unicast MAC address."),
> +
> 
>      "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..59c3e50 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)
> +            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)
> +            rollback.prependDefer(inst.vmiface_delete, 'kimchi-ifaces', mac)
> +
>      @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>




More information about the Kimchi-devel mailing list