[libvirt] [PATCH go-xml] Add support for Node Device with basic testing.

Vladik Romanovsky vromanso at redhat.com
Tue May 30 14:36:22 UTC 2017


Thanks for the review.

On Tue, May 30, 2017 at 8:46 AM, Daniel P. Berrange <berrange at redhat.com> wrote:
> On Mon, May 29, 2017 at 03:58:52AM -0400, Vladik Romanovsky wrote:
>> ---
>>  node_device.go      | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  node_device_test.go | 111 +++++++++++++++++++++
>>  2 files changed, 388 insertions(+)
>>  create mode 100644 node_device.go
>>  create mode 100644 node_device_test.go
>>
>> diff --git a/node_device.go b/node_device.go
>> new file mode 100644
>> index 0000000..5375d32
>> --- /dev/null
>> +++ b/node_device.go
>> @@ -0,0 +1,277 @@
>> +/*
>> + * This file is part of the libvirt-go-xml project
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
>> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + *
>> + * Copyright (C) 2017 Red Hat, Inc.
>> + *
>> + */
>> +
>> +package libvirtxml
>> +
>> +import (
>> +     "encoding/xml"
>> +     "fmt"
>> +)
>> +
>> +type NodeDevice struct {
>> +     Name       string          `xml:"name"`
>> +     Path       string          `xml:"path,omitempty"`
>> +     Parent     string          `xml:"parent,omitempty"`
>> +     Driver     string          `xml:"driver>name,omitempty"`
>> +     Capability *CapabilityType `xml:"capability"`
>> +}
>
> A single device can have multiple capabilities.
Do you mean it should be:
             Capability []CapabilityType `xml:"capability"` ?

>
>> +
>> +type CapabilityType struct {
>> +     Type interface{} `xml:"type,attr"`
>> +}
>
> I'm not convinced about this approach - it differs from what we've
> done in other schemas, where we just have a single struct containing
> the union of data from all types. The user has no idea what they
> should be providing for 'Type' here since its a generic interface
> type.
>
Yes. I wasn't sure about this either. Originally, I wanted to avoid
the Type attribute and make
CapabilityType to be an interface type.
However, I figured that it's not possible to write an UnmarshalXML for
an interface type.

The reason I didn't use the same approach as in the rest of the
schemas is because the Capability
determins it's type by a XML attribute and not an element name.
I didn't find an easy way to query the xml attribue as something
like.. `xml:"type=something,attr"`

OK, I'll try a ddiffernt approach.

>>
>> +type IDName struct {
>> +     ID   string `xml:"id,attr"`
>> +     Name string `xml:",chardata"`
>> +}
>> +
>> +type PciExpress struct {
>> +     Links []PciExpressLink `xml:"link"`
>> +}
>> +
>> +type PciExpressLink struct {
>> +     Validity string  `xml:"validity,attr,omitempty"`
>> +     Speed    float64 `xml:"speed,attr,omitempty"`
>> +     Port     int     `xml:"port,attr,omitempty"`
>> +     Width    int     `xml:"width,attr,omitempty"`
>> +}
>> +
>> +type IOMMUGroupType struct {
>> +     Number int `xml:"number,attr"`
>> +}
>> +
>> +type NUMA struct {
>> +     Node int `xml:"node,attr"`
>> +}
>> +
>> +type PciCapabilityType struct {
>> +     Domain       int             `xml:"domain,omitempty"`
>> +     Bus          int             `xml:"bus,omitempty"`
>> +     Slot         int             `xml:"slot,omitempty"`
>> +     Function     int             `xml:"function,omitempty"`
>> +     Product      IDName          `xml:"product,omitempty"`
>> +     Vendor       IDName          `xml:"vendor,omitempty"`
>> +     IommuGroup   IOMMUGroupType  `xml:"iommuGroup,omitempty"`
>> +     Numa         NUMA            `xml:"numa,omitempty"`
>> +     PciExpress   PciExpress      `xml:"pci-express,omitempty"`
>> +     Capabilities []PciCapability `xml:"capability,omitempty"`
>> +}
>> +
>> +type PCIAddress struct {
>> +     Domain   string `xml:"domain,attr"`
>> +     Bus      string `xml:"bus,attr"`
>> +     Slot     string `xml:"slot,attr"`
>> +     Function string `xml:"function,attr"`
>> +}
>> +
>> +type PciCapability struct {
>
> There's alot of inconsistency in capitalization of abbreviations
> in this file.  PCI vs Pci,  Numa vs NUMA, IOMMU vs Iiommu. They
> should generally always be capitalized.
>
>> +     Type     string`xml:"type,attr"`
>> +     Address  []PCIAddress `xml:"address,omitempty"`
>> +     MaxCount int          `xml:"maxCount,attr,omitempty"`
>> +}
>> +
>> +type SystemHardware struct {
>> +     Vendor  string `xml:"vendor"`
>> +     Version string `xml:"version"`
>> +     Serial  string `xml:"serial"`
>> +     UUID    string `xml:"uuid"`
>> +}
>> +
>> +type SystemFirmware struct {
>> +     Vendor      string `xml:"vendor"`
>> +     Version     string `xml:"version"`
>> +     ReleaseData string `xml:"release_date"`
>> +}
>> +
>> +type SystemCapabilityType struct {
>> +     Product  string         `xml:"product"`
>> +     Hardware SystemHardware `xml:"hardware"`
>> +     Firmware SystemFirmware `xml:"firmware"`
>> +}
>> +
>> +type USBDeviceCapabilityType struct {
>> +     Bus     int    `xml:"bus"`
>> +     Device  int    `xml:"device"`
>> +     Product IDName `xml:"product,omitempty"`
>> +     Vendor  IDName `xml:"vendor,omitempty"`
>> +}
>> +
>> +type USBCapabilityType struct {
>> +     Number      int    `xml:"number"`
>> +     Class       int    `xml:"class"`
>> +     Subclass    int    `xml:"subclass"`
>> +     Protocol    int    `xml:"protocol"`
>> +     Description string `xml:"description,omitempty"`
>> +}
>> +
>> +type NetOffloadFeatures struct {
>> +     Name string `xml:"number"`
>> +}
>> +
>> +type NetLink struct {
>> +     State string `xml:"state,attr"`
>> +     Speed string `xml:"speed,attr,omitempty"`
>> +}
>> +
>> +type NetCapability struct {
>> +     Type string `xml:"type,attr"`
>> +}
>> +
>> +type NetCapabilityType struct {
>> +     Interface  string               `xml:"interface"`
>> +     Address    string               `xml:"address"`
>> +     Link       NetLink              `xml:"link"`
>> +     Features   []NetOffloadFeatures `xml:"feature,omitempty"`
>> +     Capability NetCapability        `xml:"capability"`
>> +}
>> +
>> +type SCSIVportsOPS struct {
>> +     Vports    int `xml:"vports,omitempty"`
>> +     MaxVports int `xml:"maxvports,,omitempty"`
>> +}
>> +
>> +type SCSIFCHost struct {
>> +     WWNN      string `xml:"wwnn,omitempty"`
>> +     WWPN      string `xml:"wwpn,omitempty"`
>> +     FabricWWN string `xml:"fabric_wwn,omitempty"`
>> +}
>> +
>> +type SCSIHostCapability struct {
>> +     VportsOPS SCSIVportsOPS `xml:"vports_ops"`
>> +     FCHost    SCSIFCHost    `xml:"fc_host"`
>> +}
>> +
>> +type SCSIHostCapabilityType struct {
>> +     Host       int                `xml:"host"`
>> +     UniqueID   int                `xml:"unique_id"`
>> +     Capability SCSIHostCapability `xml:"capability"`
>> +}
>> +
>> +type SCSICapabilityType struct {
>> +     Host   int    `xml:"host"`
>> +     Bus    int    `xml:"bus"`
>> +     Target int    `xml:"target"`
>> +     Lun    int    `xml:"lun"`
>> +     Type   string `xml:"type"`
>> +}
>> +
>> +type StroageCap struct {
>
> s/Stroage/Storage/
>
>> +     Type           string `xml:"match,attr"`
>> +     MediaAvailable int    `xml:"media_available,omitempty"`
>> +     MediaSize      int    `xml:"media_size,omitempty"`
>> +     MediaLable     int    `xml:"media_label,omitempty"`
>> +}
>> +
>> +type StorageCapabilityType struct {
>> +     Block        string     `xml:"block"`
>> +     Bus          string     `xml:"bus"`
>> +     DriverType   string     `xml:"drive_type"`
>> +     Model        string     `xml:"model"`
>> +     Vendor       string     `xml:"vendor"`
>> +     Serial       string     `xml:"serial"`
>> +     Size         int        `xml:"size"`
>> +     Capatibility StroageCap `xml:"capability,omitempty"`
>> +}
>> +
>> +type DRMCapabilityType struct {
>> +     Type string `xml:"type"`
>> +}
>
> Note that every struct is in the same 'libvirtxml' namespace, even if the
> declarations are spread across different .go files. We need the structs
> for each XML document to be isolated from each other, so you need to have
> a 'NodeDevice' prefix on all these struct names.
>
>
>> +
>> +func (c *CapabilityType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
>> +     for _, attr := range start.Attr {
>> +             fmt.Println(attr.Name.Local)
>> +             if attr.Name.Local == "type" {
>> +                     switch attr.Value {
>> +                     case "pci":
>> +                             var pciCaps PciCapabilityType
>> +                             if err := d.DecodeElement(&pciCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = pciCaps
>> +                     case "system":
>> +                             var systemCaps SystemCapabilityType
>> +                             if err := d.DecodeElement(&systemCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = systemCaps
>> +                     case "usb_device":
>> +                             var usbdevCaps USBDeviceCapabilityType
>> +                             if err := d.DecodeElement(&usbdevCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = usbdevCaps
>> +                     case "usb":
>> +                             var usbCaps USBCapabilityType
>> +                             if err := d.DecodeElement(&usbCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = usbCaps
>> +                     case "net":
>> +                             var netCaps NetCapabilityType
>> +                             if err := d.DecodeElement(&netCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = netCaps
>> +                     case "scsi_host":
>> +                             var scsiHostCaps SCSIHostCapabilityType
>> +                             if err := d.DecodeElement(&scsiHostCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = scsiHostCaps
>> +                     case "scsi":
>> +                             var scsiCaps SCSICapabilityType
>> +                             if err := d.DecodeElement(&scsiCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = scsiCaps
>> +                     case "storage":
>> +                             var storageCaps StorageCapabilityType
>> +                             if err := d.DecodeElement(&storageCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = storageCaps
>> +                     case "drm":
>> +                             var drmCaps DRMCapabilityType
>> +                             if err := d.DecodeElement(&drmCaps, &start); err != nil {
>> +                                     return err
>> +                             }
>> +                             c.Type = drmCaps
>> +                     }
>> +             }
>> +     }
>> +     return nil
>> +}
>> +
>> +func (c *NodeDevice) Unmarshal(doc string) error {
>> +     return xml.Unmarshal([]byte(doc), c)
>> +}
>> +
>> +func (c *NodeDevice) Marshal() (string, error) {
>> +     doc, err := xml.MarshalIndent(c, "", "  ")
>> +     if err != nil {
>> +             return "", err
>> +     }
>> +     return string(doc), nil
>> +}
>> diff --git a/node_device_test.go b/node_device_test.go
>> new file mode 100644
>> index 0000000..129956b
>> --- /dev/null
>> +++ b/node_device_test.go
>> @@ -0,0 +1,111 @@
>> +/*
>> + * This file is part of the libvirt-go-xml project
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
>> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + *
>> + * Copyright (C) 2017 Red Hat, Inc.
>> + *
>> + */
>> +
>> +package libvirtxml
>> +
>> +import (
>> +     "reflect"
>> +     "strings"
>> +     "testing"
>> +)
>> +
>> +var NodeDeviceTestData = []struct {
>> +     Object *NodeDevice
>> +     XML    []string
>> +}{
>> +     {
>> +             Object: &NodeDevice{
>> +                     Name:   "pci_0000_81_00_0",
>> +                     Parent: "pci_0000_80_01_0",
>> +                     Driver: "ixgbe",
>> +                     Capability: &CapabilityType{
>> +                             Type: PciCapabilityType{
>> +                                     Domain:   1,
>> +                                     Bus:      21,
>> +                                     Slot:     10,
>> +                                     Function: 50,
>> +                                     Product: IDName{
>> +                                             ID:   "0x1528",
>> +                                             Name: "Ethernet Controller 10-Gigabit X540-AT2",
>> +                                     },
>> +                                     Vendor: IDName{
>> +                                             ID:   "0x8086",
>> +                                             Name: "Intel Corporation",
>> +                                     },
>> +                                     IommuGroup: IOMMUGroupType{
>> +                                             Number: 3,
>> +                                     },
>> +                                     Numa: NUMA{
>> +                                             Node: 1,
>> +                                     },
>> +                                     Capabilities: []PciCapability{
>> +                                             PciCapability{
>> +                                                     Type:     "virt_functions",
>> +                                                     MaxCount: 63,
>> +                                             },
>> +                                     },
>> +                             },
>> +                     },
>> +             },
>> +             XML: []string{
>> +                     `<device>`,
>> +                     `  <name>pci_0000_81_00_0</name>`,
>> +                     `  <parent>pci_0000_80_01_0</parent>`,
>> +                     `  <driver>`,
>> +                     `               <name>ixgbe</name>`,
>> +                     `  </driver>`,
>> +                     `  <capability type='pci'>`,
>> +                     `       <domain>1</domain>`,
>> +                     `       <bus>21</bus>`,
>> +                     `       <slot>10</slot>`,
>> +                     `       <function>50</function>`,
>> +                     `       <product id='0x1528'>Ethernet Controller 10-Gigabit X540-AT2</product>`,
>> +                     `       <vendor id='0x8086'>Intel Corporation</vendor>`,
>> +                     `       <capability type='virt_functions' maxCount='63'/>`,
>> +                     `       <iommuGroup number='3'>`,
>> +                     `         <address domain='0x0000' bus='0x15' slot='0x00' function='0x4'/>`,
>> +                     `       </iommuGroup>`,
>> +                     `   <numa node='1'/>`,
>> +                     `  </capability>`,
>> +                     `</device>`,
>> +             },
>> +     },
>> +}
>> +
>> +func TestNodeDevice(t *testing.T) {
>> +     for _, test := range NodeDeviceTestData {
>> +             xmlDoc := strings.Join(test.XML, "\n")
>> +             nodeDevice := NodeDevice{}
>> +             err := nodeDevice.Unmarshal(xmlDoc)
>> +             if err != nil {
>> +                     t.Fatal(err)
>> +             }
>> +
>> +             res := reflect.DeepEqual(&nodeDevice, test.Object)
>> +             if !res {
>> +                     t.Fatal("Bad NodeDevice object creation.")
>> +             }
>> +     }
>> +}
>> --
>> 2.9.4
>>
>> --
>> libvir-list mailing list
>> libvir-list at redhat.com
>> https://www.redhat.com/mailman/listinfo/libvir-list
>
> Regards,
> Daniel
> --
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|




More information about the libvir-list mailing list