Revision 7432d332 lib/ovf.py
b/lib/ovf.py | ||
---|---|---|
80 | 80 |
RASD_TYPE = { |
81 | 81 |
"vcpus": "3", |
82 | 82 |
"memory": "4", |
83 |
"scsi-controller": "6", |
|
84 |
"ethernet-adapter": "10", |
|
85 |
"disk": "17", |
|
83 | 86 |
} |
84 | 87 |
|
85 | 88 |
# AllocationUnits values and conversion |
... | ... | |
107 | 110 |
TAGS = "tags" |
108 | 111 |
VERSION = "version" |
109 | 112 |
|
113 |
# Instance IDs of System and SCSI controller |
|
114 |
SYSTEM_ID = 0 |
|
115 |
SCSI_ID = 3 |
|
116 |
|
|
117 |
# Disk format descriptions |
|
118 |
DISK_FORMAT = { |
|
119 |
"raw": "http://en.wikipedia.org/wiki/Byte", |
|
120 |
"vmdk": "http://www.vmware.com/interfaces/specifications/vmdk.html" |
|
121 |
"#monolithicSparse", |
|
122 |
"cow": "http://www.gnome.org/~markmc/qcow-image-format.html", |
|
123 |
} |
|
124 |
|
|
110 | 125 |
|
111 | 126 |
def LinkFile(old_path, prefix=None, suffix=None, directory=None): |
112 | 127 |
"""Create link with a given prefix and suffix. |
... | ... | |
539 | 554 |
return results |
540 | 555 |
|
541 | 556 |
|
557 |
def SubElementText(parent, tag, text, attrib={}, **extra): |
|
558 |
# pylint: disable=W0102 |
|
559 |
"""This is just a wrapper on ET.SubElement that always has text content. |
|
560 |
|
|
561 |
""" |
|
562 |
if text is None: |
|
563 |
return None |
|
564 |
elem = ET.SubElement(parent, tag, attrib=attrib, **extra) |
|
565 |
elem.text = str(text) |
|
566 |
return elem |
|
567 |
|
|
568 |
|
|
542 | 569 |
class OVFWriter(object): |
543 | 570 |
"""Writer class for OVF files. |
544 | 571 |
|
545 | 572 |
@type tree: ET.ElementTree |
546 | 573 |
@ivar tree: XML tree that we are constructing |
574 |
@type hardware_list: list |
|
575 |
@ivar hardware_list: list of items prepared for VirtualHardwareSection |
|
547 | 576 |
|
548 | 577 |
""" |
549 | 578 |
def __init__(self, has_gnt_section): |
... | ... | |
565 | 594 |
if has_gnt_section: |
566 | 595 |
env_attribs["xmlns:gnt"] = GANETI_SCHEMA |
567 | 596 |
self.tree = ET.Element("Envelope", attrib=env_attribs) |
597 |
self.hardware_list = [] |
|
598 |
|
|
599 |
def SaveDisksData(self, disks): |
|
600 |
"""Convert disk information to certain OVF sections. |
|
601 |
|
|
602 |
@type disks: list |
|
603 |
@param disks: list of dictionaries of disk options from config.ini |
|
604 |
|
|
605 |
""" |
|
606 |
references = ET.SubElement(self.tree, "References") |
|
607 |
disk_section = ET.SubElement(self.tree, "DiskSection") |
|
608 |
for counter, disk in enumerate(disks): |
|
609 |
file_id = "file%s" % counter |
|
610 |
disk_id = "disk%s" % counter |
|
611 |
file_attribs = { |
|
612 |
"ovf:href": disk["path"], |
|
613 |
"ovf:size": str(disk["real-size"]), |
|
614 |
"ovf:id": file_id, |
|
615 |
} |
|
616 |
disk_attribs = { |
|
617 |
"ovf:capacity": str(disk["virt-size"]), |
|
618 |
"ovf:diskId": disk_id, |
|
619 |
"ovf:fileRef": file_id, |
|
620 |
"ovf:format": DISK_FORMAT.get(disk["format"], disk["format"]), |
|
621 |
} |
|
622 |
if "compression" in disk: |
|
623 |
file_attribs["ovf:compression"] = disk["compression"] |
|
624 |
ET.SubElement(references, "File", attrib=file_attribs) |
|
625 |
ET.SubElement(disk_section, "Disk", attrib=disk_attribs) |
|
626 |
|
|
627 |
# Item in VirtualHardwareSection creation |
|
628 |
disk_item = ET.Element("Item") |
|
629 |
SubElementText(disk_item, "rasd:ElementName", disk_id) |
|
630 |
SubElementText(disk_item, "rasd:ResourceType", RASD_TYPE["disk"]) |
|
631 |
SubElementText(disk_item, "rasd:HostResource", "ovf:/disk/%s" % disk_id) |
|
632 |
SubElementText(disk_item, "rasd:Parent", SCSI_ID) |
|
633 |
self.hardware_list.append(disk_item) |
|
634 |
|
|
635 |
def SaveNetworksData(self, networks): |
|
636 |
"""Convert network information to NetworkSection. |
|
637 |
|
|
638 |
@type networks: list |
|
639 |
@param networks: list of dictionaries of network options form config.ini |
|
640 |
|
|
641 |
""" |
|
642 |
network_section = ET.SubElement(self.tree, "NetworkSection") |
|
643 |
for counter, network in enumerate(networks): |
|
644 |
network_name = "%s%s" % (network["mode"], counter) |
|
645 |
network_attrib = {"ovf:name": network_name} |
|
646 |
ET.SubElement(network_section, "Network", attrib=network_attrib) |
|
647 |
|
|
648 |
# Item in VirtualHardwareSection creation |
|
649 |
network_item = ET.Element("Item") |
|
650 |
SubElementText(network_item, "rasd:ElementName", network_name) |
|
651 |
SubElementText(network_item, "rasd:ResourceType", |
|
652 |
RASD_TYPE["ethernet-adapter"]) |
|
653 |
SubElementText(network_item, "rasd:Connection", network_name) |
|
654 |
SubElementText(network_item, "rasd:Address", network["mac"]) |
|
655 |
self.hardware_list.append(network_item) |
|
656 |
|
|
657 |
@staticmethod |
|
658 |
def _SaveNameAndParams(root, data): |
|
659 |
"""Save name and parameters information under root using data. |
|
660 |
|
|
661 |
@type root: ET.Element |
|
662 |
@param root: root element for the Name and Parameters |
|
663 |
@type data: dict |
|
664 |
@param data: data from which we gather the values |
|
665 |
|
|
666 |
""" |
|
667 |
assert(data.get("name")) |
|
668 |
name = SubElementText(root, "gnt:Name", data["name"]) |
|
669 |
params = ET.SubElement(root, "gnt:Parameters") |
|
670 |
for name, value in data.iteritems(): |
|
671 |
if name != "name": |
|
672 |
SubElementText(params, "gnt:%s" % name, value) |
|
673 |
|
|
674 |
def SaveGanetiData(self, ganeti, networks): |
|
675 |
"""Convert Ganeti-specific information to GanetiSection. |
|
676 |
|
|
677 |
@type ganeti: dict |
|
678 |
@param ganeti: dictionary of Ganeti-specific options from config.ini |
|
679 |
@type networks: list |
|
680 |
@param networks: list of dictionaries of network options form config.ini |
|
681 |
|
|
682 |
""" |
|
683 |
ganeti_section = ET.SubElement(self.tree, "gnt:GanetiSection") |
|
684 |
|
|
685 |
SubElementText(ganeti_section, "gnt:Version", ganeti.get("version")) |
|
686 |
SubElementText(ganeti_section, "gnt:DiskTemplate", |
|
687 |
ganeti.get("disk_template")) |
|
688 |
SubElementText(ganeti_section, "gnt:AutoBalance", |
|
689 |
ganeti.get("auto_balance")) |
|
690 |
SubElementText(ganeti_section, "gnt:Tags", ganeti.get("tags")) |
|
691 |
|
|
692 |
osys = ET.SubElement(ganeti_section, "gnt:OperatingSystem") |
|
693 |
self._SaveNameAndParams(osys, ganeti["os"]) |
|
694 |
|
|
695 |
hypervisor = ET.SubElement(ganeti_section, "gnt:Hypervisor") |
|
696 |
self._SaveNameAndParams(hypervisor, ganeti["hypervisor"]) |
|
697 |
|
|
698 |
network_section = ET.SubElement(ganeti_section, "gnt:Network") |
|
699 |
for counter, network in enumerate(networks): |
|
700 |
network_name = "%s%s" % (network["mode"], counter) |
|
701 |
nic_attrib = {"ovf:name": network_name} |
|
702 |
nic = ET.SubElement(network_section, "gnt:Nic", attrib=nic_attrib) |
|
703 |
SubElementText(nic, "gnt:Mode", network["mode"]) |
|
704 |
SubElementText(nic, "gnt:MACAddress", network["mac"]) |
|
705 |
SubElementText(nic, "gnt:IPAddress", network["ip"]) |
|
706 |
SubElementText(nic, "gnt:Link", network["link"]) |
|
707 |
|
|
708 |
def SaveVirtualSystemData(self, name, vcpus, memory): |
|
709 |
"""Convert virtual system information to OVF sections. |
|
710 |
|
|
711 |
@type name: string |
|
712 |
@param name: name of the instance |
|
713 |
@type vcpus: int |
|
714 |
@param vcpus: number of VCPUs |
|
715 |
@type memory: int |
|
716 |
@param memory: RAM memory in MB |
|
717 |
|
|
718 |
""" |
|
719 |
assert(vcpus > 0) |
|
720 |
assert(memory > 0) |
|
721 |
vs_attrib = {"ovf:id": name} |
|
722 |
virtual_system = ET.SubElement(self.tree, "VirtualSystem", attrib=vs_attrib) |
|
723 |
|
|
724 |
name_section = ET.SubElement(virtual_system, "Name") |
|
725 |
name_section.text = name |
|
726 |
os_attrib = {"ovf:id": "0"} |
|
727 |
ET.SubElement(virtual_system, "OperatingSystemSection", |
|
728 |
attrib=os_attrib) |
|
729 |
hardware_section = ET.SubElement(virtual_system, "VirtualHardwareSection") |
|
730 |
|
|
731 |
# System description |
|
732 |
system = ET.SubElement(hardware_section, "System") |
|
733 |
SubElementText(system, "vssd:ElementName", "Virtual Hardware Family") |
|
734 |
SubElementText(system, "vssd:InstanceId", SYSTEM_ID) |
|
735 |
SubElementText(system, "vssd:VirtualSystemIdentifier", name) |
|
736 |
SubElementText(system, "vssd:VirtualSystemType", "ganeti-ovf") |
|
737 |
|
|
738 |
# Item for vcpus |
|
739 |
vcpus_item = ET.SubElement(hardware_section, "Item") |
|
740 |
SubElementText(vcpus_item, "rasd:ElementName", |
|
741 |
"%s virtual CPU(s)" % vcpus) |
|
742 |
SubElementText(vcpus_item, "rasd:InstanceID", "1") |
|
743 |
SubElementText(vcpus_item, "rasd:ResourceType", RASD_TYPE["vcpus"]) |
|
744 |
SubElementText(vcpus_item, "rasd:VirtualQuantity", vcpus) |
|
745 |
|
|
746 |
# Item for memory |
|
747 |
memory_item = ET.SubElement(hardware_section, "Item") |
|
748 |
SubElementText(memory_item, "rasd:AllocationUnits", "byte * 2^20") |
|
749 |
SubElementText(memory_item, "rasd:ElementName", "%sMB of memory" % memory) |
|
750 |
SubElementText(memory_item, "rasd:InstanceID", "2") |
|
751 |
SubElementText(memory_item, "rasd:ResourceType", RASD_TYPE["memory"]) |
|
752 |
SubElementText(memory_item, "rasd:VirtualQuantity", memory) |
|
753 |
|
|
754 |
# Item for scsi controller |
|
755 |
scsi_item = ET.SubElement(hardware_section, "Item") |
|
756 |
SubElementText(scsi_item, "rasd:Address", SYSTEM_ID) |
|
757 |
SubElementText(scsi_item, "rasd:ElementName", "scsi_controller0") |
|
758 |
SubElementText(scsi_item, "rasd:ResourceType", RASD_TYPE["scsi-controller"]) |
|
759 |
SubElementText(scsi_item, "rasd:InstanceId", "3") |
|
760 |
|
|
761 |
# Other items - from self.hardware_list |
|
762 |
for counter, item in enumerate(self.hardware_list): |
|
763 |
SubElementText(item, "rasd:InstanceID", counter + 4) |
|
764 |
hardware_section.append(item) |
|
568 | 765 |
|
569 | 766 |
def PrettyXmlDump(self): |
570 | 767 |
"""Formatter of the XML file. |
... | ... | |
1241 | 1438 |
@ivar output_path: complete path to .ovf file |
1242 | 1439 |
@type config_parser: L{ConfigParserWithDefaults} |
1243 | 1440 |
@ivar config_parser: parser for the config.ini file |
1441 |
@type reference_files: list |
|
1442 |
@ivar reference_files: files referenced in the ovf file |
|
1244 | 1443 |
@type results_disk: list |
1245 | 1444 |
@ivar results_disk: list of dictionaries of disk options from config.ini |
1246 | 1445 |
@type results_network: list |
... | ... | |
1528 | 1727 |
self.output_path = utils.PathJoin(self.output_dir, output_file) |
1529 | 1728 |
files_list = [self.output_path] |
1530 | 1729 |
|
1730 |
self.ovf_writer.SaveDisksData(self.results_disk) |
|
1731 |
self.ovf_writer.SaveNetworksData(self.results_network) |
|
1732 |
if not self.options.ext_usage: |
|
1733 |
self.ovf_writer.SaveGanetiData(self.results_ganeti, self.results_network) |
|
1734 |
|
|
1735 |
self.ovf_writer.SaveVirtualSystemData(self.results_name, self.results_vcpus, |
|
1736 |
self.results_memory) |
|
1737 |
|
|
1531 | 1738 |
data = self.ovf_writer.PrettyXmlDump() |
1532 | 1739 |
utils.WriteFile(self.output_path, data=data) |
1533 | 1740 |
|
Also available in: Unified diff