/* * 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" "io" "strconv" "strings" ) type NodeDevice struct { XMLName xml.Name `xml:"device"` Name string `xml:"name"` Path string `xml:"path,omitempty"` DevNodes []NodeDeviceDevNode `xml:"devnode"` Parent string `xml:"parent,omitempty"` Driver *NodeDeviceDriver `xml:"driver"` Capability NodeDeviceCapability `xml:"capability"` } type NodeDeviceDevNode struct { Type string `xml:"type,attr,omitempty"` Path string `xml:",chardata"` } type NodeDeviceDriver struct { Name string `xml:"name"` } type NodeDeviceCapability struct { System *NodeDeviceSystemCapability PCI *NodeDevicePCICapability USB *NodeDeviceUSBCapability USBDevice *NodeDeviceUSBDeviceCapability Net *NodeDeviceNetCapability SCSIHost *NodeDeviceSCSIHostCapability SCSITarget *NodeDeviceSCSITargetCapability SCSI *NodeDeviceSCSICapability Storage *NodeDeviceStorageCapability DRM *NodeDeviceDRMCapability CCW *NodeDeviceCCWCapability MDev *NodeDeviceMDevCapability } type NodeDeviceIDName struct { ID string `xml:"id,attr"` Name string `xml:",chardata"` } type NodeDevicePCIExpress struct { Links []NodeDevicePCIExpressLink `xml:"link"` } type NodeDevicePCIExpressLink struct { Validity string `xml:"validity,attr,omitempty"` Speed float64 `xml:"speed,attr,omitempty"` Port *uint `xml:"port,attr"` Width *uint `xml:"width,attr"` } type NodeDeviceIOMMUGroup struct { Number int `xml:"number,attr"` Address []NodeDevicePCIAddress `xml:"address"` } type NodeDeviceNUMA struct { Node int `xml:"node,attr"` } type NodeDevicePCICapability struct { Class string `xml:"class,omitempty"` Domain *uint `xml:"domain"` Bus *uint `xml:"bus"` Slot *uint `xml:"slot"` Function *uint `xml:"function"` Product NodeDeviceIDName `xml:"product,omitempty"` Vendor NodeDeviceIDName `xml:"vendor,omitempty"` IOMMUGroup *NodeDeviceIOMMUGroup `xml:"iommuGroup"` NUMA *NodeDeviceNUMA `xml:"numa"` PCIExpress *NodeDevicePCIExpress `xml:"pci-express"` Capabilities []NodeDevicePCISubCapability `xml:"capability"` } type NodeDevicePCIAddress struct { Domain *uint `xml:"domain,attr"` Bus *uint `xml:"bus,attr"` Slot *uint `xml:"slot,attr"` Function *uint `xml:"function,attr"` } type NodeDevicePCISubCapability struct { VirtFunctions *NodeDevicePCIVirtFunctionsCapability PhysFunction *NodeDevicePCIPhysFunctionCapability MDevTypes *NodeDevicePCIMDevTypesCapability Bridge *NodeDevicePCIBridgeCapability } type NodeDevicePCIVirtFunctionsCapability struct { Address []NodeDevicePCIAddress `xml:"address,omitempty"` MaxCount int `xml:"maxCount,attr,omitempty"` } type NodeDevicePCIPhysFunctionCapability struct { Address NodeDevicePCIAddress `xml:"address,omitempty"` } type NodeDevicePCIMDevTypesCapability struct { Types []NodeDevicePCIMDevType `xml:"type"` } type NodeDevicePCIMDevType struct { ID string `xml:"id,attr"` Name string `xml:"name"` DeviceAPI string `xml:"deviceAPI"` AvailableInstances uint `xml:"availableInstances"` } type NodeDevicePCIBridgeCapability struct { } type NodeDeviceSystemHardware struct { Vendor string `xml:"vendor"` Version string `xml:"version"` Serial string `xml:"serial"` UUID string `xml:"uuid"` } type NodeDeviceSystemFirmware struct { Vendor string `xml:"vendor"` Version string `xml:"version"` ReleaseData string `xml:"release_date"` } type NodeDeviceSystemCapability struct { Product string `xml:"product,omitempty"` Hardware *NodeDeviceSystemHardware `xml:"hardware"` Firmware *NodeDeviceSystemFirmware `xml:"firmware"` } type NodeDeviceUSBDeviceCapability struct { Bus int `xml:"bus"` Device int `xml:"device"` Product NodeDeviceIDName `xml:"product,omitempty"` Vendor NodeDeviceIDName `xml:"vendor,omitempty"` } type NodeDeviceUSBCapability struct { Number int `xml:"number"` Class int `xml:"class"` Subclass int `xml:"subclass"` Protocol int `xml:"protocol"` Description string `xml:"description,omitempty"` } type NodeDeviceNetOffloadFeatures struct { Name string `xml:"name,attr"` } type NodeDeviceNetLink struct { State string `xml:"state,attr"` Speed string `xml:"speed,attr,omitempty"` } type NodeDeviceNetSubCapability struct { Wireless80211 *NodeDeviceNet80211Capability Ethernet80203 *NodeDeviceNet80203Capability } type NodeDeviceNet80211Capability struct { } type NodeDeviceNet80203Capability struct { } type NodeDeviceNetCapability struct { Interface string `xml:"interface"` Address string `xml:"address"` Link *NodeDeviceNetLink `xml:"link"` Features []NodeDeviceNetOffloadFeatures `xml:"feature,omitempty"` Capability []NodeDeviceNetSubCapability `xml:"capability"` } type NodeDeviceSCSIVPortOpsCapability struct { VPorts int `xml:"vports,omitempty"` MaxVPorts int `xml:"maxvports,omitempty"` } type NodeDeviceSCSIFCHostCapability struct { WWNN string `xml:"wwnn,omitempty"` WWPN string `xml:"wwpn,omitempty"` FabricWWN string `xml:"fabric_wwn,omitempty"` } type NodeDeviceSCSIHostSubCapability struct { VPortOps *NodeDeviceSCSIVPortOpsCapability FCHost *NodeDeviceSCSIFCHostCapability } type NodeDeviceSCSIHostCapability struct { Host uint `xml:"host"` UniqueID *uint `xml:"unique_id"` Capability []NodeDeviceSCSIHostSubCapability `xml:"capability"` } type NodeDeviceSCSITargetCapability struct { Target string `xml:"target"` Capability []NodeDeviceSCSITargetSubCapability `xml:"capability"` } type NodeDeviceSCSITargetSubCapability struct { FCRemotePort *NodeDeviceSCSIFCRemotePortCapability } type NodeDeviceSCSIFCRemotePortCapability struct { RPort string `xml:"rport"` WWPN string `xml:"wwpn"` } type NodeDeviceSCSICapability struct { Host int `xml:"host"` Bus int `xml:"bus"` Target int `xml:"target"` Lun int `xml:"lun"` Type string `xml:"type"` } type NodeDeviceStorageSubCapability struct { Removable *NodeDeviceStorageRemovableCapability } type NodeDeviceStorageRemovableCapability struct { MediaAvailable *uint `xml:"media_available"` MediaSize *uint `xml:"media_size"` MediaLabel string `xml:"media_label,omitempty"` LogicalBlockSize *uint `xml:"logical_block_size"` NumBlocks *uint `xml:"num_blocks"` } type NodeDeviceStorageCapability struct { Block string `xml:"block,omitempty"` Bus string `xml:"bus,omitempty"` DriverType string `xml:"drive_type,omitempty"` Model string `xml:"model,omitempty"` Vendor string `xml:"vendor,omitempty"` Serial string `xml:"serial,omitempty"` Size *uint `xml:"size"` LogicalBlockSize *uint `xml:"logical_block_size"` NumBlocks *uint `xml:"num_blocks"` Capability []NodeDeviceStorageSubCapability `xml:"capability"` } type NodeDeviceDRMCapability struct { Type string `xml:"type"` } type NodeDeviceCCWCapability struct { CSSID *uint `xml:"cssid"` SSID *uint `xml:"ssid"` DevNo *uint `xml:"devno"` } type NodeDeviceMDevCapability struct { Type *NodeDeviceMDevCapabilityType `xml:"type"` IOMMUGroup *NodeDeviceIOMMUGroup `xml:"iommuGroup"` } type NodeDeviceMDevCapabilityType struct { ID string `xml:"id,attr"` } func (a *NodeDevicePCIAddress) MarshalXML(e *xml.Encoder, start xml.StartElement) error { marshalUintAttr(&start, "domain", a.Domain, "0x%04x") marshalUintAttr(&start, "bus", a.Bus, "0x%02x") marshalUintAttr(&start, "slot", a.Slot, "0x%02x") marshalUintAttr(&start, "function", a.Function, "0x%x") e.EncodeToken(start) e.EncodeToken(start.End()) return nil } func (a *NodeDevicePCIAddress) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for _, attr := range start.Attr { if attr.Name.Local == "domain" { if err := unmarshalUintAttr(attr.Value, &a.Domain, 0); err != nil { return err } } else if attr.Name.Local == "bus" { if err := unmarshalUintAttr(attr.Value, &a.Bus, 0); err != nil { return err } } else if attr.Name.Local == "slot" { if err := unmarshalUintAttr(attr.Value, &a.Slot, 0); err != nil { return err } } else if attr.Name.Local == "function" { if err := unmarshalUintAttr(attr.Value, &a.Function, 0); err != nil { return err } } } d.Skip() return nil } func (c *NodeDeviceCCWCapability) MarshalXML(e *xml.Encoder, start xml.StartElement) error { e.EncodeToken(start) if c.CSSID != nil { cssid := xml.StartElement{ Name: xml.Name{Local: "cssid"}, } e.EncodeToken(cssid) e.EncodeToken(xml.CharData(fmt.Sprintf("0x%x", *c.CSSID))) e.EncodeToken(cssid.End()) } if c.SSID != nil { ssid := xml.StartElement{ Name: xml.Name{Local: "ssid"}, } e.EncodeToken(ssid) e.EncodeToken(xml.CharData(fmt.Sprintf("0x%x", *c.SSID))) e.EncodeToken(ssid.End()) } if c.DevNo != nil { devno := xml.StartElement{ Name: xml.Name{Local: "devno"}, } e.EncodeToken(devno) e.EncodeToken(xml.CharData(fmt.Sprintf("0x%04x", *c.DevNo))) e.EncodeToken(devno.End()) } e.EncodeToken(start.End()) return nil } func (c *NodeDeviceCCWCapability) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for { tok, err := d.Token() if err == io.EOF { break } if err != nil { return err } switch tok := tok.(type) { case xml.StartElement: cdata, err := d.Token() if err != nil { return err } if tok.Name.Local != "cssid" && tok.Name.Local != "ssid" && tok.Name.Local != "devno" { continue } chardata, ok := cdata.(xml.CharData) if !ok { return fmt.Errorf("Expected text for CCW '%s'", tok.Name.Local) } valstr := strings.TrimPrefix(string(chardata), "0x") val, err := strconv.ParseUint(valstr, 16, 64) if err != nil { return err } vali := uint(val) if tok.Name.Local == "cssid" { c.CSSID = &vali } else if tok.Name.Local == "ssid" { c.SSID = &vali } else if tok.Name.Local == "devno" { c.DevNo = &vali } } } return nil } func (c *NodeDevicePCISubCapability) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { typ, ok := getAttr(start.Attr, "type") if !ok { return fmt.Errorf("Missing node device capability type") } switch typ { case "virt_functions": var virtFuncCaps NodeDevicePCIVirtFunctionsCapability if err := d.DecodeElement(&virtFuncCaps, &start); err != nil { return err } c.VirtFunctions = &virtFuncCaps case "phys_function": var physFuncCaps NodeDevicePCIPhysFunctionCapability if err := d.DecodeElement(&physFuncCaps, &start); err != nil { return err } c.PhysFunction = &physFuncCaps case "mdev_types": var mdevTypeCaps NodeDevicePCIMDevTypesCapability if err := d.DecodeElement(&mdevTypeCaps, &start); err != nil { return err } c.MDevTypes = &mdevTypeCaps case "pci-bridge": var bridgeCaps NodeDevicePCIBridgeCapability if err := d.DecodeElement(&bridgeCaps, &start); err != nil { return err } c.Bridge = &bridgeCaps } d.Skip() return nil } func (c *NodeDevicePCISubCapability) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if c.VirtFunctions != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "virt_functions", }) return e.EncodeElement(c.VirtFunctions, start) } else if c.PhysFunction != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "phys_function", }) return e.EncodeElement(c.PhysFunction, start) } else if c.MDevTypes != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "mdev_types", }) return e.EncodeElement(c.MDevTypes, start) } else if c.Bridge != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "pci-bridge", }) return e.EncodeElement(c.Bridge, start) } return nil } func (c *NodeDeviceSCSITargetSubCapability) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { typ, ok := getAttr(start.Attr, "type") if !ok { return fmt.Errorf("Missing node device capability type") } switch typ { case "fc_remote_port": var fcCaps NodeDeviceSCSIFCRemotePortCapability if err := d.DecodeElement(&fcCaps, &start); err != nil { return err } c.FCRemotePort = &fcCaps } d.Skip() return nil } func (c *NodeDeviceSCSITargetSubCapability) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if c.FCRemotePort != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "fc_remote_port", }) return e.EncodeElement(c.FCRemotePort, start) } return nil } func (c *NodeDeviceSCSIHostSubCapability) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { typ, ok := getAttr(start.Attr, "type") if !ok { return fmt.Errorf("Missing node device capability type") } switch typ { case "fc_host": var fcCaps NodeDeviceSCSIFCHostCapability if err := d.DecodeElement(&fcCaps, &start); err != nil { return err } c.FCHost = &fcCaps case "vport_ops": var vportCaps NodeDeviceSCSIVPortOpsCapability if err := d.DecodeElement(&vportCaps, &start); err != nil { return err } c.VPortOps = &vportCaps } d.Skip() return nil } func (c *NodeDeviceSCSIHostSubCapability) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if c.FCHost != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "fc_host", }) return e.EncodeElement(c.FCHost, start) } else if c.VPortOps != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "vport_ops", }) return e.EncodeElement(c.VPortOps, start) } return nil } func (c *NodeDeviceStorageSubCapability) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { typ, ok := getAttr(start.Attr, "type") if !ok { return fmt.Errorf("Missing node device capability type") } switch typ { case "removable": var removeCaps NodeDeviceStorageRemovableCapability if err := d.DecodeElement(&removeCaps, &start); err != nil { return err } c.Removable = &removeCaps } d.Skip() return nil } func (c *NodeDeviceStorageSubCapability) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if c.Removable != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "removable", }) return e.EncodeElement(c.Removable, start) } return nil } func (c *NodeDeviceNetSubCapability) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { typ, ok := getAttr(start.Attr, "type") if !ok { return fmt.Errorf("Missing node device capability type") } switch typ { case "80211": var wlanCaps NodeDeviceNet80211Capability if err := d.DecodeElement(&wlanCaps, &start); err != nil { return err } c.Wireless80211 = &wlanCaps case "80203": var ethCaps NodeDeviceNet80203Capability if err := d.DecodeElement(ðCaps, &start); err != nil { return err } c.Ethernet80203 = ðCaps } d.Skip() return nil } func (c *NodeDeviceNetSubCapability) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if c.Wireless80211 != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "80211", }) return e.EncodeElement(c.Wireless80211, start) } else if c.Ethernet80203 != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "80203", }) return e.EncodeElement(c.Ethernet80203, start) } return nil } func (c *NodeDeviceCapability) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { typ, ok := getAttr(start.Attr, "type") if !ok { return fmt.Errorf("Missing node device capability type") } switch typ { case "pci": var pciCaps NodeDevicePCICapability if err := d.DecodeElement(&pciCaps, &start); err != nil { return err } c.PCI = &pciCaps case "system": var systemCaps NodeDeviceSystemCapability if err := d.DecodeElement(&systemCaps, &start); err != nil { return err } c.System = &systemCaps case "usb_device": var usbdevCaps NodeDeviceUSBDeviceCapability if err := d.DecodeElement(&usbdevCaps, &start); err != nil { return err } c.USBDevice = &usbdevCaps case "usb": var usbCaps NodeDeviceUSBCapability if err := d.DecodeElement(&usbCaps, &start); err != nil { return err } c.USB = &usbCaps case "net": var netCaps NodeDeviceNetCapability if err := d.DecodeElement(&netCaps, &start); err != nil { return err } c.Net = &netCaps case "scsi_host": var scsiHostCaps NodeDeviceSCSIHostCapability if err := d.DecodeElement(&scsiHostCaps, &start); err != nil { return err } c.SCSIHost = &scsiHostCaps case "scsi_target": var scsiTargetCaps NodeDeviceSCSITargetCapability if err := d.DecodeElement(&scsiTargetCaps, &start); err != nil { return err } c.SCSITarget = &scsiTargetCaps case "scsi": var scsiCaps NodeDeviceSCSICapability if err := d.DecodeElement(&scsiCaps, &start); err != nil { return err } c.SCSI = &scsiCaps case "storage": var storageCaps NodeDeviceStorageCapability if err := d.DecodeElement(&storageCaps, &start); err != nil { return err } c.Storage = &storageCaps case "drm": var drmCaps NodeDeviceDRMCapability if err := d.DecodeElement(&drmCaps, &start); err != nil { return err } c.DRM = &drmCaps case "ccw": var ccwCaps NodeDeviceCCWCapability if err := d.DecodeElement(&ccwCaps, &start); err != nil { return err } c.CCW = &ccwCaps case "mdev": var mdevCaps NodeDeviceMDevCapability if err := d.DecodeElement(&mdevCaps, &start); err != nil { return err } c.MDev = &mdevCaps } d.Skip() return nil } func (c *NodeDeviceCapability) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if c.PCI != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "pci", }) return e.EncodeElement(c.PCI, start) } else if c.System != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "system", }) return e.EncodeElement(c.System, start) } else if c.USB != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "usb", }) return e.EncodeElement(c.USB, start) } else if c.USBDevice != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "usb_device", }) return e.EncodeElement(c.USBDevice, start) } else if c.Net != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "net", }) return e.EncodeElement(c.Net, start) } else if c.SCSI != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "scsi", }) return e.EncodeElement(c.SCSI, start) } else if c.SCSIHost != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "scsi_host", }) return e.EncodeElement(c.SCSIHost, start) } else if c.SCSITarget != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "scsi_target", }) return e.EncodeElement(c.SCSITarget, start) } else if c.Storage != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "storage", }) return e.EncodeElement(c.Storage, start) } else if c.DRM != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "drm", }) return e.EncodeElement(c.DRM, start) } else if c.CCW != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "ccw", }) return e.EncodeElement(c.CCW, start) } else if c.MDev != nil { start.Attr = append(start.Attr, xml.Attr{ xml.Name{Local: "type"}, "mdev", }) return e.EncodeElement(c.MDev, start) } 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 }