diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 53462463..adf75fd7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -3,17 +3,24 @@ name: Unit tests
on: [push, pull_request]
jobs:
- test_py2:
- runs-on: ubuntu-20.04
+ test:
+ strategy:
+ matrix:
+ include:
+ - pyversion: '2.7'
+ os: ubuntu-20.04
+ - pyversion: '3'
+ os: ubuntu-latest
+ runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- - name: Set up Python 2.7
+ - name: Set up Python ${{ matrix.pyversion }}
uses: actions/setup-python@v2
with:
- python-version: '2.7'
+ python-version: ${{ matrix.pyversion }}
- name: Install dependencies
run: |
@@ -21,7 +28,6 @@ jobs:
pip install -r requirements-dev.txt
# FIXME: branding.py still has no permanent home
curl https://gist.github.com/ydirson/3c36a7e19d762cc529a6c82340894ccc/raw/5ca39f621b1feab813e171f535c1aad1bd483f1d/branding.py -O -L
- pip install pyliblzma
pip install -e .
command -v xz
@@ -29,22 +35,22 @@ jobs:
run: |
pytest --cov -rP
coverage xml
- coverage html
- coverage html -d htmlcov-tests --include="tests/*"
- diff-cover --html-report coverage-diff.html coverage.xml
+ coverage html -d htmlcov-${{ matrix.pyversion }}
+ coverage html -d htmlcov-tests-${{ matrix.pyversion }} --include="tests/*"
+ diff-cover --compare-branch=origin/master --html-report coverage-diff-${{ matrix.pyversion }}.html coverage.xml
- name: Pylint
run: |
pylint --version
pylint --exit-zero xcp/ tests/ setup.py
pylint --exit-zero --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" xcp/ tests/ setup.py > pylint.txt
- diff-quality --violations=pylint --html-report pylint-diff.html pylint.txt
+ diff-quality --compare-branch=origin/master --violations=pylint --html-report pylint-diff-${{ matrix.pyversion }}.html pylint.txt
- uses: actions/upload-artifact@v3
with:
name: Coverage and pylint reports
path: |
- coverage-diff.html
- pylint-diff.html
- htmlcov
- htmlcov-tests
+ coverage-diff-${{ matrix.pyversion }}.html
+ pylint-diff-${{ matrix.pyversion }}.html
+ htmlcov-${{ matrix.pyversion }}
+ htmlcov-tests-${{ matrix.pyversion }}
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 37123724..ce760b45 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -5,3 +5,11 @@ diff_cover
mock
pytest
pytest-cov
+parameterized
+# dependencies also in setup.py until they can be used
+six
+future
+
+# python-2.7 only
+configparser ; python_version < "3.0"
+pyliblzma ; python_version < "3.0"
diff --git a/setup.py b/setup.py
index 9fb2bd83..08c8200d 100644
--- a/setup.py
+++ b/setup.py
@@ -35,5 +35,7 @@
],
requires=[
'branding',
+ 'six',
+ 'future',
],
)
diff --git a/tests/data/all_ethN.biosdevname b/tests/data/all_ethN.biosdevname
new file mode 100644
index 00000000..63bdc506
--- /dev/null
+++ b/tests/data/all_ethN.biosdevname
@@ -0,0 +1,68 @@
+BIOS device: eth0
+Kernel name: eth0
+Permanent MAC: EC:F4:BB:C3:AF:A8
+Assigned MAC : EC:F4:BB:C3:AF:A8
+Driver: ixgbe
+Driver version: 5.9.4
+Firmware version: 0x8000095c, 19.5.12
+Bus Info: 0000:01:00.0
+PCI name : 0000:01:00.0
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 1
+SMBIOS Label: Integrated NIC 1
+sysfs Label: NIC1
+Embedded Index: 1
+
+BIOS device: eth1
+Kernel name: eth1
+Permanent MAC: EC:F4:BB:C3:AF:AA
+Assigned MAC : EC:F4:BB:C3:AF:AA
+Driver: ixgbe
+Driver version: 5.9.4
+Firmware version: 0x8000095c, 19.5.12
+Bus Info: 0000:01:00.1
+PCI name : 0000:01:00.1
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 2
+SMBIOS Label: Integrated NIC 2
+sysfs Label: NIC2
+Embedded Index: 2
+
+BIOS device: eth2
+Kernel name: eth2
+Permanent MAC: EC:F4:BB:C3:AF:AC
+Assigned MAC : EC:F4:BB:C3:AF:AC
+Driver: igb
+Driver version: 5.3.5.20
+Firmware version: 1.67, 0x80000fab, 19.5.12
+Bus Info: 0000:07:00.0
+PCI name : 0000:07:00.0
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 3
+SMBIOS Label: Integrated NIC 3
+sysfs Label: NIC3
+VPD Port: 3
+VPD Index: 1
+VPD PCI master: 0000:07:00.0
+
+BIOS device: eth3
+Kernel name: eth3
+Permanent MAC: EC:F4:BB:C3:AF:AD
+Assigned MAC : EC:F4:BB:C3:AF:AD
+Driver: igb
+Driver version: 5.3.5.20
+Firmware version: 1.67, 0x80000fab, 19.5.12
+Bus Info: 0000:07:00.1
+PCI name : 0000:07:00.1
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 4
+SMBIOS Label: Integrated NIC 4
+sysfs Label: NIC4
+VPD Port: 4
+VPD Index: 1
+VPD PCI master: 0000:07:00.1
+
diff --git a/tests/data/grub-linux.cfg b/tests/data/grub-linux.cfg
new file mode 100644
index 00000000..e98e5d19
--- /dev/null
+++ b/tests/data/grub-linux.cfg
@@ -0,0 +1,5 @@
+set default=0
+menuentry 'linux' {
+ linux /boot/vmlinuz-1 ro
+ initrd /boot/initrd.img-1
+}
diff --git a/tests/data/grub.cfg b/tests/data/grub.cfg
new file mode 100644
index 00000000..c6d2b4a1
--- /dev/null
+++ b/tests/data/grub.cfg
@@ -0,0 +1,35 @@
+serial --unit=0 --speed=115200
+terminal_input serial console
+terminal_output serial console
+set default=0
+set timeout=5
+menuentry 'XCP-ng' {
+ search --label --set root root-vgdorj
+ multiboot2 /boot/xen.gz dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G console=vga vga=mode-0x0311
+ module2 /boot/vmlinuz-4.19-xen root=LABEL=root-vgdorj ro nolvm hpet=disable console=hvc0 console=tty0 quiet vga=785 splash plymouth.ignore-serial-consoles
+ module2 /boot/initrd-4.19-xen.img
+}
+menuentry 'XCP-ng (Serial)' {
+ search --label --set root root-vgdorj
+ multiboot2 /boot/xen.gz com1=115200,8n1 console=com1,vga dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G
+ module2 /boot/vmlinuz-4.19-xen root=LABEL=root-vgdorj ro nolvm hpet=disable console=tty0 console=hvc0
+ module2 /boot/initrd-4.19-xen.img
+}
+menuentry 'XCP-ng in Safe Mode' {
+ search --label --set root root-vgdorj
+ multiboot2 /boot/xen.gz nosmp noreboot noirqbalance no-mce no-bootscrub no-numa no-hap no-mmcfg max_cstate=0 nmi=ignore allow_unsafe dom0_mem=7584M,max:7584M com1=115200,8n1 console=com1,vga
+ module2 /boot/vmlinuz-4.19-xen earlyprintk=xen root=LABEL=root-vgdorj ro nolvm hpet=disable console=tty0 console=hvc0
+ module2 /boot/initrd-4.19-xen.img
+}
+menuentry 'XCP-ng (Xen 4.13.1 / Linux 4.19.0+1)' {
+ search --label --set root root-vgdorj
+ multiboot2 /boot/xen-fallback.gz dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G
+ module2 /boot/vmlinuz-fallback root=LABEL=root-vgdorj ro nolvm hpet=disable console=hvc0 console=tty0
+ module2 /boot/initrd-fallback.img
+}
+menuentry 'XCP-ng (Serial, Xen 4.13.1 / Linux 4.19.0+1)' {
+ search --label --set root root-vgdorj
+ multiboot2 /boot/xen-fallback.gz com1=115200,8n1 console=com1,vga dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G
+ module2 /boot/vmlinuz-fallback root=LABEL=root-vgdorj ro nolvm hpet=disable console=tty0 console=hvc0
+ module2 /boot/initrd-fallback.img
+}
diff --git a/tests/data/inventory/etc/xensource-inventory b/tests/data/inventory/etc/xensource-inventory
new file mode 100644
index 00000000..950472c0
--- /dev/null
+++ b/tests/data/inventory/etc/xensource-inventory
@@ -0,0 +1,23 @@
+PRIMARY_DISK='/dev/disk/by-id/scsi-123456789012345678901234567899012'
+PRODUCT_VERSION='8.2.1'
+DOM0_VCPUS='16'
+CONTROL_DOMAIN_UUID='3b1dccee-9da4-49c6-8250-e3725e0e4b2d'
+DOM0_MEM='7584'
+COMPANY_NAME_SHORT='Open Source'
+MANAGEMENT_ADDRESS_TYPE='IPv4'
+PARTITION_LAYOUT='ROOT,BACKUP,LOG,BOOT,SWAP,SR'
+PRODUCT_VERSION_TEXT='8.2'
+PRODUCT_BRAND='XCP-ng'
+INSTALLATION_UUID='b8b63475-d28c-44d4-9d1e-c38298ad3a0d'
+PRODUCT_VERSION_TEXT_SHORT='8.2'
+BRAND_CONSOLE='XCP-ng Center'
+PRODUCT_NAME='xenenterprise'
+MANAGEMENT_INTERFACE='xapi1'
+COMPANY_PRODUCT_BRAND='XCP-ng'
+PLATFORM_VERSION='3.2.1'
+BUILD_NUMBER='release/yangtze/master/58'
+PLATFORM_NAME='XCP'
+BRAND_CONSOLE_URL='https://xcp-ng.org'
+BACKUP_PARTITION='/dev/disk/by-id/scsi-123456789012345678901234567899012-part2'
+INSTALLATION_DATE='2021-08-03 16:39:24.077945'
+COMPANY_NAME='Open Source'
diff --git a/tests/data/lspci-mn b/tests/data/lspci-mn
new file mode 100644
index 00000000..4450e6d3
--- /dev/null
+++ b/tests/data/lspci-mn
@@ -0,0 +1,36 @@
+00:00.0 "0600" "1022" "1630" "1022" "1630"
+00:00.2 "0806" "1022" "1631" "1022" "1631"
+00:01.0 "0600" "1022" "1632" "" ""
+00:01.1 "0604" "1022" "1633" "" ""
+00:02.0 "0600" "1022" "1632" "" ""
+00:02.1 "0604" "1022" "1634" "" ""
+00:02.2 "0604" "1022" "1634" "" ""
+00:02.3 "0604" "1022" "1634" "" ""
+00:08.0 "0600" "1022" "1632" "" ""
+00:08.1 "0604" "1022" "1635" "" ""
+00:08.2 "0604" "1022" "1635" "" ""
+00:14.0 "0c05" "1022" "790b" -r51 "1462" "12ac"
+00:14.3 "0601" "1022" "790e" -r51 "1462" "12ac"
+00:18.0 "0600" "1022" "1448" "" ""
+00:18.1 "0600" "1022" "1449" "" ""
+00:18.2 "0600" "1022" "144a" "" ""
+00:18.3 "0600" "1022" "144b" "" ""
+00:18.4 "0600" "1022" "144c" "" ""
+00:18.5 "0600" "1022" "144d" "" ""
+00:18.6 "0600" "1022" "144e" "" ""
+00:18.7 "0600" "1022" "144f" "" ""
+01:00.0 "0604" "1002" "1478" -rc1 "" ""
+02:00.0 "0604" "1002" "1479" "" ""
+03:00.0 "0380" "1002" "7340" -rc1 "1462" "12ac"
+03:00.1 "0403" "1002" "ab38" "1462" "12ac"
+04:00.0 "0280" "8086" "2723" -r1a "8086" "0084"
+05:00.0 "0200" "10ec" "8168" -r15 "1462" "12ac"
+06:00.0 "0108" "144d" "a808" -p02 "144d" "a801"
+07:00.0 "0300" "1002" "1636" -rc6 "1462" "12ac"
+07:00.2 "1080" "1022" "15df" "1022" "15df"
+07:00.3 "0c03" "1022" "1639" -p30 "1462" "12ac"
+07:00.4 "0c03" "1022" "1639" -p30 "1462" "12ac"
+07:00.5 "0480" "1022" "15e2" -r01 "1462" "12ac"
+07:00.6 "0403" "1022" "15e3" "1462" "12ac"
+08:00.0 "0106" "1022" "7901" -r81 -p01 "1462" "12ac"
+08:00.1 "0106" "1022" "7901" -r81 -p01 "1462" "12ac"
diff --git a/tests/data/pci.ids b/tests/data/pci.ids
new file mode 100644
index 00000000..acaccbb1
--- /dev/null
+++ b/tests/data/pci.ids
@@ -0,0 +1,29 @@
+## Excerpt from /usr/share/hwdata/pci.ids
+
+# Vendors, devices and subsystems. Please keep sorted.
+
+# Syntax:
+# vendor vendor_name
+# device device_name <-- single tab
+# subvendor subdevice subsystem_name <-- two tabs
+
+1002 Advanced Micro Devices, Inc. [AMD/ATI]
+ 1314 Wrestler HDMI Audio
+ 174b 1001 PURE Fusion Mini
+ 1636 Renoir
+ 7340 Navi 14 [Radeon RX 5500/5500M / Pro 5500M]
+
+# List of known device classes, subclasses and programming interfaces
+
+# Syntax:
+# C class class_name
+# subclass subclass_name <-- single tab
+# prog-if prog-if_name <-- two tabs
+
+C 03 Display controller
+ 00 VGA compatible controller
+ 00 VGA controller
+ 01 8514 controller
+ 01 XGA compatible controller
+ 02 3D controller
+ 80 Display controller
diff --git a/tests/data/physical.biosdevname b/tests/data/physical.biosdevname
new file mode 100644
index 00000000..3e0611d7
--- /dev/null
+++ b/tests/data/physical.biosdevname
@@ -0,0 +1,68 @@
+BIOS device: em1
+Kernel name: eth0
+Permanent MAC: EC:F4:BB:C3:AF:A8
+Assigned MAC : EC:F4:BB:C3:AF:A8
+Driver: ixgbe
+Driver version: 5.9.4
+Firmware version: 0x8000095c, 19.5.12
+Bus Info: 0000:01:00.0
+PCI name : 0000:01:00.0
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 1
+SMBIOS Label: Integrated NIC 1
+sysfs Label: NIC1
+Embedded Index: 1
+
+BIOS device: em2
+Kernel name: eth1
+Permanent MAC: EC:F4:BB:C3:AF:AA
+Assigned MAC : EC:F4:BB:C3:AF:AA
+Driver: ixgbe
+Driver version: 5.9.4
+Firmware version: 0x8000095c, 19.5.12
+Bus Info: 0000:01:00.1
+PCI name : 0000:01:00.1
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 2
+SMBIOS Label: Integrated NIC 2
+sysfs Label: NIC2
+Embedded Index: 2
+
+BIOS device: em3_1
+Kernel name: eth2
+Permanent MAC: EC:F4:BB:C3:AF:AC
+Assigned MAC : EC:F4:BB:C3:AF:AC
+Driver: igb
+Driver version: 5.3.5.20
+Firmware version: 1.67, 0x80000fab, 19.5.12
+Bus Info: 0000:07:00.0
+PCI name : 0000:07:00.0
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 3
+SMBIOS Label: Integrated NIC 3
+sysfs Label: NIC3
+VPD Port: 3
+VPD Index: 1
+VPD PCI master: 0000:07:00.0
+
+BIOS device: em4_1
+Kernel name: eth3
+Permanent MAC: EC:F4:BB:C3:AF:AD
+Assigned MAC : EC:F4:BB:C3:AF:AD
+Driver: igb
+Driver version: 5.3.5.20
+Firmware version: 1.67, 0x80000fab, 19.5.12
+Bus Info: 0000:07:00.1
+PCI name : 0000:07:00.1
+PCI Slot : embedded
+SMBIOS Device Type: Ethernet
+SMBIOS Instance: 4
+SMBIOS Label: Integrated NIC 4
+sysfs Label: NIC4
+VPD Port: 4
+VPD Index: 1
+VPD PCI master: 0000:07:00.1
+
diff --git a/tests/data/repo/.treeinfo b/tests/data/repo/.treeinfo
new file mode 100644
index 00000000..e49cb4fc
--- /dev/null
+++ b/tests/data/repo/.treeinfo
@@ -0,0 +1,34 @@
+[platform]
+name = XCP
+version = 3.2.1
+
+[branding]
+name = XCP-ng
+version = 8.2.1
+
+[build]
+number = release/yangtze/master/58
+
+[keys]
+key1 = RPM-GPG-KEY-CH-8
+key2 = RPM-GPG-KEY-CH-8-LCM
+key3 = RPM-GPG-KEY-Platform-V1
+
+[general]
+name = XCP-ng-8.2.1
+family = XCP-ng
+timestamp = 1645700813.00
+variant =
+version = 8.2.1
+packagedir =
+arch = x86_64
+
+[images-x86_64]
+kernel = boot/pxelinux/mboot.c32
+initrd = boot/vmlinuz
+boot.iso = boot/xen.gz
+
+[images-xen]
+kernel = boot/pxelinux/mboot.c32
+initrd = boot/vmlinuz
+
diff --git a/tests/data/repo/XS-PACKAGES b/tests/data/repo/XS-PACKAGES
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/data/repo/XS-REPOSITORY b/tests/data/repo/XS-REPOSITORY
new file mode 100644
index 00000000..8213385d
--- /dev/null
+++ b/tests/data/repo/XS-REPOSITORY
@@ -0,0 +1,14 @@
+
+
+
+ Description
+
+
+
+
diff --git a/tests/data/repo/XS-REPOSITORY-LIST b/tests/data/repo/XS-REPOSITORY-LIST
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/data/repo/boot/isolinux/mboot.c32 b/tests/data/repo/boot/isolinux/mboot.c32
new file mode 100644
index 00000000..3f5b2fe5
Binary files /dev/null and b/tests/data/repo/boot/isolinux/mboot.c32 differ
diff --git a/tests/data/repo/repodata/repomd.xml b/tests/data/repo/repodata/repomd.xml
new file mode 100644
index 00000000..43ef48d8
--- /dev/null
+++ b/tests/data/repo/repodata/repomd.xml
@@ -0,0 +1,55 @@
+
+
+ 1645708616
+
+ f8454f26592a0669368825d673edc18a067d8a434f2d3ceaa03cdb45e72572e8
+ 6f2d69e90edbe7b3eb83654995dc07c6e577d8fc2f9ee7ec7f4f206ed486218b
+
+ 1645708616
+ 208220
+ 1449213
+
+
+ 4e678f6ce4fc197faef2c75645d00c79ee8b70d3210aa2f692fe432ed223c695
+ cea7a040a15876aba2238f3b8910cd48ded102ff0d4976df81903a3bc4ae8586
+
+ 1645708616
+ 265582
+ 2704673
+
+
+ a19f12f68c74459bc9fc7887376408b4c8dc9e84876ebbf3301afec79f110a7b
+ 46a83a4100e33249a95462d7ed54cb71ee4a178db8f2346b83f1ba5f1a5d47dc
+
+ 1645708616
+ 204512
+ 1054253
+
+
+ 34a89554a4214fde5b473ddcf1d9be796801e8e8676cda860a3d032a7e8b10d8
+ 1fc99d5403906556e8fc91d2121816354cac667741f1a04f51988b5cc3e01b98
+
+ 1645708616
+ 419050
+ 1754112
+ 10
+
+
+ f4c416c2cf253741749420300109cf8a046e0cdd27c269e69795a9ecc092669b
+ 1de3d29150b9da80b10c95b913c91369f3b7d03bb2ed57eedf511038e4537c50
+
+ 1645708616
+ 323942
+ 1380352
+ 10
+
+
+ 4a192c51fff556d2f1fdbb8f90b5454558c910a50674e65b7cdfacd5f791fdb5
+ 23dbdc6ce9ece7f1d3ac3d7b31cd61b4019e96ef01903975516fd5e3083de32a
+
+ 1645708616
+ 243295
+ 988160
+ 10
+
+
diff --git a/tests/test_accessor.py b/tests/test_accessor.py
new file mode 100644
index 00000000..d001edb8
--- /dev/null
+++ b/tests/test_accessor.py
@@ -0,0 +1,42 @@
+import unittest
+import hashlib
+from tempfile import NamedTemporaryFile
+
+from parameterized import parameterized_class
+
+import xcp.accessor
+
+@parameterized_class([{"url": "file://tests/data/repo/"},
+ {"url": "https://updates.xcp-ng.org/netinstall/8.2.1"}])
+class TestAccessor(unittest.TestCase):
+ def test_access(self):
+ a = xcp.accessor.createAccessor(self.url, True)
+ a.start()
+ self.assertTrue(a.access('.treeinfo'))
+ self.assertFalse(a.access('no_such_file'))
+ self.assertEqual(a.lastError, 404)
+ a.finish()
+
+ def test_file_binfile(self):
+ BINFILE = "boot/isolinux/mboot.c32"
+ a = xcp.accessor.createAccessor(self.url, True)
+ a.start()
+ self.assertTrue(a.access(BINFILE))
+ inf = a.openAddress(BINFILE)
+ with NamedTemporaryFile("wb") as outf:
+ while True:
+ data = inf.read()
+ if not data: # EOF
+ break
+ outf.write(data)
+ outf.flush()
+ hasher = hashlib.md5()
+ with open(outf.name, "rb") as bincontents:
+ while True:
+ data = bincontents.read()
+ if not data: # EOF
+ break
+ hasher.update(data)
+ csum = hasher.hexdigest()
+ self.assertEqual(csum, "eab52cebc3723863432dc672360f6dac")
+ a.finish()
diff --git a/tests/test_biosdevname.py b/tests/test_biosdevname.py
index 07133bec..75a3a266 100644
--- a/tests/test_biosdevname.py
+++ b/tests/test_biosdevname.py
@@ -1,7 +1,7 @@
-#!/usr/bin/env python
+import unittest
+from mock import patch, Mock
-import unittest, sys, os, os.path as path
-from xcp.net.biosdevname import has_ppn_quirks
+from xcp.net.biosdevname import has_ppn_quirks, all_devices_all_names
class TestQuirks(unittest.TestCase):
@@ -24,6 +24,25 @@ def test_ppn_true(self):
{"SMBIOS Instance": 1}
]))
-
-if __name__ == "__main__":
- sys.exit(unittest.main())
+class TestDeviceNames(unittest.TestCase):
+ def test(self):
+ with patch("xcp.net.biosdevname.Popen") as popen_mock:
+ with open("tests/data/physical.biosdevname") as f:
+ fake_output_1 = f.read()
+ with open("tests/data/all_ethN.biosdevname") as f:
+ fake_output_2 = f.read()
+ communicate_mock = Mock(side_effect=iter([(fake_output_1, ""),
+ (fake_output_2, "")]))
+ popen_mock.return_value.communicate = communicate_mock
+ popen_mock.return_value.returncode = 0
+
+ devices = all_devices_all_names()
+
+ # check after the fact that we mocked the proper calls
+ self.assertEqual(popen_mock.call_count, 2)
+ calls = popen_mock.call_args_list
+ self.assertEqual(calls[0].args[0], ['/sbin/biosdevname', '--policy', 'physical', '-d'])
+ self.assertEqual(calls[1].args[0], ['/sbin/biosdevname', '--policy', 'all_ethN', '-d'])
+
+ self.assertEqual(devices['eth0']['BIOS device'],
+ {'all_ethN': 'eth0', 'physical': 'em1'})
diff --git a/tests/test_bootloader.py b/tests/test_bootloader.py
new file mode 100644
index 00000000..2e5700b5
--- /dev/null
+++ b/tests/test_bootloader.py
@@ -0,0 +1,59 @@
+import unittest
+import os
+import shutil
+import subprocess
+from tempfile import NamedTemporaryFile, mkdtemp
+
+from xcp.bootloader import Bootloader
+
+class TestBootloader(unittest.TestCase):
+ def test_grub2(self):
+ bl = Bootloader.readGrub2("tests/data/grub.cfg")
+ with NamedTemporaryFile("w") as temp:
+ bl.writeGrub2(temp.name)
+ # get a diff
+ proc = subprocess.Popen(["diff", "tests/data/grub.cfg", temp.name],
+ stdout = subprocess.PIPE,
+ universal_newlines=True)
+ for line in proc.stdout:
+ # FIXME: check is entirely ad-hoc, should we have a diff at all ?
+ self.assertRegexpMatches(line, r"^(5a6,13$|>)")
+
+ proc.stdout.close()
+ proc.wait()
+
+class TestLinuxBootloader(unittest.TestCase):
+ def setUp(self):
+ self.tmpdir = mkdtemp(prefix="testbl")
+ bootdir = os.path.join(self.tmpdir, "boot")
+ grubdir = os.path.join(bootdir, "grub")
+ os.makedirs(grubdir)
+ shutil.copyfile("tests/data/grub-linux.cfg", os.path.join(grubdir, "grub.cfg"))
+ with open(os.path.join(bootdir, "vmlinuz-1"), "w"):
+ pass
+ with open(os.path.join(bootdir, "vmlinuz-2"), "w"):
+ pass
+ with open(os.path.join(bootdir, "initrd.img-1"), "w"):
+ pass
+ with open(os.path.join(bootdir, "initrd.img-2"), "w"):
+ pass
+ def tearDown(self):
+ shutil.rmtree(self.tmpdir)
+ def test_grub2_newdefault(self):
+ Bootloader.newDefault("/boot/vmlinuz-2", "/boot/initrd.img-2", root=self.tmpdir)
+
+class TestBootloaderAdHoc(unittest.TestCase):
+ def setUp(self):
+ self.bl = Bootloader.readGrub2("tests/data/grub.cfg")
+
+ def test_grub(self):
+ with NamedTemporaryFile("w", delete=False) as temp:
+ self.bl.writeGrub(temp)
+ bl2 = Bootloader.readGrub(temp.name)
+ os.unlink(temp.name)
+
+ def test_extlinux(self):
+ with NamedTemporaryFile("w", delete=False) as temp:
+ self.bl.writeExtLinux(temp)
+ bl2 = Bootloader.readExtLinux(temp.name)
+ os.unlink(temp.name)
diff --git a/tests/test_cmd.py b/tests/test_cmd.py
new file mode 100644
index 00000000..c6304873
--- /dev/null
+++ b/tests/test_cmd.py
@@ -0,0 +1,54 @@
+import unittest
+from mock import patch, Mock, DEFAULT
+
+from xcp.cmd import OutputCache
+
+class TestCache(unittest.TestCase):
+ def setUp(self):
+ self.c = OutputCache()
+
+ def test_fileContents(self):
+ with patch("xcp.cmd.open") as open_mock:
+ open_mock.return_value.readlines = Mock(return_value=["line1\n", "line2\n"])
+
+ # uncached fileContents
+ data = self.c.fileContents('/tmp/foo')
+ open_mock.assert_called_once_with("/tmp/foo")
+ self.assertEqual(data, "line1\nline2\n")
+
+ # rerun as cached
+ open_mock.reset_mock()
+ data = self.c.fileContents('/tmp/foo')
+ open_mock.assert_not_called()
+ self.assertEqual(data, "line1\nline2\n")
+
+ # rerun after clearing cache
+ open_mock.reset_mock()
+ self.c.clearCache()
+ data = self.c.fileContents('/tmp/foo')
+ open_mock.assert_called_once_with("/tmp/foo")
+ self.assertEqual(data, "line1\nline2\n")
+
+ def test_runCmd(self):
+ output_data = "line1\nline2\n"
+ with patch("xcp.cmd.subprocess.Popen") as popen_mock:
+ # mock Popen .communicate and .returncode for
+ # `output_data`on stdout, nothing on stderr, and exit
+ # value of 42
+ communicate_mock = Mock(return_value=(output_data, ""))
+ popen_mock.return_value.communicate = communicate_mock
+ def communicate_side_effect(_input_text):
+ popen_mock.return_value.returncode = 42
+ return DEFAULT
+ communicate_mock.side_effect = communicate_side_effect
+
+ # uncached runCmd
+ data = self.c.runCmd(['ls', '/tmp'], True)
+ popen_mock.assert_called_once()
+ self.assertEqual(data, (42, output_data))
+
+ # rerun as cached
+ popen_mock.reset_mock()
+ data = self.c.runCmd(['ls', '/tmp'], True)
+ popen_mock.assert_not_called()
+ self.assertEqual(data, (42, output_data))
diff --git a/tests/test_cpio.py b/tests/test_cpio.py
index 33084484..fdb34f40 100644
--- a/tests/test_cpio.py
+++ b/tests/test_cpio.py
@@ -1,29 +1,27 @@
-#!/usr/bin/env python
-
-import unittest, sys, os, os.path as path
+from __future__ import print_function
+from hashlib import md5
+import os
+import sys
+import shutil
+import subprocess
+import unittest
import warnings
-from xcp.cpiofile import CpioFile, CpioInfo
-import subprocess, shutil
-
-try:
- from hashlib import md5
-except:
- from md5 import md5
-
-def writeRandomFile(fn, size, start='', add='a'):
- f = open(fn, 'wb')
- m = md5()
- m.update(start)
- assert(len(add) != 0)
- while size > 0:
- d = m.digest()
- if size < len(d):
- d=d[:size]
- f.write(d)
- size -= len(d)
- m.update(add)
- f.close()
+from xcp.cpiofile import CpioFile, CpioFileCompat, CPIO_PLAIN, CPIO_GZIPPED
+
+def writeRandomFile(fn, size, start=b'', add=b'a'):
+ "Create a pseudo-random reproducible file from seeds `start` amd `add`"
+ with open(fn, 'wb') as f:
+ m = md5()
+ m.update(start)
+ assert add
+ while size > 0:
+ d = m.digest()
+ if size < len(d):
+ d = d[:size]
+ f.write(d)
+ size -= len(d)
+ m.update(add)
def check_call(cmd):
@@ -40,20 +38,27 @@ def setUp(self):
shutil.rmtree('archive', True)
os.mkdir('archive')
writeRandomFile('archive/data', 10491)
- self.md5data = md5(open('archive/data').read()).hexdigest()
- check_call("find archive | cpio -o -H newc > archive.cpio")
+ with open('archive/data', 'rb') as fd:
+ self.md5data = md5(fd.read()).hexdigest()
+ # fixed timestamps for cpio reproducibility
+ os.utime('archive/data', (0, 0))
+ os.utime('archive', (0, 0))
+
+ check_call(
+ "find archive | cpio --reproducible -o -H newc > archive.cpio")
check_call("gzip -c < archive.cpio > archive.cpio.gz")
check_call("bzip2 -c < archive.cpio > archive.cpio.bz2")
try:
- import lzma
- self.doXZ = subprocess.call("xz --check=crc32 --lzma2=dict=1MiB < archive.cpio > archive.cpio.xz", shell=True) == 0
+ import lzma # pylint: disable=unused-variable
+ self.doXZ = subprocess.call("xz --check=crc32 --lzma2=dict=1MiB"
+ " < archive.cpio > archive.cpio.xz", shell=True) == 0
except Exception as ex:
# FIXME will issue warning even if test_xz is not requested
warnings.warn("will not test cpio.xz: %s" % ex)
self.doXZ = False
def tearDown(self):
- check_call("rm -rf archive archive.cpio*")
+ check_call("rm -rf archive archive.cpio* archive2 archive2.cpio*")
# TODO check with file (like 'r:*')
# TODO use cat to check properly for pipes
@@ -66,28 +71,49 @@ def archiveExtract(self, fn, fmt='r|*'):
self.assertEqual(len(data), f.size)
self.assertEqual(self.md5data, md5(data).hexdigest())
found = True
+ arc.close()
self.assertTrue(found)
+ # extract with extractall and compare
+ arc = CpioFile.open(fn, fmt)
+ check_call("rm -rf archive2")
+ os.rename('archive', 'archive2')
+ arc.extractall()
+ check_call("diff -rq archive2 archive")
+ arc.close()
def archiveCreate(self, fn, fmt='w'):
- os.unlink(fn)
+ if os.path.exists(fn):
+ os.unlink(fn)
arc = CpioFile.open(fn, fmt)
f = arc.getcpioinfo('archive/data')
- arc.addfile(f, open('archive/data'))
+ with open('archive/data', 'rb') as fd:
+ arc.addfile(f, fd)
+ # test recursively add "."
+ os.chdir('archive')
+ arc.add(".")
+ os.chdir("..")
# TODO add self crafted file
arc.close()
# special case for XZ, test check type (crc32)
if fmt.endswith('xz'):
- f = open(fn, 'rb')
- f.seek(6)
- self.assertEqual(f.read(2), '\x00\x01')
- f.close()
+ with open(fn, 'rb') as f:
+ # check xz magic
+ self.assertEqual(f.read(6), b"\xfd7zXZ\0")
+ # check stream flags
+ if sys.version_info < (3, 0):
+ expected_flags = b'\x00\x01' # pylzma defaults to CRC32
+ else:
+ expected_flags = b'\x00\x04' # python3 defaults to CRC64
+ self.assertEqual(f.read(2), expected_flags)
self.archiveExtract(fn)
def doArchive(self, fn, fmt=None):
self.archiveExtract(fn)
- self.archiveCreate(fn, fmt is None and 'w' or 'w|%s' % fmt )
- if not fmt is None:
- self.archiveExtract(fn, 'r|%s' % fmt)
+ fn2 = "archive2" + fn[len("archive"):]
+ print("creating %s" % fn2)
+ self.archiveCreate(fn2, fmt is None and 'w' or 'w|%s' % fmt)
+ if fmt is not None:
+ self.archiveExtract(fn2, 'r|%s' % fmt)
def test_plain(self):
self.doArchive('archive.cpio')
@@ -101,8 +127,45 @@ def test_bz2(self):
def test_xz(self):
if not self.doXZ:
raise unittest.SkipTest("lzma package or xz tool not available")
- print 'Running test for XZ'
+ print('Running test for XZ')
self.doArchive('archive.cpio.xz', 'xz')
-if __name__ == "__main__":
- unittest.main()
+ # CpioFileCompat testing
+
+ def archiveExtractCompat(self, fn, comp):
+ arc = CpioFileCompat(fn, mode="r", compression={"": CPIO_PLAIN,
+ "gz": CPIO_GZIPPED}[comp])
+ found = False
+ for f in arc.namelist():
+ info = arc.getinfo(f)
+ if info.isfile():
+ data = arc.read(f)
+ self.assertEqual(len(data), info.size)
+ self.assertEqual(self.md5data, md5(data).hexdigest())
+ found = True
+ arc.close()
+ self.assertTrue(found)
+
+ def archiveCreateCompat(self, fn, comp):
+ if os.path.exists(fn):
+ os.unlink(fn)
+ arc = CpioFileCompat(fn, mode="w", compression={"": CPIO_PLAIN,
+ "gz": CPIO_GZIPPED}[comp])
+ arc.write('archive/data')
+ arc.close()
+ self.archiveExtract(fn)
+
+ def doArchiveCompat(self, fn, fmt):
+ self.archiveExtractCompat(fn, fmt)
+
+ fn2 = "archive2" + fn[len("archive"):]
+ self.archiveCreateCompat(fn2, fmt)
+ self.archiveExtractCompat(fn2, fmt)
+
+ def test_compat_plain(self):
+ self.doArchiveCompat('archive.cpio', '')
+
+ def test_compat_gz(self):
+ # FIXME: this test exhibits "unclosed file" warnings when run
+ # under `-Wd`
+ self.doArchiveCompat('archive.cpio.gz', 'gz')
diff --git a/tests/test_dom0.py b/tests/test_dom0.py
index 2fc1746d..440bb109 100644
--- a/tests/test_dom0.py
+++ b/tests/test_dom0.py
@@ -1,9 +1,7 @@
-#!/usr/bin/env python
-
-import unittest, sys, os
+import unittest
+from mock import patch, Mock
from xcp.dom0 import default_memory, parse_mem, default_vcpus
-from mock import patch, Mock
class TestDom0(unittest.TestCase):
@@ -32,7 +30,7 @@ def mock_version(open_mock, version):
(2*1024, 4*1024, 8*1024), # Above max
]
- with patch("__builtin__.open") as open_mock:
+ with patch("xcp.dom0.open") as open_mock:
for host_gib, dom0_mib, _ in test_values:
mock_version(open_mock, '2.8.0')
expected = dom0_mib * 1024;
@@ -41,7 +39,7 @@ def mock_version(open_mock, version):
open_mock.assert_called_with("/etc/xensource-inventory")
- with patch("__builtin__.open") as open_mock:
+ with patch("xcp.dom0.open") as open_mock:
for host_gib, _, dom0_mib in test_values:
mock_version(open_mock, '2.9.0')
expected = dom0_mib * 1024;
@@ -112,6 +110,3 @@ def test_default_vcpus(self):
for host_pcpus, mem, expected in test_values:
calculated = default_vcpus(host_pcpus, mem)
self.assertEqual(calculated, expected)
-
-if __name__ == "__main__":
- sys.exit(unittest.main())
diff --git a/tests/test_environ.py b/tests/test_environ.py
new file mode 100644
index 00000000..8b0da44e
--- /dev/null
+++ b/tests/test_environ.py
@@ -0,0 +1,8 @@
+import unittest
+import xcp.environ
+
+class TestEnviron(unittest.TestCase):
+ def test_read_inventory(self):
+ inventory = xcp.environ.readInventory(root="tests/data/inventory")
+ self.assertEqual(inventory["COMPANY_PRODUCT_BRAND"], 'XCP-ng')
+ self.assertEqual(inventory["COMPANY_NAME"], 'Open Source')
diff --git a/tests/test_ifrename_dynamic.py b/tests/test_ifrename_dynamic.py
index 2048f762..0948d254 100644
--- a/tests/test_ifrename_dynamic.py
+++ b/tests/test_ifrename_dynamic.py
@@ -1,13 +1,10 @@
-#!/usr/bin/env python
-
-import unittest, sys, os, os.path as path, logging
+from __future__ import unicode_literals
import json
+import logging
+import unittest
from copy import deepcopy
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
+from io import StringIO
from xcp.net.ifrename.dynamic import DynamicRules
from xcp.net.ifrename.macpci import MACPCI
@@ -17,7 +14,7 @@
class TestLoadAndParse(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
def tearDown(self):
@@ -27,7 +24,7 @@ def tearDown(self):
def test_null(self):
- fd = StringIO.StringIO("")
+ fd = StringIO("")
dr = DynamicRules(fd=fd)
self.assertTrue(dr.load_and_parse())
@@ -37,7 +34,7 @@ def test_null(self):
def test_empty(self):
- fd = StringIO.StringIO(
+ fd = StringIO(
'{"lastboot":[],"old":[]}'
)
dr = DynamicRules(fd=fd)
@@ -49,7 +46,7 @@ def test_empty(self):
def test_one_invalid(self):
- fd = StringIO.StringIO(
+ fd = StringIO(
'{"lastboot":[["","",""]],"old":[]}'
)
dr = DynamicRules(fd=fd)
@@ -61,7 +58,7 @@ def test_one_invalid(self):
def test_one_valid_lastboot(self):
- fd = StringIO.StringIO(
+ fd = StringIO(
'{"lastboot":[["01:23:45:67:89:0a","00:10.2","eth2"]],"old":[]}'
)
dr = DynamicRules(fd=fd)
@@ -75,7 +72,7 @@ def test_one_valid_lastboot(self):
def test_one_valid_lastboot2(self):
- fd = StringIO.StringIO(
+ fd = StringIO(
'{"lastboot":[],"old":[["01:23:45:67:89:0a","00:10.2","eth2"]]}'
)
dr = DynamicRules(fd=fd)
@@ -89,7 +86,7 @@ def test_one_valid_lastboot2(self):
class TestGenerate(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
def tearDown(self):
@@ -128,10 +125,10 @@ def test_pci_matching_invert(self):
MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", kname="eth1",
ppn="", label="")])
- self.assertEqual(dr.rules,[
+ self.assertEqual(set(dr.rules), set([
MACPCI("c8:cb:b8:d3:0c:ce", "0000:04:00.0", tname="eth1"),
MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", tname="eth0")
- ])
+ ]))
def test_pci_missing(self):
@@ -150,7 +147,7 @@ def test_pci_missing(self):
class TestSave(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
def tearDown(self):
@@ -163,10 +160,7 @@ def test_one_invalid_lastboot(self):
dr = DynamicRules()
dr.lastboot = [["foo", "bar", "baz"]]
- try:
- json.loads(dr.write(False))
- except Exception:
- self.fail()
+ json.loads(dr.write(False))
def test_one_ibft_lastboot(self):
@@ -175,7 +169,3 @@ def test_one_ibft_lastboot(self):
self.assertEqual(json.loads(dr.write(False)), {'lastboot': [],
'old': []})
-
-
-if __name__ == "__main__":
- sys.exit(unittest.main())
diff --git a/tests/test_ifrename_logic.py b/tests/test_ifrename_logic.py
index 326f8acb..a2715334 100644
--- a/tests/test_ifrename_logic.py
+++ b/tests/test_ifrename_logic.py
@@ -1,12 +1,11 @@
-#!/usr/bin/env python
-
-import unittest, sys, os, os.path as path, logging
+from __future__ import print_function
+from __future__ import unicode_literals
+import logging
+import sys
+import unittest
from copy import deepcopy
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
+from io import StringIO
from xcp.net.ifrename.logic import *
from xcp.logger import LOG, openLog, closeLogs
@@ -34,7 +33,7 @@ def apply_transactions(lst, trans):
class TestSimpleLogic(unittest.TestCase):
def setUp(self):
- self.siobuff = StringIO.StringIO()
+ self.siobuff = StringIO()
openLog(self.siobuff, logging.NOTSET)
def tearDown(self):
@@ -43,15 +42,15 @@ def tearDown(self):
self.siobuff.close()
def debug_state(self, ts):
- print >>sys.stderr, ""
- print >>sys.stderr, self.siobuff.getvalue()
- print >>sys.stderr, ""
+ print("", file=sys.stderr)
+ print(self.siobuff.getvalue(), file=sys.stderr)
+ print("", file=sys.stderr)
if len(ts):
for (s,d) in ts:
- print >>sys.stderr, "'%s' -> '%s'" % (s, d)
+ print("'%s' -> '%s'" % (s, d), file=sys.stderr)
else:
- print >>sys.stderr, "No transactions"
- print >>sys.stderr, ""
+ print("No transactions", file=sys.stderr)
+ print("", file=sys.stderr)
def test_newhw_norules_1eth(self):
@@ -259,7 +258,7 @@ def test_1drule_1eth_already_complete(self):
class TestUseCases(unittest.TestCase):
def setUp(self):
- self.siobuff = StringIO.StringIO()
+ self.siobuff = StringIO()
openLog(self.siobuff, logging.NOTSET)
def tearDown(self):
@@ -268,16 +267,16 @@ def tearDown(self):
self.siobuff.close()
def debug_state(self, ts):
- print >>sys.stderr, ""
- print >>sys.stderr, self.siobuff.getvalue()
- print >>sys.stderr, ""
+ print("", file=sys.stderr)
+ print(self.siobuff.getvalue(), file=sys.stderr)
+ print("", file=sys.stderr)
if len(ts):
- print >>sys.stderr, "Transactions:"
+ print("Transactions:", file=sys.stderr)
for (s,d) in ts:
- print >>sys.stderr, "'%s' -> '%s'" % (s, d)
+ print("'%s' -> '%s'" % (s, d), file=sys.stderr)
else:
- print >>sys.stderr, "No transactions"
- print >>sys.stderr, ""
+ print("No transactions", file=sys.stderr)
+ print("", file=sys.stderr)
def test_usecase1(self):
"""
@@ -519,6 +518,7 @@ def test_ibft_nic_to_ibft(self):
class TestInputSanitisation(unittest.TestCase):
+ # pylint: disable=no-member
def setUp(self):
"""
@@ -533,7 +533,7 @@ def setUp(self):
set to None and a tname set to the 'eth'
"""
- self.siobuff = StringIO.StringIO()
+ self.siobuff = StringIO()
openLog(self.siobuff)
@@ -555,22 +555,8 @@ def tearDown(self):
self.siobuff.close()
- def assertNotRaises(self, excp, fn, *argl, **kwargs):
- """Because unittest.TestCase seems to be missing this functionality"""
- try:
- fn(*argl, **kwargs)
- except excp, e:
- self.fail("function raised %s unexpectedly: %s"
- % (excp, e))
-
def test_srule_eth_unaliased(self):
-
- self.assertNotRaises(StaticRuleError,
- rename,
- [self.s111],
- [],
- [],
- [])
+ rename([self.s111], [], [], [])
def test_srule_eth_alias(self):
"""
@@ -583,8 +569,8 @@ def test_srule_eth_alias(self):
[self.s112, self.s222, self.s331] ]
for i in srule_inputs:
- self.assertRaises(StaticRuleError, rename,
- i, [], [], [])
+ with self.assertRaises(StaticRuleError):
+ rename(i, [], [], [])
def test_srule_mac_alias(self):
"""
@@ -597,8 +583,8 @@ def test_srule_mac_alias(self):
[self.s211, self.s222, self.s133] ]
for i in srule_inputs:
- self.assertRaises(StaticRuleError, rename,
- i, [], [], [])
+ with self.assertRaises(StaticRuleError):
+ rename(i, [], [], [])
def test_curstate_eth_alias(self):
"""
@@ -611,8 +597,8 @@ def test_curstate_eth_alias(self):
[self.c112, self.c222, self.c331] ]
for i in curstate_inputs:
- self.assertRaises(CurrentStateError, rename,
- [], i, [], [])
+ with self.assertRaises(CurrentStateError):
+ rename([], i, [], [])
def test_curstate_mac_alias(self):
"""
@@ -625,8 +611,8 @@ def test_curstate_mac_alias(self):
[self.c211, self.c222, self.c133] ]
for i in curstate_inputs:
- self.assertRaises(CurrentStateError, rename,
- [], i, [], [])
+ with self.assertRaises(CurrentStateError):
+ rename([], i, [], [])
def test_laststate_input(self):
"""
@@ -634,11 +620,10 @@ def test_laststate_input(self):
(xx:xx.x,xx:xx:xx:xx:xx:xx)->eth##
"""
- self.assertRaises(LastStateError, rename,
- [], [], [self.c123], [])
+ with self.assertRaises(LastStateError):
+ rename([], [], [self.c123], [])
- self.assertNotRaises(LastStateError, rename,
- [], [], [self.s123], [])
+ rename([], [], [self.s123], [])
def test_oldstate_input(self):
"""
@@ -646,11 +631,7 @@ def test_oldstate_input(self):
(xx:xx.x,xx:xx:xx:xx:xx:xx)->eth##
"""
- self.assertRaises(OldStateError, rename,
- [], [], [], [self.c123])
-
- self.assertNotRaises(OldStateError, rename,
- [], [], [], [self.s123])
+ with self.assertRaises(OldStateError):
+ rename([], [], [], [self.c123])
-if __name__ == "__main__":
- sys.exit(unittest.main())
+ rename([], [], [], [self.s123])
diff --git a/tests/test_ifrename_static.py b/tests/test_ifrename_static.py
index 8b72b607..674decfe 100644
--- a/tests/test_ifrename_static.py
+++ b/tests/test_ifrename_static.py
@@ -1,12 +1,9 @@
-#!/usr/bin/env python
-
-import unittest, sys, os, os.path as path, logging
+from __future__ import unicode_literals
+import logging
+import unittest
from copy import deepcopy
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
+from io import StringIO
from xcp.net.ifrename.static import StaticRules
from xcp.net.ifrename.macpci import MACPCI
@@ -16,7 +13,7 @@
class TestLoadAndParse(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
def tearDown(self):
@@ -38,7 +35,7 @@ def test_null(self):
def test_empty(self):
- fd = StringIO.StringIO("")
+ fd = StringIO("")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -47,7 +44,7 @@ def test_empty(self):
def test_comment(self):
- fd = StringIO.StringIO("#comment")
+ fd = StringIO("#comment")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -56,7 +53,7 @@ def test_comment(self):
def test_comment_and_empty(self):
- fd = StringIO.StringIO("\n # Another Comment\n ")
+ fd = StringIO("\n # Another Comment\n ")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -65,7 +62,7 @@ def test_comment_and_empty(self):
def test_single_incorrect_mac(self):
- fd = StringIO.StringIO('eth0:mac="foo"')
+ fd = StringIO('eth0:mac="foo"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -74,7 +71,7 @@ def test_single_incorrect_mac(self):
def test_single_mac(self):
- fd = StringIO.StringIO('eth0:mac="AB:CD:EF:AB:CD:EF"')
+ fd = StringIO('eth0:mac="AB:CD:EF:AB:CD:EF"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -83,7 +80,7 @@ def test_single_mac(self):
def test_single_invalid_pci(self):
- fd = StringIO.StringIO('eth0:pci="bar"')
+ fd = StringIO('eth0:pci="bar"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -92,7 +89,7 @@ def test_single_invalid_pci(self):
def test_single_pci(self):
- fd = StringIO.StringIO('eth0:pci="0000:00:00.1"')
+ fd = StringIO('eth0:pci="0000:00:00.1"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -101,7 +98,7 @@ def test_single_pci(self):
def test_single_pci_0index(self):
- fd = StringIO.StringIO('eth0:pci="0000:00:00.1[0]"')
+ fd = StringIO('eth0:pci="0000:00:00.1[0]"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -110,7 +107,7 @@ def test_single_pci_0index(self):
def test_single_invalid_ppn(self):
- fd = StringIO.StringIO('eth0:ppn="baz"')
+ fd = StringIO('eth0:ppn="baz"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -119,7 +116,7 @@ def test_single_invalid_ppn(self):
def test_single_ppn_embedded(self):
- fd = StringIO.StringIO('eth0:ppn="em2"')
+ fd = StringIO('eth0:ppn="em2"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -128,7 +125,7 @@ def test_single_ppn_embedded(self):
def test_single_ppn_slot(self):
- fd = StringIO.StringIO('eth0:ppn="p2p3"')
+ fd = StringIO('eth0:ppn="p2p3"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -137,7 +134,7 @@ def test_single_ppn_slot(self):
def test_single_oldsytle_ppn_slot(self):
# CA-82901 - Accept old-style PPNs but translate them to new-style
- fd = StringIO.StringIO('eth0:ppn="pci2p3"')
+ fd = StringIO('eth0:ppn="pci2p3"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -146,7 +143,7 @@ def test_single_oldsytle_ppn_slot(self):
def test_single_label(self):
- fd = StringIO.StringIO('eth0:label="somestring"')
+ fd = StringIO('eth0:label="somestring"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -156,7 +153,7 @@ def test_single_label(self):
class TestLoadAndParseGuess(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
def tearDown(self):
@@ -166,7 +163,7 @@ def tearDown(self):
def test_single_explicit_label(self):
- fd = StringIO.StringIO("eth0=\"foo\"")
+ fd = StringIO("eth0=\"foo\"")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -175,7 +172,7 @@ def test_single_explicit_label(self):
def test_single_implicit_label(self):
- fd = StringIO.StringIO("eth0=foo")
+ fd = StringIO("eth0=foo")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -184,7 +181,7 @@ def test_single_implicit_label(self):
def test_single_mac(self):
- fd = StringIO.StringIO("eth0=00:00:00:00:00:00")
+ fd = StringIO("eth0=00:00:00:00:00:00")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -193,7 +190,7 @@ def test_single_mac(self):
def test_single_pci(self):
- fd = StringIO.StringIO("eth0=0000:00:00.0")
+ fd = StringIO("eth0=0000:00:00.0")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -202,7 +199,7 @@ def test_single_pci(self):
def test_single_pci_index(self):
- fd = StringIO.StringIO("eth0=0000:00:00.0[1]")
+ fd = StringIO("eth0=0000:00:00.0[1]")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -211,7 +208,7 @@ def test_single_pci_index(self):
def test_single_ppn_embedded(self):
- fd = StringIO.StringIO("eth0=em4")
+ fd = StringIO("eth0=em4")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -220,7 +217,7 @@ def test_single_ppn_embedded(self):
def test_single_ppn_slot(self):
- fd = StringIO.StringIO("eth0=p1p2")
+ fd = StringIO("eth0=p1p2")
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -231,7 +228,7 @@ def test_single_ppn_slot(self):
class TestGenerate(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
self.state = [
@@ -252,7 +249,7 @@ def tearDown(self):
def test_null(self):
- fd = StringIO.StringIO('eth0:label="somestring"')
+ fd = StringIO('eth0:label="somestring"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
sr.generate([])
@@ -261,7 +258,7 @@ def test_null(self):
def test_single_not_matching_state(self):
- fd = StringIO.StringIO('eth0:label="somestring"')
+ fd = StringIO('eth0:label="somestring"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
sr.generate(self.state)
@@ -270,7 +267,7 @@ def test_single_not_matching_state(self):
def test_single_mac_matching(self):
- fd = StringIO.StringIO('eth0:mac="01:23:45:67:89:0a"')
+ fd = StringIO('eth0:mac="01:23:45:67:89:0a"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -282,7 +279,7 @@ def test_single_mac_matching(self):
def test_single_pci_matching(self):
- fd = StringIO.StringIO('eth0:pci="0000:00:10.0"')
+ fd = StringIO('eth0:pci="0000:00:10.0"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -294,7 +291,7 @@ def test_single_pci_matching(self):
def test_single_ppn_embedded_matching(self):
- fd = StringIO.StringIO('eth0:ppn="em1"')
+ fd = StringIO('eth0:ppn="em1"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -306,7 +303,7 @@ def test_single_ppn_embedded_matching(self):
def test_single_ppn_slot_matching(self):
- fd = StringIO.StringIO('eth0:ppn="p2p2"')
+ fd = StringIO('eth0:ppn="p2p2"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -318,7 +315,7 @@ def test_single_ppn_slot_matching(self):
def test_single_label_matching(self):
- fd = StringIO.StringIO('eth0:label="Ethernet1"')
+ fd = StringIO('eth0:label="Ethernet1"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -331,8 +328,8 @@ def test_single_label_matching(self):
def test_ppn_quirks(self):
# Test case taken from example on CA-75599
- fd = StringIO.StringIO('eth0:ppn="em1"\n'
- 'eth1:ppn="em2"')
+ fd = StringIO('eth0:ppn="em1"\n'
+ 'eth1:ppn="em2"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -351,7 +348,7 @@ def test_ppn_quirks(self):
class TestMultiplePCI(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
self.state = [
MACPCI("c8:cb:b8:d3:0c:ca", "0000:03:00.0", kname="eth0",
@@ -371,50 +368,50 @@ def tearDown(self):
def test_pci_matching(self):
- fd = StringIO.StringIO('eth0:pci="0000:04:00.0"\n'
- 'eth1:pci="0000:04:00.0[1]"')
+ fd = StringIO('eth0:pci="0000:04:00.0"\n'
+ 'eth1:pci="0000:04:00.0[1]"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
sr.generate(self.state)
- self.assertEqual(sr.rules,[
- MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", tname="eth1"),
- MACPCI("c8:cb:b8:d3:0c:ce", "0000:04:00.0", tname="eth0")
- ])
+ self.assertEqual(set(sr.rules), set([
+ MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", tname="eth1"),
+ MACPCI("c8:cb:b8:d3:0c:ce", "0000:04:00.0", tname="eth0")
+ ]))
def test_pci_matching_invert(self):
- fd = StringIO.StringIO('eth0:pci="0000:04:00.0[1]"\n'
- 'eth1:pci="0000:04:00.0[0]"')
+ fd = StringIO('eth0:pci="0000:04:00.0[1]"\n'
+ 'eth1:pci="0000:04:00.0[0]"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
sr.generate(self.state)
- self.assertEqual(sr.rules,[
- MACPCI("c8:cb:b8:d3:0c:ce", "0000:04:00.0", tname="eth1"),
- MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", tname="eth0")
- ])
+ self.assertEqual(set(sr.rules), set([
+ MACPCI("c8:cb:b8:d3:0c:ce", "0000:04:00.0", tname="eth1"),
+ MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", tname="eth0")
+ ]))
def test_pci_matching_mixed(self):
- fd = StringIO.StringIO('eth0:ppn="em3"\n'
- 'eth1:pci="0000:04:00.0[1]"')
+ fd = StringIO('eth0:ppn="em3"\n'
+ 'eth1:pci="0000:04:00.0[1]"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
sr.generate(self.state)
- self.assertEqual(sr.rules,[
- MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", tname="eth0"),
- MACPCI("c8:cb:b8:d3:0c:ce", "0000:04:00.0", tname="eth1")
- ])
+ self.assertEqual(set(sr.rules), set([
+ MACPCI("c8:cb:b8:d3:0c:cf", "0000:04:00.0", tname="eth0"),
+ MACPCI("c8:cb:b8:d3:0c:ce", "0000:04:00.0", tname="eth1")
+ ]))
def test_pci_missing(self):
- fd = StringIO.StringIO('eth0:pci="0000:03:00.0"\n'
- 'eth4:pci="0000:05:00.0"')
+ fd = StringIO('eth0:pci="0000:03:00.0"\n'
+ 'eth4:pci="0000:05:00.0"')
sr = StaticRules(fd = fd)
self.assertTrue(sr.load_and_parse())
@@ -428,7 +425,7 @@ def test_pci_missing(self):
class TestSave(unittest.TestCase):
def setUp(self):
- self.logbuf = StringIO.StringIO()
+ self.logbuf = StringIO()
openLog(self.logbuf, logging.NOTSET)
def tearDown(self):
@@ -473,7 +470,3 @@ def test_two_valid(self):
)
self.assertEqual(sr.write(False), desired_result)
-
-
-if __name__ == "__main__":
- sys.exit(unittest.main())
diff --git a/tests/test_mac.py b/tests/test_mac.py
index 77ef2e79..7976a3bf 100644
--- a/tests/test_mac.py
+++ b/tests/test_mac.py
@@ -1,6 +1,4 @@
-#!/usr/bin/env python
-
-import unittest, sys, os, os.path as path
+import unittest
from xcp.net.mac import MAC
@@ -13,18 +11,21 @@ class TestInvalidMAC(unittest.TestCase):
def test_null_str(self):
val = ""
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_non_str(self):
for val in [None, [], {}]:
- self.assertRaises(TypeError, MAC, val)
+ with self.assertRaises(TypeError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_colon_too_few_octets(self):
val = "00:00:00:00:00"
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_colon_invalid_octets(self):
@@ -34,27 +35,32 @@ def test_colon_invalid_octets(self):
"12:34:56g:78:90:ab"
"12:34::78:90:ab"
]:
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_colon_too_many_octets(self):
val = "00:00:00:00:00:00:00"
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_dash_too_few_octetes(self):
val = "00-00-00-00-00"
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_dash_too_many_octets(self):
val = "00-00-00-00-00-00-00"
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_dotquad_too_few_quads(self):
val = "0000.0000"
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_dotquad_invalid_quads(self):
@@ -64,12 +70,14 @@ def test_dotquad_invalid_quads(self):
"abcd.efgh.ijkl",
"1234.-5678.90Ab"
]:
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
def test_dotquad_too_many_quads(self):
val = "0000.0000.0000.0000"
- self.assertRaises(ValueError, MAC, val)
+ with self.assertRaises(ValueError):
+ MAC(val)
self.assertFalse(MAC.is_valid(val))
class TestValidMAC(unittest.TestCase):
@@ -235,6 +243,3 @@ def test_keys(self):
self.assertEqual(d[m1], "zero")
self.assertEqual(d[m2], "ascending")
self.assertEqual(d[m3], "random")
-
-if __name__ == "__main__":
- sys.exit(unittest.main())
diff --git a/tests/test_pci.py b/tests/test_pci.py
index 65fcd796..1895e88a 100644
--- a/tests/test_pci.py
+++ b/tests/test_pci.py
@@ -1,18 +1,20 @@
-#!/usr/bin/env python
+import subprocess
+import unittest
+from mock import patch, Mock
-import unittest, sys, os, os.path as path
-
-from xcp.pci import PCI
+from xcp.pci import PCI, PCIIds, PCIDevices
class TestInvalid(unittest.TestCase):
def test_invalid_types(self):
-
- self.assertRaises(TypeError, PCI, 0)
- self.assertRaises(TypeError, PCI, 0L)
- self.assertRaises(TypeError, PCI, (0,))
- self.assertRaises(TypeError, PCI, [])
- self.assertRaises(TypeError, PCI, {})
+ with self.assertRaises(TypeError):
+ PCI(0)
+ with self.assertRaises(TypeError):
+ PCI((0,))
+ with self.assertRaises(TypeError):
+ PCI([])
+ with self.assertRaises(TypeError):
+ PCI({})
def test_invalid_format(self):
pass
@@ -56,5 +58,49 @@ def test_equality(self):
self.assertEqual(PCI("0000:00:00.0"), PCI("00:00.0"))
-if __name__ == "__main__":
- sys.exit(unittest.main())
+class TestPCIIds(unittest.TestCase):
+ def tests_nodb(self):
+ with patch("xcp.pci.os.path.exists") as exists_mock:
+ exists_mock.return_value = False
+ with self.assertRaises(Exception):
+ PCIIds.read()
+ exists_mock.assert_called_once_with("/usr/share/hwdata/pci.ids")
+
+ def tests_videoclass(self):
+ with patch("xcp.pci.os.path.exists") as exists_mock, \
+ patch("xcp.pci.open") as open_mock, \
+ open("tests/data/pci.ids") as fake_data:
+ exists_mock.return_value = True
+ open_mock.return_value.__iter__ = Mock(return_value=iter(fake_data))
+ ids = PCIIds.read()
+ exists_mock.assert_called_once_with("/usr/share/hwdata/pci.ids")
+ open_mock.assert_called_once_with("/usr/share/hwdata/pci.ids")
+ video_class = ids.lookupClass('Display controller')
+ self.assertEqual(video_class, ['03'])
+
+ with patch("xcp.pci.subprocess.Popen") as popen_mock, \
+ open("tests/data/lspci-mn") as fake_data:
+ popen_mock.return_value.stdout.__iter__ = Mock(return_value=iter(fake_data))
+ devs = PCIDevices()
+ popen_mock.assert_called_once_with(['lspci', '-mn'], bufsize = 1,
+ stdout = subprocess.PIPE)
+ sorted_devices = sorted(devs.findByClass(video_class),
+ key=lambda x: x['id'])
+ self.assertEqual(len(sorted_devices), 2)
+
+ for (video_dev,
+ num_functions,
+ vendor,
+ device,
+ ) in zip(sorted_devices,
+ (1, 5),
+ ("Advanced Micro Devices, Inc. [AMD/ATI]",
+ "Advanced Micro Devices, Inc. [AMD/ATI]"),
+ ("Navi 14 [Radeon RX 5500/5500M / Pro 5500M]",
+ "Renoir"),
+ ):
+ self.assertEqual(len(devs.findRelatedFunctions(video_dev['id'])), num_functions)
+ self.assertEqual(ids.findVendor(video_dev['vendor']), vendor)
+ self.assertEqual(ids.findDevice(video_dev['vendor'], video_dev['device']), device)
+
+ self.assertEqual(len(devs.findRelatedFunctions('00:18.1')), 7)
diff --git a/tests/test_repository.py b/tests/test_repository.py
new file mode 100644
index 00000000..8081c33a
--- /dev/null
+++ b/tests/test_repository.py
@@ -0,0 +1,18 @@
+import unittest
+from parameterized import parameterized_class
+
+import xcp.accessor
+from xcp import repository
+from xcp.version import Version
+
+@parameterized_class([{"url": "file://tests/data/repo/"},
+ {"url": "https://updates.xcp-ng.org/netinstall/8.2.1"}])
+class TestRepository(unittest.TestCase):
+ def test_basicinfo(self):
+ a = xcp.accessor.createAccessor(self.url, True)
+ repo_ver = repository.BaseRepository.getRepoVer(a)
+ self.assertEqual(repo_ver, Version([3, 2, 1]))
+ product_ver = repository.BaseRepository.getProductVersion(a)
+ self.assertEqual(product_ver, Version([8, 2, 1]))
+ repos = repository.BaseRepository.findRepositories(a)
+ self.assertEqual(len(repos), 1)
diff --git a/tests/test_xmlunwrap.py b/tests/test_xmlunwrap.py
new file mode 100644
index 00000000..23313331
--- /dev/null
+++ b/tests/test_xmlunwrap.py
@@ -0,0 +1,37 @@
+import unittest
+import xml.dom.minidom
+
+from xcp.xmlunwrap import (getElementsByTagName, getText, getMapAttribute,
+ getStrAttribute, XmlUnwrapError)
+
+class TestXmlUnwrap(unittest.TestCase):
+ def setUp(self):
+ a_text = """
+ text1
+ text2
+ """
+ xmldoc = xml.dom.minidom.parseString(a_text)
+ self.top_el = xmldoc.documentElement
+
+ def test(self):
+ self.assertEqual(self.top_el.tagName, "installation")
+
+ self.assertEqual([getText(el)
+ for el in getElementsByTagName(self.top_el, ["fred"])],
+ [b"text1", b"text2"])
+
+ x = getMapAttribute(self.top_el, ["mode"], [(b'test', 42), (b'stuff', 77)])
+ self.assertEqual(x, 42)
+ x = getMapAttribute(self.top_el, ["made"], [(b'test', 42), (b'stuff', 77)],
+ default=b'stuff')
+ self.assertEqual(x, 77)
+
+ x = getStrAttribute(self.top_el, ["mode"])
+ self.assertEqual(x, b"test")
+ x = getStrAttribute(self.top_el, ["made"])
+ self.assertEqual(x, b"")
+ x = getStrAttribute(self.top_el, ["made"], None)
+ self.assertEqual(x, None)
+
+ with self.assertRaises(XmlUnwrapError):
+ x = getStrAttribute(self.top_el, ["made"], mandatory=True)
diff --git a/xcp/accessor.py b/xcp/accessor.py
index 22f9e24f..6d057927 100644
--- a/xcp/accessor.py
+++ b/xcp/accessor.py
@@ -25,13 +25,16 @@
"""accessor - provide common interface to access methods"""
+# pylint: disable=wrong-import-position,wrong-import-order
+from future import standard_library
+standard_library.install_aliases()
+
import ftplib
import os
import tempfile
-import types
-import urllib
-import urllib2
-import urlparse
+import urllib.request # pylint: disable=import-error
+import urllib.error # pylint: disable=import-error
+import urllib.parse # pylint: disable=import-error
import errno
import xcp.mount as mount
@@ -68,7 +71,7 @@ def access(self, name):
return True
- def openAddress(self, name):
+ def openAddress(self, address):
"""should be overloaded"""
pass
@@ -96,9 +99,9 @@ def __init__(self, location, ro):
super(FilesystemAccessor, self).__init__(ro)
self.location = location
- def openAddress(self, addr):
+ def openAddress(self, address):
try:
- file = open(os.path.join(self.location, addr), 'r')
+ filehandle = open(os.path.join(self.location, address), 'r')
except OSError as e:
if e.errno == errno.EIO:
self.lastError = 5
@@ -114,11 +117,11 @@ def openAddress(self, addr):
except Exception as e:
self.lastError = 500
return False
- return file
+ return filehandle
class MountingAccessor(FilesystemAccessor):
- def __init__(self, mount_types, mount_source, mount_options = None):
- ro = isinstance(mount_options, types.ListType) and 'ro' in mount_options
+ def __init__(self, mount_types, mount_source, mount_options=None):
+ ro = isinstance(mount_options, list) and 'ro' in mount_options
super(MountingAccessor, self).__init__(None, ro)
self.mount_types = mount_types
@@ -135,7 +138,7 @@ def start(self):
try:
opts = self.mount_options
if fs == 'iso9660':
- if isinstance(opts, types.ListType):
+ if isinstance(opts, list):
if 'ro' not in opts:
opts.append('ro')
else:
@@ -251,14 +254,14 @@ def rebuild_url(url_parts):
host = url_parts.hostname
if url_parts.port:
host += ':' + str(url_parts.port)
- return urlparse.urlunsplit(
+ return urllib.parse.urlunsplit(
(url_parts.scheme, host,
url_parts.path, '', ''))
class FTPAccessor(Accessor):
def __init__(self, baseAddress, ro):
super(FTPAccessor, self).__init__(ro)
- self.url_parts = urlparse.urlsplit(baseAddress, allow_fragments=False)
+ self.url_parts = urllib.parse.urlsplit(baseAddress, allow_fragments=False)
self.start_count = 0
self.cleanup = False
self.ftp = None
@@ -281,12 +284,12 @@ def start(self):
username = self.url_parts.username
password = self.url_parts.password
if username:
- username = urllib.unquote(username)
+ username = urllib.parse.unquote(username)
if password:
- password = urllib.unquote(password)
+ password = urllib.parse.unquote(password)
self.ftp.login(username, password)
- directory = urllib.unquote(self.url_parts.path[1:])
+ directory = urllib.parse.unquote(self.url_parts.path[1:])
if directory != '':
logger.debug("Changing to " + directory)
self.ftp.cwd(directory)
@@ -306,12 +309,12 @@ def access(self, path):
try:
logger.debug("Testing "+path)
self._cleanup()
- url = urllib.unquote(path)
+ url = urllib.parse.unquote(path)
if self.ftp.size(url) is not None:
return True
lst = self.ftp.nlst(os.path.dirname(url))
- return os.path.basename(url) in map(os.path.basename, lst)
+ return os.path.basename(url) in list(map(os.path.basename, lst))
except IOError as e:
if e.errno == errno.EIO:
self.lastError = 5
@@ -331,7 +334,7 @@ def access(self, path):
def openAddress(self, address):
logger.debug("Opening "+address)
self._cleanup()
- url = urllib.unquote(address)
+ url = urllib.parse.unquote(address)
self.ftp.voidcmd('TYPE I')
s = self.ftp.transfercmd('RETR ' + url).makefile('rb')
@@ -340,7 +343,7 @@ def openAddress(self, address):
def writeFile(self, in_fh, out_name):
self._cleanup()
- fname = urllib.unquote(out_name)
+ fname = urllib.parse.unquote(out_name)
logger.debug("Storing as " + fname)
self.ftp.storbinary('STOR ' + fname, in_fh)
@@ -352,28 +355,28 @@ class HTTPAccessor(Accessor):
def __init__(self, baseAddress, ro):
assert ro
super(HTTPAccessor, self).__init__(ro)
- self.url_parts = urlparse.urlsplit(baseAddress, allow_fragments=False)
+ self.url_parts = urllib.parse.urlsplit(baseAddress, allow_fragments=False)
if self.url_parts.username:
username = self.url_parts.username
if username is not None:
- username = urllib.unquote(self.url_parts.username)
+ username = urllib.parse.unquote(self.url_parts.username)
password = self.url_parts.password
if password is not None:
- password = urllib.unquote(self.url_parts.password)
- self.passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
+ password = urllib.parse.unquote(self.url_parts.password)
+ self.passman = urllib.request.HTTPPasswordMgrWithDefaultRealm()
self.passman.add_password(None, self.url_parts.hostname,
username, password)
- self.authhandler = urllib2.HTTPBasicAuthHandler(self.passman)
- self.opener = urllib2.build_opener(self.authhandler)
- urllib2.install_opener(self.opener)
+ self.authhandler = urllib.request.HTTPBasicAuthHandler(self.passman)
+ self.opener = urllib.request.build_opener(self.authhandler)
+ urllib.request.install_opener(self.opener)
self.baseAddress = rebuild_url(self.url_parts)
def openAddress(self, address):
try:
- urlFile = urllib2.urlopen(os.path.join(self.baseAddress, address))
- except urllib2.HTTPError as e:
+ urlFile = urllib.request.urlopen(os.path.join(self.baseAddress, address))
+ except urllib.error.HTTPError as e:
self.lastError = e.code
return False
return urlFile
@@ -390,7 +393,7 @@ def __repr__(self):
}
def createAccessor(baseAddress, *args):
- url_parts = urlparse.urlsplit(baseAddress, allow_fragments=False)
+ url_parts = urllib.parse.urlsplit(baseAddress, allow_fragments=False)
- assert url_parts.scheme in SUPPORTED_ACCESSORS.keys()
+ assert url_parts.scheme in SUPPORTED_ACCESSORS
return SUPPORTED_ACCESSORS[url_parts.scheme](baseAddress, *args)
diff --git a/xcp/bootloader.py b/xcp/bootloader.py
index e1af85f6..81a61bd4 100644
--- a/xcp/bootloader.py
+++ b/xcp/bootloader.py
@@ -23,6 +23,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from __future__ import print_function
+from __future__ import division
import os
import os.path
import re
@@ -144,13 +146,12 @@ def readExtLinux(cls, src_file):
title = l[1:].lstrip()
elif keywrd == 'kernel' and len(els) > 1:
kernel = els[1]
- elif keywrd == 'append' and len(els) > 1 and \
- kernel == 'mboot.c32':
+ elif keywrd == 'append' and len(els) > 1 and kernel == 'mboot.c32':
if 'tboot' in els[1]:
# els[2] contains tboot args, hypervisor,
# hypervisor args, kernel,
# kernel args & initrd
- args = map(lambda x: x.strip(), els[2].split('---'))
+ args = [x.strip() for x in els[2].split('---')]
if len(args) == 4:
hypervisor = args[1].split(None, 1)
kernel = args[2].split(None, 1)
@@ -166,7 +167,7 @@ def readExtLinux(cls, src_file):
elif 'xen' in els[1]:
# els[2] contains hypervisor args, kernel,
# kernel args & initrd
- args = map(lambda x: x.strip(), els[2].split('---'))
+ args = [x.strip() for x in els[2].split('---')]
if len(args) == 3:
kernel = args[1].split(None, 1)
if len(kernel) == 2:
@@ -335,19 +336,19 @@ def create_label(title):
try:
for line in fh:
l = line.strip()
- menu_match = re.match("menuentry ['\"]([^']*)['\"](.*){", l)
+ menu_match = re.match(r"menuentry ['\"]([^']*)['\"](.*){", l)
# Only parse unindented default and timeout lines to prevent
# changing these lines in if statements.
if l.startswith('set default=') and l == line.rstrip():
default = l.split('=')[1]
- match = re.match("['\"](.*)['\"]$", default)
+ match = re.match(r"['\"](.*)['\"]$", default)
if match:
default = match.group(1)
elif l.startswith('set timeout=') and l == line.rstrip():
timeout = int(l.split('=')[1]) * 10
elif l.startswith('serial'):
- match = re.match("serial --unit=(\d+) --speed=(\d+)", l)
+ match = re.match(r"serial --unit=(\d+) --speed=(\d+)", l)
if match:
serial = {
'port': int(match.group(1)),
@@ -431,7 +432,7 @@ def create_label(title):
# If this fails, it is probably a string, so leave it unchanged.
try:
default = menu_order[int(default)]
- except ValueError, KeyError:
+ except (ValueError, KeyError):
pass
finally:
fh.close()
@@ -455,47 +456,47 @@ def loadExisting(cls, root = '/'):
elif os.path.exists(os.path.join(root, "boot/grub/menu.lst")):
return cls.readGrub(os.path.join(root, "boot/grub/menu.lst"))
else:
- raise RuntimeError, "No existing bootloader configuration found"
+ raise RuntimeError("No existing bootloader configuration found")
def writeExtLinux(self, dst_file = None):
if hasattr(dst_file, 'name'):
fh = dst_file
else:
fh = open(dst_file, 'w')
- print >> fh, "# location " + self.location
+ print("# location " + self.location, file=fh)
if self.serial:
if self.serial.get('flow', None) is None:
- print >> fh, "serial %d %d" % (self.serial['port'],
- self.serial['baud'])
+ print("serial %s %s" % (self.serial['port'],
+ self.serial['baud']), file=fh)
else:
- print >> fh, "serial %d %d %s" % (self.serial['port'],
- self.serial['baud'],
- self.serial['flow'])
+ print("serial %s %s %s" % (self.serial['port'],
+ self.serial['baud'],
+ self.serial['flow']), file=fh)
if self.default:
- print >> fh, "default " + self.default
- print >> fh, "prompt 1"
+ print("default " + self.default, file=fh)
+ print("prompt 1", file=fh)
if self.timeout:
- print >> fh, "timeout %d" % self.timeout
+ print("timeout %d" % self.timeout, file=fh)
for label in self.menu_order:
- print >> fh, "\nlabel " + label
+ print("\nlabel " + label, file=fh)
m = self.menu[label]
if m.title:
- print >> fh, " # " + m.title
+ print(" # " + m.title, file=fh)
if m.tboot:
- print >> fh, " kernel mboot.c32"
- print >> fh, " append %s %s --- %s %s --- %s %s --- %s" % \
- (m.tboot, m.tboot_args, m.hypervisor, m.hypervisor_args,
- m.kernel, m.kernel_args, m.initrd)
+ print(" kernel mboot.c32", file=fh)
+ print(" append %s %s --- %s %s --- %s %s --- %s" %
+ (m.tboot, m.tboot_args, m.hypervisor, m.hypervisor_args,
+ m.kernel, m.kernel_args, m.initrd), file=fh)
elif m.hypervisor:
- print >> fh, " kernel mboot.c32"
- print >> fh, " append %s %s --- %s %s --- %s" % \
- (m.hypervisor, m.hypervisor_args, m.kernel, m.kernel_args, m.initrd)
+ print(" kernel mboot.c32", file=fh)
+ print(" append %s %s --- %s %s --- %s" %
+ (m.hypervisor, m.hypervisor_args, m.kernel, m.kernel_args, m.initrd), file=fh)
else:
- print >> fh, " kernel " + m.kernel
- print >> fh, " append " + m.kernel_args
- print >> fh, " initrd " + m.initrd
+ print(" kernel " + m.kernel, file=fh)
+ print(" append " + m.kernel_args, file=fh)
+ print(" initrd " + m.initrd, file=fh)
if not hasattr(dst_file, 'name'):
fh.close()
@@ -504,32 +505,32 @@ def writeGrub(self, dst_file = None):
fh = dst_file
else:
fh = open(dst_file, 'w')
- print >> fh, "# location " + self.location
+ print("# location " + self.location, file=fh)
if self.serial:
- print >> fh, "serial --unit=%d --speed=%s" % (self.serial['port'],
- self.serial['baud'])
- print >> fh, "terminal --timeout=10 console serial"
+ print("serial --unit=%s --speed=%s" %
+ (self.serial['port'], self.serial['baud']), file=fh)
+ print("terminal --timeout=10 console serial", file=fh)
else:
- print >> fh, "terminal console"
+ print("terminal console", file=fh)
if self.default:
for i in range(len(self.menu_order)):
if self.menu_order[i] == self.default:
- print >> fh, "default %d" % i
+ print("default %d" % i, file=fh)
break
if self.timeout:
- print >> fh, "timeout %d" % (self.timeout / 10)
+ print("timeout %d" % (self.timeout // 10), file=fh)
for label in self.menu_order:
m = self.menu[label]
- print >> fh, "\ntitle " + m.title
+ print("\ntitle " + m.title, file=fh)
if m.hypervisor:
- print >> fh, " kernel " + m.hypervisor + " " + m.hypervisor_args
- print >> fh, " module " + m.kernel + " " + m.kernel_args
- print >> fh, " module " + m.initrd
+ print(" kernel " + m.hypervisor + " " + m.hypervisor_args, file=fh)
+ print(" module " + m.kernel + " " + m.kernel_args, file=fh)
+ print(" module " + m.initrd, file=fh)
else:
- print >> fh, " kernel " + m.kernel + " " + m.kernel_args
- print >> fh, " initrd " + m.initrd
+ print(" kernel " + m.kernel + " " + m.kernel_args, file=fh)
+ print(" initrd " + m.initrd, file=fh)
if not hasattr(dst_file, 'name'):
fh.close()
@@ -540,19 +541,19 @@ def writeGrub2(self, dst_file = None):
fh = open(dst_file, 'w')
if self.serial:
- print >> fh, "serial --unit=%d --speed=%s" % (self.serial['port'],
- self.serial['baud'])
- print >> fh, "terminal_input serial console"
- print >> fh, "terminal_output serial console"
+ print("serial --unit=%s --speed=%s" % (self.serial['port'],
+ self.serial['baud']), file=fh)
+ print("terminal_input serial console", file=fh)
+ print("terminal_output serial console", file=fh)
if self.default:
for i in range(len(self.menu_order)):
if self.menu_order[i] == self.default:
- print >> fh, "set default=%d" % i
+ print("set default=%d" % i, file=fh)
break
else:
- print >> fh, "set default='%s'" % str(self.default)
+ print("set default='%s'" % str(self.default), file=fh)
if self.timeout:
- print >> fh, "set timeout=%d" % (self.timeout / 10)
+ print("set timeout=%d" % (self.timeout // 10), file=fh)
boilerplate = getattr(self, 'boilerplate', [])[:]
boilerplate.reverse()
@@ -563,41 +564,41 @@ def writeGrub2(self, dst_file = None):
if boilerplate:
text = boilerplate.pop()
if text:
- print >> fh, "\n".join(text)
+ print("\n".join(text), file=fh)
extra = ' '
try:
extra = m.extra
except AttributeError:
pass
- print >> fh, "menuentry '%s'%s{" % (m.title, extra)
+ print("menuentry '%s'%s{" % (m.title, extra), file=fh)
try:
contents = "\n".join(m.contents)
if contents:
- print >> fh, contents
+ print(contents, file=fh)
except AttributeError:
pass
if m.root:
- print >> fh, "\tsearch --label --set root %s" % m.root
+ print("\tsearch --label --set root %s" % m.root, file=fh)
if m.hypervisor:
if m.tboot:
- print >> fh, "\tmultiboot2 %s %s" % (m.tboot, m.tboot_args)
- print >> fh, "\tmodule2 %s %s" % (m.hypervisor, m.hypervisor_args)
+ print("\tmultiboot2 %s %s" % (m.tboot, m.tboot_args), file=fh)
+ print("\tmodule2 %s %s" % (m.hypervisor, m.hypervisor_args), file=fh)
else:
- print >> fh, "\tmultiboot2 %s %s" % (m.hypervisor, m.hypervisor_args)
+ print("\tmultiboot2 %s %s" % (m.hypervisor, m.hypervisor_args), file=fh)
if m.kernel:
- print >> fh, "\tmodule2 %s %s" % (m.kernel, m.kernel_args)
+ print("\tmodule2 %s %s" % (m.kernel, m.kernel_args), file=fh)
if m.initrd:
- print >> fh, "\tmodule2 %s" % m.initrd
+ print("\tmodule2 %s" % m.initrd, file=fh)
else:
if m.kernel:
- print >> fh, "\tlinux %s %s" % (m.kernel, m.kernel_args)
+ print("\tlinux %s %s" % (m.kernel, m.kernel_args), file=fh)
if m.initrd:
- print >> fh, "\tinitrd %s" % m.initrd
- print >> fh, "}"
+ print("\tinitrd %s" % m.initrd, file=fh)
+ print("}", file=fh)
if not hasattr(dst_file, 'name'):
fh.close()
@@ -642,9 +643,9 @@ def newDefault(cls, kernel_link_name, initrd_link_name, root = '/'):
if b.menu[b.default].kernel != kernel_link_name:
backup = []
if not os.path.exists(os.path.join(root, kernel_link_name[1:])):
- raise RuntimeError, "kernel symlink not found"
+ raise RuntimeError("kernel symlink not found")
if not os.path.exists(os.path.join(root, initrd_link_name[1:])):
- raise RuntimeError, "initrd symlink not found"
+ raise RuntimeError("initrd symlink not found")
old_kernel_link = b.menu[b.default].kernel
old_ver = 'old'
m = re.search(r'(-\d+\.\d+)-', old_kernel_link)
diff --git a/xcp/cmd.py b/xcp/cmd.py
index b51eaa8a..16cfb64f 100644
--- a/xcp/cmd.py
+++ b/xcp/cmd.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-
# Copyright (c) 2013, Citrix Inc.
# All rights reserved.
#
@@ -26,15 +24,16 @@
"""Command processing"""
import subprocess
+import six
import xcp.logger as logger
def runCmd(command, with_stdout = False, with_stderr = False, inputtext = None):
- cmd = subprocess.Popen(command, bufsize = 1,
- stdin = (inputtext and subprocess.PIPE or None),
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE,
- shell = isinstance(command, str))
+ cmd = subprocess.Popen(command, bufsize=1,
+ stdin=(inputtext and subprocess.PIPE or None),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=isinstance(command, six.string_types))
(out, err) = cmd.communicate(inputtext)
rv = cmd.returncode
@@ -58,7 +57,7 @@ def runCmd(command, with_stdout = False, with_stderr = False, inputtext = None):
return rv, err
return rv
-class OutputCache:
+class OutputCache(object):
def __init__(self):
self.cache = {}
@@ -89,12 +88,3 @@ def runCmd(self, command, with_stdout = False, with_stderr = False, inputtext =
def clearCache(self):
self.cache.clear()
-
-if __name__ == '__main__':
- c = OutputCache()
- print c.fileContents('/tmp/foo')
- print c.fileContents('/tmp/foo')
- c.clearCache()
- print c.fileContents('/tmp/foo')
- print c.runCmd(['ls', '/tmp'], True)
- print c.runCmd(['ls', '/tmp'], True)
diff --git a/xcp/cpiofile.py b/xcp/cpiofile.py
index d6163849..fb4d96f7 100755
--- a/xcp/cpiofile.py
+++ b/xcp/cpiofile.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
#-------------------------------------------------------------------
# cpiofile.py
@@ -33,10 +33,11 @@
Derived from Lars Gustäbel's tarfile.py
"""
+from __future__ import print_function
__version__ = "0.1"
__author__ = "Simon Rowe"
-__credits__ = "Lars Gustäbel"
+__credits__ = "Lars Gustäbel, Gustavo Niemeyer, Niels Gustäbel, Richard Townsend."
#---------
# Imports
@@ -49,13 +50,16 @@
import time
import struct
import copy
+import io
+
+import six
if sys.platform == 'mac':
# This module needs work for MacOS9, especially in the area of pathname
# handling. In many places it is assumed a simple substitution of / by the
# local os.path.sep is good enough to convert pathnames, but this does not
# work with the mac rooted:path:name versus :nonrooted:path:name syntax
- raise ImportError, "cpiofile does not work for platform==mac"
+ raise ImportError("cpiofile does not work for platform==mac")
try:
import grp as GRP, pwd as PWD
@@ -69,35 +73,35 @@
# cpio constants
#---------------------------------------------------------
MAGIC_NEWC = 0x070701 # magic for SVR4 portable format (no CRC)
-TRAILER_NAME = "TRAILER!!!" # filename in final member
+TRAILER_NAME = b"TRAILER!!!" # filename in final member
WORDSIZE = 4 # pad size
-NUL = "\0" # the null character
+NUL = b"\0" # the null character
BLOCKSIZE = 512 # length of processing blocks
HEADERSIZE_SVR4 = 110 # length of fixed header
#---------------------------------------------------------
# Bits used in the mode field, values in octal.
#---------------------------------------------------------
-S_IFLNK = 0120000 # symbolic link
-S_IFREG = 0100000 # regular file
-S_IFBLK = 0060000 # block device
-S_IFDIR = 0040000 # directory
-S_IFCHR = 0020000 # character device
-S_IFIFO = 0010000 # fifo
-
-TSUID = 04000 # set UID on execution
-TSGID = 02000 # set GID on execution
-TSVTX = 01000 # reserved
-
-TUREAD = 0400 # read by owner
-TUWRITE = 0200 # write by owner
-TUEXEC = 0100 # execute/search by owner
-TGREAD = 0040 # read by group
-TGWRITE = 0020 # write by group
-TGEXEC = 0010 # execute/search by group
-TOREAD = 0004 # read by other
-TOWRITE = 0002 # write by other
-TOEXEC = 0001 # execute/search by other
+S_IFLNK = 0o120000 # symbolic link
+S_IFREG = 0o100000 # regular file
+S_IFBLK = 0o060000 # block device
+S_IFDIR = 0o040000 # directory
+S_IFCHR = 0o020000 # character device
+S_IFIFO = 0o010000 # fifo
+
+TSUID = 0o4000 # set UID on execution
+TSGID = 0o2000 # set GID on execution
+TSVTX = 0o1000 # reserved
+
+TUREAD = 0o400 # read by owner
+TUWRITE = 0o200 # write by owner
+TUEXEC = 0o100 # execute/search by owner
+TGREAD = 0o040 # read by group
+TGWRITE = 0o020 # write by group
+TGEXEC = 0o010 # execute/search by group
+TOREAD = 0o004 # read by other
+TOWRITE = 0o002 # write by other
+TOEXEC = 0o001 # execute/search by other
#---------------------------------------------------------
# Some useful functions
@@ -115,7 +119,7 @@ def copyfileobj(src, dst, length=None):
bufsize = 16 * 1024
blocks, remainder = divmod(length, bufsize)
- for _ in xrange(blocks):
+ for b in range(blocks):
buf = src.read(bufsize)
if len(buf) < bufsize:
raise IOError("end of file reached")
@@ -252,8 +256,8 @@ def __init__(self, name, mode, comptype, fileobj, bufsize):
self.comptype = comptype
self.fileobj = fileobj
self.bufsize = bufsize
- self.buf = ""
- self.pos = 0L
+ self.buf = b""
+ self.pos = 0
self.closed = False
if comptype == "gz":
@@ -262,7 +266,7 @@ def __init__(self, name, mode, comptype, fileobj, bufsize):
except ImportError:
raise CompressionError("zlib module is not available")
self.zlib = zlib
- self.crc = zlib.crc32("")
+ self.crc = zlib.crc32(b"")
if mode == "r":
self._init_read_gz()
else:
@@ -274,7 +278,7 @@ def __init__(self, name, mode, comptype, fileobj, bufsize):
except ImportError:
raise CompressionError("bz2 module is not available")
if mode == "r":
- self.dbuf = ""
+ self.dbuf = b""
self.cmp = bz2.BZ2Decompressor()
else:
self.cmp = bz2.BZ2Compressor()
@@ -285,7 +289,7 @@ def __init__(self, name, mode, comptype, fileobj, bufsize):
except ImportError:
raise CompressionError("lzma module is not available")
if mode == "r":
- self.dbuf = ""
+ self.dbuf = b""
self.cmp = lzma.LZMADecompressor()
else:
self.cmp = lzma.LZMACompressor()
@@ -302,11 +306,11 @@ def _init_write_gz(self):
-self.zlib.MAX_WBITS,
self.zlib.DEF_MEM_LEVEL,
0)
- timestamp = struct.pack("= 0:
blocks, remainder = divmod(pos - self.pos, self.bufsize)
- for _ in xrange(blocks):
+ for i in range(blocks):
self.read(self.bufsize)
self.read(remainder)
else:
@@ -416,7 +420,7 @@ def read(self, size=None):
if not buf:
break
t.append(buf)
- buf = "".join(t)
+ buf = b"".join(t)
else:
buf = self._read(size)
self.pos += len(buf)
@@ -437,7 +441,7 @@ def _read(self, size):
buf = self.cmp.decompress(buf)
t.append(buf)
c += len(buf)
- t = "".join(t)
+ t = b"".join(t)
self.dbuf = t[size:]
return t[:size]
@@ -453,7 +457,7 @@ def __read(self, size):
break
t.append(buf)
c += len(buf)
- t = "".join(t)
+ t = b"".join(t)
self.buf = t[size:]
return t[:size]
# class _Stream
@@ -473,11 +477,11 @@ def read(self, size):
return self.buf
def getcomptype(self):
- if self.buf.startswith("\037\213\010"):
+ if self.buf.startswith(b"\037\213\010"):
return "gz"
- if self.buf.startswith("BZh91"):
+ if self.buf.startswith(b"BZh91"):
return "bz2"
- if self.buf.startswith("\xfd7zXZ"):
+ if self.buf.startswith(b"\xfd7zXZ\0"):
return "xz"
return "cpio"
@@ -507,7 +511,7 @@ def read(self, size):
except EOFError:
break
x += len(data)
- self.buf = "".join(b)
+ self.buf = b"".join(b)
buf = self.buf[:size]
self.buf = self.buf[size:]
@@ -557,7 +561,7 @@ def init(self):
if self.mode == "r":
self.cmpobj = bz2.BZ2Decompressor()
self.fileobj.seek(0)
- self.buf = ""
+ self.buf = b""
else:
self.cmpobj = bz2.BZ2Compressor()
@@ -578,7 +582,7 @@ def init(self):
if self.mode == "r":
self.cmpobj = lzma.BZ2Decompressor()
self.fileobj.seek(0)
- self.buf = ""
+ self.buf = b""
else:
self.cmpobj = lzma.BZ2Compressor()
@@ -641,7 +645,7 @@ def readsparse(self, size):
break
size -= len(buf)
data.append(buf)
- return "".join(data)
+ return b"".join(data)
def readsparsesection(self, size):
"""Read a single section of a sparse file.
@@ -649,7 +653,7 @@ def readsparsesection(self, size):
section = self.sparse.find(self.position)
if section is None:
- return ""
+ return b""
size = min(size, section.offset + section.size - self.position)
@@ -663,10 +667,6 @@ def readsparsesection(self, size):
# return NUL * size
#class _FileInFile
-SEEK_SET = 0
-SEEK_CUR = 1
-SEEK_END = 2
-
class ExFileObject(object):
"""File-like object for reading an archive member.
Is returned by CpioFile.extractfile().
@@ -684,7 +684,7 @@ def __init__(self, cpiofile, cpioinfo):
self.size = cpioinfo.size
self.position = 0
- self.buffer = ""
+ self.buffer = b""
def read(self, size=None):
"""Read at most size bytes from the file. If size is not
@@ -693,11 +693,11 @@ def read(self, size=None):
if self.closed:
raise ValueError("I/O operation on closed file")
- buf = ""
+ buf = b""
if self.buffer:
if size is None:
buf = self.buffer
- self.buffer = ""
+ self.buffer = b""
else:
buf = self.buffer[:size]
self.buffer = self.buffer[size:]
@@ -710,6 +710,8 @@ def read(self, size=None):
self.position += len(buf)
return buf
+ # FIXME no universal-newlines, a TextIOWrapper would help but hey
+ # we're not using cpio archives on non-unices, right ?
def readline(self, size=-1):
"""Read one entire line from the file. If size is present
and non-negative, return a string with at most that
@@ -718,16 +720,16 @@ def readline(self, size=-1):
if self.closed:
raise ValueError("I/O operation on closed file")
- if "\n" in self.buffer:
- pos = self.buffer.find("\n") + 1
+ if b"\n" in self.buffer:
+ pos = self.buffer.find(b"\n") + 1
else:
buffers = [self.buffer]
while True:
buf = self.fileobj.read(self.blocksize)
buffers.append(buf)
- if not buf or "\n" in buf:
- self.buffer = "".join(buffers)
- pos = self.buffer.find("\n") + 1
+ if not buf or b"\n" in buf:
+ self.buffer = b"".join(buffers)
+ pos = self.buffer.find(b"\n") + 1
if pos == 0:
# no newline found.
pos = len(self.buffer)
@@ -739,7 +741,7 @@ def readline(self, size=-1):
buf = self.buffer[:pos]
self.buffer = self.buffer[pos:]
self.position += len(buf)
- return buf
+ return six.ensure_text(buf)
def readlines(self):
"""Return a list with all remaining lines.
@@ -760,25 +762,25 @@ def tell(self):
return self.position
- def seek(self, pos, whence=SEEK_SET):
+ def seek(self, pos, whence=os.SEEK_SET):
"""Seek to a position in the file.
"""
if self.closed:
raise ValueError("I/O operation on closed file")
- if whence == SEEK_SET:
+ if whence == os.SEEK_SET:
self.position = min(max(pos, 0), self.size)
- elif whence == SEEK_CUR:
+ elif whence == os.SEEK_CUR:
if pos < 0:
self.position = max(self.position + pos, 0)
else:
self.position = min(self.position + pos, self.size)
- elif whence == SEEK_END:
+ elif whence == os.SEEK_END:
self.position = max(min(self.size + pos, self.size), 0)
else:
raise ValueError("Invalid argument")
- self.buffer = ""
+ self.buffer = b""
self.fileobj.seek(self.position)
def close(self):
@@ -812,7 +814,7 @@ def __init__(self, name=""):
of the member.
"""
self.ino = 0 # i-node
- self.mode = S_IFREG | 0444
+ self.mode = S_IFREG | 0o444
self.uid = 0 # user id
self.gid = 0 # group id
self.nlink = 1 # number of links
@@ -862,23 +864,23 @@ def frombuf(cls, buf):
def tobuf(self):
"""Return a cpio header as a string.
"""
- buf = "%06X" % MAGIC_NEWC
- buf += "%08X" % self.ino
- buf += "%08X" % self.mode
- buf += "%08X" % self.uid
- buf += "%08X" % self.gid
- buf += "%08X" % self.nlink
- buf += "%08X" % self.mtime
- buf += "%08X" % (self.linkname == '' and self.size or
+ buf = b"%06X" % MAGIC_NEWC
+ buf += b"%08X" % self.ino
+ buf += b"%08X" % self.mode
+ buf += b"%08X" % self.uid
+ buf += b"%08X" % self.gid
+ buf += b"%08X" % self.nlink
+ buf += b"%08X" % int(self.mtime)
+ buf += b"%08X" % (self.linkname == '' and self.size or
len(self.linkname))
- buf += "%08X" % self.devmajor
- buf += "%08X" % self.devminor
- buf += "%08X" % self.rdevmajor
- buf += "%08X" % self.rdevminor
- buf += "%08X" % (len(self.name)+1)
- buf += "%08X" % self.check
-
- buf += self.name + NUL
+ buf += b"%08X" % self.devmajor
+ buf += b"%08X" % self.devminor
+ buf += b"%08X" % self.rdevmajor
+ buf += b"%08X" % self.rdevminor
+ buf += b"%08X" % (len(self.name)+1)
+ buf += b"%08X" % self.check
+
+ buf += six.ensure_binary(self.name) + NUL
_, remainder = divmod(len(buf), WORDSIZE)
if remainder != 0:
# pad to next word
@@ -916,7 +918,7 @@ def isdev(self):
return (stat.S_ISCHR(self.mode) or stat.S_ISBLK(self.mode))
# class CpioInfo
-class CpioFile(object):
+class CpioFile(six.Iterator):
"""The CpioFile Class provides an interface to cpio archives.
"""
@@ -949,7 +951,7 @@ def __init__(self, name=None, mode="r", fileobj=None):
self.mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode]
if not fileobj:
- fileobj = file(name, self.mode)
+ fileobj = bltn_open(name, self.mode)
self._extfileobj = False
else:
if name is None and hasattr(fileobj, "name"):
@@ -960,28 +962,29 @@ def __init__(self, name=None, mode="r", fileobj=None):
self.name = None
if name:
self.name = os.path.abspath(name)
+ assert not isinstance(fileobj, io.TextIOBase)
self.fileobj = fileobj
# Init datastructures
self.closed = False
self.members = [] # list of members as CpioInfo objects
self._loaded = False # flag if all members have been read
- self.offset = 0L # current position in the archive file
+ self.offset = 0 # current position in the archive file
self.inodes = {} # dictionary caching the inodes of
# archive members already added
if self._mode == "r":
self.firstmember = None
- self.firstmember = self.next()
+ self.firstmember = next(self)
if self._mode == "a":
# Move to the end of the archive,
# before the trailer.
self.firstmember = None
- last_offset = 0L
+ last_offset = 0
while True:
try:
- cpioinfo = self.next()
+ cpioinfo = next(self)
except ReadError:
self.fileobj.seek(0)
break
@@ -1106,11 +1109,10 @@ def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9):
raise CompressionError("gzip module is not available")
if fileobj is None:
- fileobj = file(name, mode + "b")
+ fileobj = bltn_open(name, mode + "b")
try:
- t = cls.cpioopen(name, mode,
- gzip.GzipFile(name, mode, compresslevel, fileobj))
+ t = cls.cpioopen(name, mode, gzip.GzipFile(name, mode, compresslevel, fileobj))
except IOError:
raise ReadError("not a gzip file")
t._extfileobj = False
@@ -1158,6 +1160,7 @@ def xzopen(cls, name, mode="r", fileobj=None, compresslevel=6):
if fileobj is not None:
fileobj = _XZProxy(fileobj, mode)
else:
+ # FIXME: not compatible with python3 API
fileobj = lzma.LZMAFile(name, mode, options={'level': compresslevel, 'dict_size': 20 })
try:
@@ -1192,11 +1195,6 @@ def close(self):
self.fileobj.write(buf)
self.offset += len(buf)
-# blocks, remainder = divmod(self.offset, BLOCKSIZE)
-# if remainder > 0:
-# self.fileobj.write((BLOCKSIZE - remainder) * NUL)
-# self.offset += (BLOCKSIZE - remainder)
-
if not self._extfileobj:
self.fileobj.close()
self.closed = True
@@ -1279,7 +1277,7 @@ def getcpioinfo(self, name=None, arcname=None, fileobj=None):
if stat.S_ISREG(stmd):
cpioinfo.size = statres.st_size
else:
- cpioinfo.size = 0L
+ cpioinfo.size = 0
cpioinfo.devmajor = os.major(statres.st_dev)
cpioinfo.devminor = os.minor(statres.st_dev)
if stat.S_ISCHR(stmd) or stat.S_ISBLK(stmd):
@@ -1301,17 +1299,22 @@ def list(self, verbose=True):
for cpioinfo in self:
if verbose:
- print filemode(cpioinfo.mode),
- print "%d/%d" % (cpioinfo.uid, cpioinfo.gid),
+ print(filemode(cpioinfo.mode), end=' ')
+ print("%d/%d" % (cpioinfo.uid, cpioinfo.gid), end=' ')
if cpioinfo.ischr() or cpioinfo.isblk():
- print "%10s" % ("%d,%d" \
- % (cpioinfo.devmajor, cpioinfo.devminor)),
+ print("%10s" % ("%d,%d" % (cpioinfo.devmajor, cpioinfo.devminor)), end=' ')
else:
- print "%10d" % cpioinfo.size,
- print "%d-%02d-%02d %02d:%02d:%02d" \
- % time.localtime(cpioinfo.mtime)[:6],
+ print("%10d" % cpioinfo.size, end=' ')
+ print("%d-%02d-%02d %02d:%02d:%02d" % time.localtime(cpioinfo.mtime)[:6], end=' ')
- print cpioinfo.name
+ print(cpioinfo.name, end="")
+
+ if verbose:
+ if cpioinfo.issym():
+ print("->", cpioinfo.linkname, end="")
+ if cpioinfo.islnk():
+ print("link to", cpioinfo.linkname, end="")
+ print()
def add(self, name, arcname=None, recursive=True):
"""Add the file `name' to the archive. `name' may be any type of file
@@ -1351,7 +1354,7 @@ def add(self, name, arcname=None, recursive=True):
# Append the cpio header and data to the archive.
if cpioinfo.isreg():
- f = file(name, "rb")
+ f = bltn_open(name, "rb")
self.addfile(cpioinfo, f)
f.close()
@@ -1376,7 +1379,7 @@ def addfile(self, cpioinfo, fileobj=None):
cpioinfo = copy.copy(cpioinfo)
if cpioinfo.nlink > 1:
- if self.hardlinks and self.inodes.has_key(cpioinfo.ino):
+ if self.hardlinks and cpioinfo.ino in self.inodes:
# this inode has already been added
cpioinfo.size = 0
self.inodes[cpioinfo.ino].append(cpioinfo.name)
@@ -1417,7 +1420,7 @@ def extractall(self, path=".", members=None):
# Extract directory with a safe mode, so that
# all files below can be extracted as well.
try:
- os.makedirs(os.path.join(path, cpioinfo.name), 0777)
+ os.makedirs(os.path.join(path, six.ensure_text(cpioinfo.name)), 0o777)
except EnvironmentError:
pass
directories.append(cpioinfo)
@@ -1425,17 +1428,17 @@ def extractall(self, path=".", members=None):
self.extract(cpioinfo, path)
# Reverse sort directories.
- directories.sort(lambda a, b: cmp(a.name, b.name))
+ directories.sort(key=lambda x: x.name)
directories.reverse()
# Set correct owner, mtime and filemode on directories.
for cpioinfo in directories:
- path = os.path.join(path, cpioinfo.name)
+ path = os.path.join(path, six.ensure_text(cpioinfo.name))
try:
self.chown(cpioinfo, path)
self.utime(cpioinfo, path)
self.chmod(cpioinfo, path)
- except ExtractError, e:
+ except ExtractError as e:
if self.errorlevel > 1:
raise
else:
@@ -1454,14 +1457,13 @@ def extract(self, member, path=""):
else:
cpioinfo = self.getmember(member)
- # Prepare the link cpioget for makelink().
+ # Prepare the link target for makelink().
if cpioinfo.islnk():
-# cpioinfo._link_cpioget = os.path.join(path, cpioinfo.linkname)
cpioinfo._link_path = path
try:
- self._extract_member(cpioinfo, os.path.join(path, cpioinfo.name))
- except EnvironmentError, e:
+ self._extract_member(cpioinfo, os.path.join(path, six.ensure_text(cpioinfo.name)))
+ except EnvironmentError as e:
if self.errorlevel > 0:
raise
else:
@@ -1469,7 +1471,7 @@ def extract(self, member, path=""):
self._dbg(1, "cpiofile: %s" % e.strerror)
else:
self._dbg(1, "cpiofile: %s %r" % (e.strerror, e.filename))
- except ExtractError, e:
+ except ExtractError as e:
if self.errorlevel > 1:
raise
else:
@@ -1479,7 +1481,7 @@ def extractfile(self, member):
"""Extract a member from the archive as a file object. `member' may be
a filename or a CpioInfo object. If `member' is a regular file, a
file-like object is returned. If `member' is a link, a file-like
- object is constructed from the link's cpioget. If `member' is none of
+ object is constructed from the link's target. If `member' is none of
the above, None is returned.
The file-like object is read-only and provides the following
methods: read(), readline(), readlines(), seek() and tell()
@@ -1491,40 +1493,41 @@ def extractfile(self, member):
else:
cpioinfo = self.getmember(member)
- if cpioinfo.issym():
+ if cpioinfo.isreg():
+ return self.fileobject(self, cpioinfo)
+
+ elif cpioinfo.islnk():
+ return self.fileobject(self, self._datamember(cpioinfo))
+ elif cpioinfo.issym():
if isinstance(self.fileobj, _Stream):
# A small but ugly workaround for the case that someone tries
# to extract a symlink as a file-object from a non-seekable
# stream of cpio blocks.
raise StreamError("cannot extract symlink as file object")
else:
- # A symlink's file object is its cpioget's file object.
+ # A symlink's file object is its target's file object.
return self.extractfile(self._getmember(cpioinfo.linkname,
cpioinfo))
- elif cpioinfo.islnk():
- return self.fileobject(self, self._datamember(cpioinfo))
- elif cpioinfo.isreg():
- return self.fileobject(self, cpioinfo)
else:
# If there's no data associated with the member (directory, chrdev,
# blkdev, etc.), return None instead of a file object.
return None
- def _extract_member(self, cpioinfo, cpiogetpath):
+ def _extract_member(self, cpioinfo, targetpath):
"""Extract the CpioInfo object cpioinfo to a physical
- file called cpiogetpath.
+ file called targetpath.
"""
# Fetch the CpioInfo object for the given name
# and build the destination pathname, replacing
# forward slashes to platform specific separators.
- cpiogetpath = os.path.normpath(cpiogetpath)
+ targetpath = os.path.normpath(targetpath)
# Create all upper directories.
- upperdirs = os.path.dirname(cpiogetpath)
+ upperdirs = os.path.dirname(targetpath)
if upperdirs and not os.path.exists(upperdirs):
ti = CpioInfo()
ti.name = upperdirs
- ti.mode = S_IFDIR | 0777
+ ti.mode = S_IFDIR | 0o777
ti.mtime = cpioinfo.mtime
ti.uid = cpioinfo.uid
ti.gid = cpioinfo.gid
@@ -1539,49 +1542,49 @@ def _extract_member(self, cpioinfo, cpiogetpath):
self._dbg(1, cpioinfo.name)
if cpioinfo.isreg():
- self.makefile(cpioinfo, cpiogetpath)
+ self.makefile(cpioinfo, targetpath)
elif cpioinfo.isdir():
- self.makedir(cpioinfo, cpiogetpath)
+ self.makedir(cpioinfo, targetpath)
elif cpioinfo.isfifo():
- self.makefifo(cpioinfo, cpiogetpath)
+ self.makefifo(cpioinfo, targetpath)
elif cpioinfo.ischr() or cpioinfo.isblk():
- self.makedev(cpioinfo, cpiogetpath)
+ self.makedev(cpioinfo, targetpath)
elif cpioinfo.issym():
- self.makesymlink(cpioinfo, cpiogetpath)
+ self.makesymlink(cpioinfo, targetpath)
else:
- self.makefile(cpioinfo, cpiogetpath)
+ self.makefile(cpioinfo, targetpath)
- self.chown(cpioinfo, cpiogetpath)
+ self.chown(cpioinfo, targetpath)
if not cpioinfo.issym():
- self.chmod(cpioinfo, cpiogetpath)
- self.utime(cpioinfo, cpiogetpath)
+ self.chmod(cpioinfo, targetpath)
+ self.utime(cpioinfo, targetpath)
#--------------------------------------------------------------------------
# Below are the different file methods. They are called via
# _extract_member() when extract() is called. They can be replaced in a
# subclass to implement other functionality.
- def makedir(self, cpioinfo, cpiogetpath):
- """Make a directory called cpiogetpath.
+ def makedir(self, cpioinfo, targetpath):
+ """Make a directory called targetpath.
"""
try:
- os.mkdir(cpiogetpath)
- except EnvironmentError, e:
+ os.mkdir(targetpath)
+ except EnvironmentError as e:
if e.errno != errno.EEXIST:
raise
- def makefile(self, cpioinfo, cpiogetpath):
- """Make a file called cpiogetpath.
+ def makefile(self, cpioinfo, targetpath):
+ """Make a file called targetpath.
"""
extractinfo = None
if cpioinfo.nlink == 1:
extractinfo = cpioinfo
else:
- if self.inodes.has_key(cpioinfo.ino):
+ if cpioinfo.ino in self.inodes:
# actual file exists, create link
# FIXME handle platforms that don't support hardlinks
os.link(os.path.join(cpioinfo._link_path,
- self.inodes[cpioinfo.ino][0]), cpiogetpath)
+ six.ensure_text(self.inodes[cpioinfo.ino][0])), targetpath)
else:
extractinfo = self._datamember(cpioinfo)
@@ -1591,21 +1594,21 @@ def makefile(self, cpioinfo, cpiogetpath):
if extractinfo:
source = self.extractfile(extractinfo)
- cpioget = file(cpiogetpath, "wb")
- copyfileobj(source, cpioget)
+ target = bltn_open(targetpath, "wb")
+ copyfileobj(source, target)
source.close()
- cpioget.close()
+ target.close()
- def makefifo(self, cpioinfo, cpiogetpath):
- """Make a fifo called cpiogetpath.
+ def makefifo(self, cpioinfo, targetpath):
+ """Make a fifo called targetpath.
"""
if hasattr(os, "mkfifo"):
- os.mkfifo(cpiogetpath)
+ os.mkfifo(targetpath)
else:
raise ExtractError("fifo not supported by system")
- def makedev(self, cpioinfo, cpiogetpath):
- """Make a character or block device called cpiogetpath.
+ def makedev(self, cpioinfo, targetpath):
+ """Make a character or block device called targetpath.
"""
if not hasattr(os, "mknod") or not hasattr(os, "makedev"):
raise ExtractError("special devices not supported by system")
@@ -1616,25 +1619,25 @@ def makedev(self, cpioinfo, cpiogetpath):
else:
mode |= stat.S_IFCHR
- os.mknod(cpiogetpath, mode,
+ os.mknod(targetpath, mode,
os.makedev(cpioinfo.devmajor, cpioinfo.devminor))
- def makesymlink(self, cpioinfo, cpiogetpath):
+ def makesymlink(self, cpioinfo, targetpath):
# FIXME handle platforms that don't support symlinks
- os.symlink(cpioinfo.linkname, cpiogetpath)
+ os.symlink(cpioinfo.linkname, targetpath)
- def makelink(self, cpioinfo, cpiogetpath):
- """Make a (symbolic) link called cpiogetpath. If it cannot be created
+ def makelink(self, cpioinfo, targetpath):
+ """Make a (symbolic) link called targetpath. If it cannot be created
(platform limitation), we try to make a copy of the referenced file
instead of a link.
"""
linkpath = cpioinfo.linkname
try:
if cpioinfo.issym():
- os.symlink(linkpath, cpiogetpath)
+ os.symlink(linkpath, targetpath)
else:
# See extract().
- os.link(cpioinfo._link_cpioget, cpiogetpath)
+ os.link(cpioinfo._link_target, targetpath)
except AttributeError:
if cpioinfo.issym():
linkpath = os.path.join(os.path.dirname(cpioinfo.name),
@@ -1642,16 +1645,16 @@ def makelink(self, cpioinfo, cpiogetpath):
linkpath = normpath(linkpath)
try:
- self._extract_member(self.getmember(linkpath), cpiogetpath)
+ self._extract_member(self.getmember(linkpath), targetpath)
except (EnvironmentError, KeyError):
linkpath = os.path.normpath(linkpath)
try:
- shutil.copy2(linkpath, cpiogetpath)
+ shutil.copy2(linkpath, targetpath)
except EnvironmentError:
raise IOError("link could not be created")
- def chown(self, cpioinfo, cpiogetpath):
- """Set owner of cpiogetpath according to cpioinfo.
+ def chown(self, cpioinfo, targetpath):
+ """Set owner of targetpath according to cpioinfo.
"""
if PWD and hasattr(os, "geteuid") and os.geteuid() == 0:
# We have to be root to do so.
@@ -1665,24 +1668,24 @@ def chown(self, cpioinfo, cpiogetpath):
u = os.getuid()
try:
if cpioinfo.issym() and hasattr(os, "lchown"):
- os.lchown(cpiogetpath, u, g)
+ os.lchown(targetpath, u, g)
else:
if sys.platform != "os2emx":
- os.chown(cpiogetpath, u, g)
+ os.chown(targetpath, u, g)
except EnvironmentError:
raise ExtractError("could not change owner")
- def chmod(self, cpioinfo, cpiogetpath):
- """Set file permissions of cpiogetpath according to cpioinfo.
+ def chmod(self, cpioinfo, targetpath):
+ """Set file permissions of targetpath according to cpioinfo.
"""
if hasattr(os, 'chmod'):
try:
- os.chmod(cpiogetpath, cpioinfo.mode)
+ os.chmod(targetpath, cpioinfo.mode)
except EnvironmentError:
raise ExtractError("could not change mode")
- def utime(self, cpioinfo, cpiogetpath):
- """Set modification time of cpiogetpath according to cpioinfo.
+ def utime(self, cpioinfo, targetpath):
+ """Set modification time of targetpath according to cpioinfo.
"""
if not hasattr(os, 'utime'):
return
@@ -1691,12 +1694,12 @@ def utime(self, cpioinfo, cpiogetpath):
# to use utime() on directories.
return
try:
- os.utime(cpiogetpath, (cpioinfo.mtime, cpioinfo.mtime))
+ os.utime(targetpath, (cpioinfo.mtime, cpioinfo.mtime))
except EnvironmentError:
raise ExtractError("could not change modification time")
#--------------------------------------------------------------------------
- def next(self):
+ def __next__(self):
"""Return the next member of the archive as a CpioInfo object, when
CpioFile is opened for reading. Return None if there is no more
available.
@@ -1737,7 +1740,7 @@ def next(self):
cpioinfo = self.proc_member(cpioinfo)
- except ValueError, e:
+ except ValueError as e:
if self.offset == 0:
raise ReadError("empty, unreadable or compressed "
"file: %s" % e)
@@ -1793,8 +1796,9 @@ def _getmember(self, name, cpioinfo=None):
else:
end = members.index(cpioinfo)
- for i in xrange(end - 1, -1, -1):
- if name == members[i].name:
+ encoded_name = six.ensure_binary(name)
+ for i in range(end - 1, -1, -1):
+ if encoded_name == members[i].name:
return members[i]
def _load(self):
@@ -1802,7 +1806,7 @@ def _load(self):
members.
"""
while True:
- cpioinfo = self.next()
+ cpioinfo = next(self)
if cpioinfo is None:
break
self._loaded = True
@@ -1828,10 +1832,10 @@ def _dbg(self, level, msg):
"""Write debugging output to sys.stderr.
"""
if level <= self.debug:
- print >> sys.stderr, msg
+ print(msg, file=sys.stderr)
# class CpioFile
-class CpioIter(object):
+class CpioIter(six.Iterator):
"""Iterator Class.
for cpioinfo in CpioFile(...):
@@ -1847,7 +1851,7 @@ def __iter__(self):
"""Return iterator object.
"""
return self
- def next(self):
+ def __next__(self):
"""Return the next item using CpioFile's next() method.
When all members have been read, set CpioFile as _loaded.
"""
@@ -1855,7 +1859,7 @@ def next(self):
# happen that getmembers() is called during iteration,
# which will cause CpioIter to stop prematurely.
if not self.cpiofile._loaded:
- cpioinfo = self.cpiofile.next()
+ cpioinfo = next(self.cpiofile)
if not cpioinfo:
self.cpiofile._loaded = True
raise StopIteration
@@ -1890,10 +1894,9 @@ def __init__(self, fpath, mode="r", compression=CPIO_PLAIN):
m.file_size = m.size
m.date_time = time.gmtime(m.mtime)[:6]
def namelist(self):
- return map(lambda m: m.name, self.infolist())
+ return [m.name for m in self.infolist()]
def infolist(self):
- return filter(lambda m: m.isreg(),
- self.cpiofile.getmembers())
+ return [m for m in self.cpiofile.getmembers() if m.isreg()]
def printdir(self):
self.cpiofile.list()
def testzip(self):
@@ -1904,16 +1907,7 @@ def read(self, name):
return self.cpiofile.extractfile(self.cpiofile.getmember(name)).read()
def write(self, filename, arcname=None, compress_type=None):
self.cpiofile.add(filename, arcname)
- def writestr(self, zinfo, bts):
- try:
- from cStringIO import StringIO
- except ImportError:
- from StringIO import StringIO
- import calendar
- zinfo.name = zinfo.filename
- zinfo.size = zinfo.file_size
- zinfo.mtime = calendar.timegm(zinfo.date_time)
- self.cpiofile.addfile(zinfo, StringIO(bts))
+ # deleted writestr method
def close(self):
self.cpiofile.close()
#class CpioFileCompat
@@ -1932,5 +1926,5 @@ def is_cpiofile(name):
except CpioError:
return False
-def cpioOpen(*al, **ad):
- return CpioFile.open(*al, **ad)
+bltn_open = open
+open = CpioFile.open
diff --git a/xcp/dom0.py b/xcp/dom0.py
index f2ca7097..b8a46c3a 100644
--- a/xcp/dom0.py
+++ b/xcp/dom0.py
@@ -23,9 +23,11 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from __future__ import absolute_import
+from __future__ import division
import re
-import version
+from . import version
import sys
def default_memory_v2(host_mem_kib):
@@ -39,7 +41,7 @@ def default_memory_v2(host_mem_kib):
#
# Add a bit extra to account for this.
#
- gb = (host_mem_kib + 256 * 1024) / 1024 / 1024
+ gb = (host_mem_kib + 256 * 1024) // 1024 // 1024
if gb < 24:
return 752 * 1024
@@ -61,7 +63,7 @@ def default_memory_v3(host_mem_kib):
#
# Add a bit extra to account for this.
#
- mb = (host_mem_kib + 256 * 1024) / 1024
+ mb = (host_mem_kib + 256 * 1024) // 1024
# Give dom0 1 GiB + 5% of host memory, rounded to 16 MiB, limited to 8 GiB
return min(1024 + int(mb * 0.05) & ~0xF, 8192) * 1024
@@ -94,7 +96,7 @@ def default_memory(host_mem_kib):
return default_memory_for_version(host_mem_kib, platform_version)
-_size_and_unit_re = re.compile("^(-?\d+)([bkmg]?)$", re.IGNORECASE)
+_size_and_unit_re = re.compile(r"^(-?\d+)([bkmg]?)$", re.IGNORECASE)
def _parse_size_and_unit(s):
m = _size_and_unit_re.match(s)
diff --git a/xcp/environ.py b/xcp/environ.py
index 27e57323..8223cca8 100644
--- a/xcp/environ.py
+++ b/xcp/environ.py
@@ -47,8 +47,8 @@ def readInventory(root = '/'):
try:
fh = open(os.path.join(root, 'etc/xensource-inventory'))
- for line in ( x for x in ( y.strip() for y in fh.xreadlines() )
- if not x.startswith('#') ):
+ for line in (x for x in (y.strip() for y in fh)
+ if not x.startswith('#')):
vals = line.split('=', 1)
@@ -59,7 +59,7 @@ def readInventory(root = '/'):
d[vals[0]] = vals[1].strip('"\'')
- except IOError, e:
+ except IOError as e:
raise InventoryError("Error reading from file '%s'" % (e,))
finally:
diff --git a/xcp/logger.py b/xcp/logger.py
index 03e8fe78..ca972b5c 100644
--- a/xcp/logger.py
+++ b/xcp/logger.py
@@ -33,6 +33,7 @@
import logging
import logging.handlers
+import six
LOG = logging.getLogger()
LOG.setLevel(logging.NOTSET)
@@ -47,7 +48,7 @@ def openLog(lfile, level=logging.INFO):
try:
# if lfile is a string, assume we need to open() it
- if isinstance(lfile, str):
+ if isinstance(lfile, six.string_types):
h = open(lfile, 'a')
if h.isatty():
handler = logging.StreamHandler(h)
diff --git a/xcp/mount.py b/xcp/mount.py
index 60d191bc..49b0a2e8 100644
--- a/xcp/mount.py
+++ b/xcp/mount.py
@@ -52,13 +52,13 @@ def mount(dev, mountpoint, options = None, fstype = None, label = None):
rc, out, err = xcp.cmd.runCmd(cmd, with_stdout=True, with_stderr=True)
if rc != 0:
- raise MountException, "out: '%s' err: '%s'" % (out, err)
+ raise MountException("out: '%s' err: '%s'" % (out, err))
def bindMount(source, mountpoint):
cmd = [ '/bin/mount', '--bind', source, mountpoint]
rc, out, err = xcp.cmd.runCmd(cmd, with_stdout=True, with_stderr=True)
if rc != 0:
- raise MountException, "out: '%s' err: '%s'" % (out, err)
+ raise MountException("out: '%s' err: '%s'" % (out, err))
def umount(mountpoint, force = False):
# -d option also removes the loop device (if present)
diff --git a/xcp/net/biosdevname.py b/xcp/net/biosdevname.py
index e8166807..78995056 100644
--- a/xcp/net/biosdevname.py
+++ b/xcp/net/biosdevname.py
@@ -35,19 +35,6 @@
__ALL_POLICIES = [ "physical", "all_ethN" ]
-def __run_single_device(eth, policy = "physical"):
- """
- Run 'biosdevname -i eth' for a specified policy.
- Return (stdout, stderr, returncode) tuple.
- """
-
- proc = Popen(["/sbin/biosdevname", "--policy", policy,
- "-i"], stdout=PIPE, stderr=PIPE)
-
- stdout, stderr = proc.communicate()
-
- return ( stdout, stderr, proc.returncode )
-
def __run_all_devices(policy = "physical"):
"""
Run 'biosdevname -d' for a specified policy.
diff --git a/xcp/net/ifrename/dynamic.py b/xcp/net/ifrename/dynamic.py
index 62844848..33040ffd 100644
--- a/xcp/net/ifrename/dynamic.py
+++ b/xcp/net/ifrename/dynamic.py
@@ -104,7 +104,7 @@ def load_and_parse(self):
LOG.error("No source of data to parse")
return False
- except IOError, e:
+ except IOError as e:
LOG.error("IOError while reading file: %s" % (e,))
return False
finally:
@@ -137,7 +137,7 @@ def load_and_parse(self):
if len(entry) != 3:
raise ValueError("Expected 3 entries")
macpci = MACPCI(entry[0], entry[1], tname=entry[2])
- except (TypeError, ValueError), e:
+ except (TypeError, ValueError) as e:
LOG.warning("Invalid lastboot data entry: %s"
% (e,))
continue
@@ -149,7 +149,7 @@ def load_and_parse(self):
if len(entry) != 3:
raise ValueError("Expected 3 entries")
macpci = MACPCI(entry[0], entry[1], tname=entry[2])
- except (TypeError, ValueError), e:
+ except (TypeError, ValueError) as e:
LOG.warning("Invalid old data entry: %s" % (e,))
continue
self.old.append(macpci)
@@ -170,7 +170,7 @@ def generate(self, state):
LOG.warning("Discovered physical policy naming quirks in provided "
"state. Disabling 'method=ppn' generation")
- for target, (method, value) in self.formulae.iteritems():
+ for target, (method, value) in self.formulae.items():
if method == "mac":
@@ -178,7 +178,7 @@ def generate(self, state):
if nic.mac == value:
try:
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -199,7 +199,7 @@ def generate(self, state):
if nic.ppn == value:
try:
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -213,7 +213,7 @@ def generate(self, state):
try:
nic = pci_sbdfi_to_nic(value, state)
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -226,7 +226,7 @@ def generate(self, state):
if nic.label == value:
try:
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -257,13 +257,13 @@ def validate(entry):
return False
MACPCI(entry[0], entry[1], tname=entry[2])
return True
- except Exception, e:
+ except Exception as e:
LOG.warning("Failed to validate '%s' because '%s'"
% (entry, e))
return False
- lastboot = filter(validate, self.lastboot)
- old = filter(validate, self.old)
+ lastboot = [x for x in self.lastboot if validate(x)]
+ old = [x for x in self.old if validate(x)]
try:
res += json.dumps({"lastboot": lastboot, "old": old},
@@ -301,7 +301,7 @@ def save(self, header = True):
LOG.error("No source of data to parse")
return False
- except IOError, e:
+ except IOError as e:
LOG.error("IOError while reading file: %s" % (e,))
return False
finally:
diff --git a/xcp/net/ifrename/logic.py b/xcp/net/ifrename/logic.py
index 2328ca54..41e74c02 100644
--- a/xcp/net/ifrename/logic.py
+++ b/xcp/net/ifrename/logic.py
@@ -52,9 +52,9 @@
from xcp.logger import LOG
from xcp.net.ifrename.macpci import MACPCI
-VALID_CUR_STATE_KNAME = re.compile("^(?:eth[\d]+|side-[\d]+-eth[\d]+)$")
-VALID_ETH_NAME = re.compile("^eth([\d])+$")
-VALID_IBFT_NAME = re.compile("^ibft([\d])+$")
+VALID_CUR_STATE_KNAME = re.compile(r"^(?:eth[\d]+|side-[\d]+-eth[\d]+)$")
+VALID_ETH_NAME = re.compile(r"^eth([\d])+$")
+VALID_IBFT_NAME = re.compile(r"^ibft([\d])+$")
# util needs to import VALID_ETH_NAME
from xcp.net.ifrename import util
@@ -83,12 +83,12 @@ def __rename_nic(nic, name, transactions, cur_state):
# Assert that name is valid
assert VALID_ETH_NAME.match(name) is not None
# Assert that name is not already taken in the current state
- assert name not in map(lambda x: x.tname, cur_state)
+ assert name not in (x.tname for x in cur_state)
# Given the previous assert, only un-renamed nics in the current state can
# possibly alias the new name
aliased = util.get_nic_with_kname(
- filter(lambda x: x.tname is None, cur_state), name)
+ (x for x in cur_state if x.tname is None), name)
if aliased is None:
# Using this rule will not alias another currently present NIC
@@ -289,9 +289,10 @@ def rename_logic( static_rules,
# Check that the function still has the same number of nics
if len(lastnics) != len(newnics):
- LOG.warn("multi-nic function %s had %d nics but now has %d. "
- "Defering all until later for renaming"
- % (fn, len(lastnics), len(newnics)))
+ LOG.warning(
+ "multi-nic function %s had %d nics but now has %d. "
+ "Defering all until later for renaming",
+ fn, len(lastnics), len(newnics))
continue
# Check that all nics are still pending a rename
@@ -325,9 +326,8 @@ def rename_logic( static_rules,
if len(multinic_functions):
LOG.debug("New multi-nic logic - attempting to re-order")
for fn in multinic_functions:
- newnics = util.get_nics_with_pci(filter(util.needs_renaming, cur_state),
- fn)
- orders = sorted(map(lambda x: x.order, newnics))
+ newnics = util.get_nics_with_pci((x for x in cur_state if util.needs_renaming(x)), fn)
+ orders = sorted(x.order for x in newnics)
newnics.sort(key = lambda n: n.mac.integer)
for nic, neworder in zip(newnics, orders):
LOG.debug("NIC '%s' getting new order '%s'" % (nic, neworder))
@@ -336,10 +336,9 @@ def rename_logic( static_rules,
# For completely new network cards which we have never seen before, work out
# a safe new number to assign it
ethnumbers = sorted(
- map(lambda x: int(x[3:]),
- filter(lambda x: VALID_ETH_NAME.match(x) is not None,
- map(lambda x: x.tname or x.kname,
- static_rules + cur_state + last_state))))
+ int(x[3:])
+ for x in (x.tname or x.kname for x in static_rules + cur_state + last_state)
+ if VALID_ETH_NAME.match(x) is not None)
if len(ethnumbers):
nextethnum = ethnumbers[-1]+1
else:
@@ -352,8 +351,8 @@ def rename_logic( static_rules,
key=lambda x: x.order):
LOG.info("Renaming brand new nic '%s'" % (nic,))
- if ( VALID_ETH_NAME.match(nic.kname) is not None and
- nic.kname not in map(lambda x: x.tname, cur_state) ):
+ if (VALID_ETH_NAME.match(nic.kname) is not None and
+ nic.kname not in (x.tname for x in cur_state)):
# User has been messing around with state files but not the udev
# rules. If the eth name is still free, give it
@@ -411,21 +410,20 @@ def rename( static_rules,
"'eth'" % (e, ))
# Verify no two static rules refer to the same eth name
- _ = frozenset( map(lambda x: x.tname, static_rules) )
+ _ = frozenset(x.tname for x in static_rules)
if len(_) != len(static_rules):
raise StaticRuleError("Some static rules alias the same "
"eth name")
# Verify no two static rules refer to the same mac address
- _ = frozenset( map(lambda x: x.mac, static_rules) )
+ _ = frozenset(x.mac for x in static_rules)
if len(_) != len(static_rules):
raise StaticRuleError("Some static rules alias the same MAC "
"address")
if len(cur_state):
# Filter out iBFT NICs
- cur_state = filter(lambda x: VALID_IBFT_NAME.match(x.kname) is None,
- cur_state)
+ cur_state = [x for x in cur_state if VALID_IBFT_NAME.match(x.kname) is None]
# Verify types and properties of the list
for e in cur_state:
@@ -445,13 +443,13 @@ def rename( static_rules,
# Verify no two entries of current state refer to the same eth name
- _ = frozenset( map(lambda x: x.kname, cur_state) )
+ _ = frozenset(x.kname for x in cur_state)
if len(_) != len(cur_state):
raise CurrentStateError("Some entries of current state alias the "
"same eth name")
# Verify no two entries of current state refer to the same mac address
- _ = frozenset( map(lambda x: x.mac, cur_state) )
+ _ = frozenset(x.mac for x in cur_state)
if len(_) != len(cur_state):
raise CurrentStateError("Some entries of current state alias the "
"same MAC address")
@@ -474,13 +472,13 @@ def rename( static_rules,
# Verify no two entries of last state refer to the same eth name
- _ = frozenset( map(lambda x: x.tname, last_state) )
+ _ = frozenset(x.tname for x in last_state)
if len(_) != len(last_state):
raise LastStateError("Some entries of last state alias the "
"same eth name")
# Verify no two entries of last state refer to the same mac address
- _ = frozenset( map(lambda x: x.mac, last_state) )
+ _ = frozenset(x.mac for x in last_state)
if len(_) != len(last_state):
raise LastStateError("Some entries of last state alias the "
"same MAC address")
diff --git a/xcp/net/ifrename/static.py b/xcp/net/ifrename/static.py
index 89a77fda..bf503d54 100644
--- a/xcp/net/ifrename/static.py
+++ b/xcp/net/ifrename/static.py
@@ -89,7 +89,7 @@ class StaticRules(object):
methods = ["mac", "pci", "ppn", "label", "guess"]
validators = { "mac": VALID_MAC,
"pci": VALID_PCI,
- "ppn": re.compile("^(?:em\d+|p(?:ci)?\d+p\d+)$")
+ "ppn": re.compile(r"^(?:em\d+|p(?:ci)?\d+p\d+)$")
}
def __init__(self, path=None, fd=None):
@@ -127,7 +127,7 @@ def load_and_parse(self):
LOG.error("No source of data to parse")
return False
- except IOError, e:
+ except IOError as e:
LOG.error("IOError while reading file: %s" % (e,))
return False
finally:
@@ -175,7 +175,7 @@ def load_and_parse(self):
# If we need to guess the method from the value
if method == "guess":
- for k, v in StaticRules.validators.iteritems():
+ for k, v in StaticRules.validators.items():
if v.match(value) is not None:
method = k
break
@@ -222,7 +222,7 @@ def generate(self, state):
LOG.warning("Discovered physical policy naming quirks in provided "
"state. Disabling 'method=ppn' generation")
- for target, (method, value) in self.formulae.iteritems():
+ for target, (method, value) in self.formulae.items():
if method == "mac":
@@ -230,7 +230,7 @@ def generate(self, state):
if nic.mac == value:
try:
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -251,7 +251,7 @@ def generate(self, state):
if nic.ppn == value:
try:
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -265,7 +265,7 @@ def generate(self, state):
try:
nic = pci_sbdfi_to_nic(value, state)
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -278,7 +278,7 @@ def generate(self, state):
if nic.label == value:
try:
rule = MACPCI(nic.mac, nic.pci, tname=target)
- except Exception, e:
+ except Exception as e:
LOG.warning("Error creating rule: %s" % (e,))
continue
self.rules.append(rule)
@@ -298,8 +298,7 @@ def write(self, header = True):
if header:
res += SAVE_HEADER
- keys = list(set(( x for x in self.formulae.keys()
- if x.startswith("eth") )))
+ keys = list(set((x for x in self.formulae if x.startswith("eth"))))
keys.sort(key=lambda x: int(x[3:]))
for target in keys:
@@ -340,7 +339,7 @@ def save(self, header = True):
LOG.error("No source of data to parse")
return False
- except IOError, e:
+ except IOError as e:
LOG.error("IOError while reading file: %s" % (e,))
return False
finally:
diff --git a/xcp/net/ifrename/util.py b/xcp/net/ifrename/util.py
index 72c89fa3..0aad71f1 100644
--- a/xcp/net/ifrename/util.py
+++ b/xcp/net/ifrename/util.py
@@ -50,7 +50,7 @@ def get_nic_with_kname(nics, kname):
def tname_free(nics, name):
"""Check that name is not taken by any nics"""
- return name not in map(lambda x: x.tname, nics)
+ return name not in (x.tname for x in nics)
def get_nic_with_mac(nics, mac):
"""Search for nic with mac"""
diff --git a/xcp/net/mac.py b/xcp/net/mac.py
index a675a179..56ba4b7b 100644
--- a/xcp/net/mac.py
+++ b/xcp/net/mac.py
@@ -31,11 +31,13 @@
__author__ = "Andrew Cooper"
import re
+import six
VALID_COLON_MAC = re.compile(r"^([\da-fA-F]{1,2}:){5}[\da-fA-F]{1,2}$")
VALID_DASH_MAC = re.compile(r"^([\da-fA-F]{1,2}-){5}[\da-fA-F]{1,2}$")
VALID_DOTQUAD_MAC = re.compile(r"^([\da-fA-F]{1,4}\.){2}[\da-fA-F]{1,4}$")
+@six.python_2_unicode_compatible
class MAC(object):
"""
Mac address object for manipulation and comparison
@@ -57,29 +59,27 @@ def __init__(self, addr):
"""Constructor"""
self.octets = []
- self.integer = -1L
+ self.integer = -1
- if isinstance(addr, (str, unicode)):
+ if not isinstance(addr, six.string_types):
+ raise TypeError("String expected")
- res = VALID_COLON_MAC.match(addr)
- if res:
- self._set_from_str_octets(addr.split(":"))
- return
+ res = VALID_COLON_MAC.match(addr)
+ if res:
+ self._set_from_str_octets(addr.split(":"))
+ return
- res = VALID_DASH_MAC.match(addr)
- if res:
- self._set_from_str_octets(addr.split("-"))
- return
+ res = VALID_DASH_MAC.match(addr)
+ if res:
+ self._set_from_str_octets(addr.split("-"))
+ return
- res = VALID_DOTQUAD_MAC.match(addr)
- if res:
- self._set_from_str_quads(addr.split("."))
- return
+ res = VALID_DOTQUAD_MAC.match(addr)
+ if res:
+ self._set_from_str_quads(addr.split("."))
+ return
- raise ValueError("Unrecognised MAC address '%s'" % addr)
-
- else:
- raise TypeError("String expected")
+ raise ValueError("Unrecognised MAC address '%s'" % addr)
def _set_from_str_octets(self, octets):
@@ -88,8 +88,8 @@ def _set_from_str_octets(self, octets):
raise ValueError("Expected 6 octets, got %d" % len(octets))
self.octets = [ int(i, 16) for i in octets ]
- self.integer = long(sum(t[0] << t[1] for t in
- zip(self.octets, xrange(40, -1, -8))))
+ self.integer = sum(t[0] << t[1] for t in
+ zip(self.octets, range(40, -1, -8)))
def _set_from_str_quads(self, quads):
"""Private helper"""
@@ -100,8 +100,8 @@ def _set_from_str_quads(self, quads):
for quad in ( int(i, 16) for i in quads ):
self.octets.extend([(quad >> 8) & 0xff, quad & 0xff])
- self.integer = long(sum(t[0] << t[1] for t in
- zip(self.octets, xrange(40, -1, -8))))
+ self.integer = sum(t[0] << t[1] for t in
+ zip(self.octets, range(40, -1, -8)))
def is_unicast(self):
"""is this a unicast address?"""
@@ -122,9 +122,6 @@ def is_local(self):
def __str__(self):
- return unicode(self).encode('utf-8')
-
- def __unicode__(self):
return ':'.join([ "%0.2x" % x for x in self.octets])
def __repr__(self):
diff --git a/xcp/pci.py b/xcp/pci.py
index c1fc90e3..1f911a6b 100644
--- a/xcp/pci.py
+++ b/xcp/pci.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-
# Copyright (c) 2013, Citrix Inc.
# All rights reserved.
#
@@ -26,11 +24,12 @@
import os.path
import subprocess
import re
+import six
_SBDF = (r"(?:(?P [\da-dA-F]{4}):)?" # Segment (optional)
- " (?P [\da-fA-F]{2}):" # Bus
- " (?P [\da-fA-F]{2})\." # Device
- " (?P[\da-fA-F])" # Function
+ r" (?P [\da-fA-F]{2}):" # Bus
+ r" (?P [\da-fA-F]{2})\." # Device
+ r" (?P[\da-fA-F])" # Function
)
# Don't change the meaning of VALID_SBDF as some parties may be using it
@@ -38,7 +37,7 @@
VALID_SBDFI = re.compile(
r"^(?P%s)"
- " (?:[[](?P[\d]{1,2})[]])?$" # Index (optional)
+ r" (?:[[](?P[\d]{1,2})[]])?$" # Index (optional)
% _SBDF
, re.X)
@@ -68,48 +67,46 @@ def __init__(self, addr):
self.function = -1
self.index = -1
- if isinstance(addr, (str, unicode)):
-
- res = VALID_SBDFI.match(addr)
- if res:
- groups = res.groupdict()
+ if not isinstance(addr, six.string_types):
+ raise TypeError("String expected")
- if "segment" in groups and groups["segment"] is not None:
- self.segment = int(groups["segment"], 16)
- else:
- self.segment = 0
+ res = VALID_SBDFI.match(addr)
+ if res:
+ groups = res.groupdict()
- self.bus = int(groups["bus"], 16)
- if not ( 0 <= self.bus < 2**8 ):
- raise ValueError("Bus '%d' out of range 0 <= bus < 256"
- % (self.bus,))
+ if "segment" in groups and groups["segment"] is not None:
+ self.segment = int(groups["segment"], 16)
+ else:
+ self.segment = 0
- self.device = int(groups["device"], 16)
- if not ( 0 <= self.device < 2**5):
- raise ValueError("Device '%d' out of range 0 <= device < 32"
- % (self.device,))
+ self.bus = int(groups["bus"], 16)
+ if not ( 0 <= self.bus < 2**8 ):
+ raise ValueError("Bus '%d' out of range 0 <= bus < 256"
+ % (self.bus,))
- self.function = int(groups["function"], 16)
- if not ( 0 <= self.function < 2**3):
- raise ValueError("Function '%d' out of range 0 <= device "
- "< 8" % (self.function,))
+ self.device = int(groups["device"], 16)
+ if not ( 0 <= self.device < 2**5):
+ raise ValueError("Device '%d' out of range 0 <= device < 32"
+ % (self.device,))
- if "index" in groups and groups["index"] is not None:
- self.index = int(groups["index"])
- else:
- self.index = 0
+ self.function = int(groups["function"], 16)
+ if not ( 0 <= self.function < 2**3):
+ raise ValueError("Function '%d' out of range 0 <= device "
+ "< 8" % (self.function,))
- self.integer = (int(self.segment << 16 |
- self.bus << 8 |
- self.device << 3 |
- self.function) << 8 |
- self.index)
- return
+ if "index" in groups and groups["index"] is not None:
+ self.index = int(groups["index"])
+ else:
+ self.index = 0
- raise ValueError("Unrecognised PCI address '%s'" % addr)
+ self.integer = (int(self.segment << 16 |
+ self.bus << 8 |
+ self.device << 3 |
+ self.function) << 8 |
+ self.index)
+ return
- else:
- raise TypeError("String expected")
+ raise ValueError("Unrecognised PCI address '%s'" % addr)
def __str__(self):
@@ -126,7 +123,7 @@ def __eq__(self, rhs):
else:
try:
return self.integer == PCI(rhs).integer
- except StandardError:
+ except Exception:
return NotImplemented
def __ne__(self, rhs):
@@ -135,7 +132,7 @@ def __ne__(self, rhs):
else:
try:
return self.integer != PCI(rhs).integer
- except StandardError:
+ except Exception:
return NotImplemented
def __hash__(self):
@@ -147,7 +144,7 @@ def __lt__(self, rhs):
else:
try:
return self.integer < PCI(rhs).integer
- except StandardError:
+ except Exception:
return NotImplemented
def __le__(self, rhs):
@@ -156,7 +153,7 @@ def __le__(self, rhs):
else:
try:
return self.integer <= PCI(rhs).integer
- except StandardError:
+ except Exception:
return NotImplemented
def __gt__(self, rhs):
@@ -165,7 +162,7 @@ def __gt__(self, rhs):
else:
try:
return self.integer > PCI(rhs).integer
- except StandardError:
+ except Exception:
return NotImplemented
def __ge__(self, rhs):
@@ -174,7 +171,7 @@ def __ge__(self, rhs):
else:
try:
return self.integer >= PCI(rhs).integer
- except StandardError:
+ except Exception:
return NotImplemented
@@ -234,7 +231,7 @@ def read(cls):
for f in ['/usr/share/hwdata/pci.ids']:
if os.path.exists(f):
return cls(f)
- raise Exception, 'Failed to open PCI database'
+ raise Exception('Failed to open PCI database')
def findVendor(self, vendor):
return vendor in self.vendor_dict and self.vendor_dict[vendor] or None
@@ -262,8 +259,7 @@ def __init__(self):
stdout = subprocess.PIPE)
for l in cmd.stdout:
line = l.rstrip()
- el = filter(lambda x: not x.startswith('-'),
- line.replace('"','').split())
+ el = [x for x in line.replace('"', '').split() if not x.startswith('-')]
self.devs[el[0]] = {'id': el[0],
'class': el[1][:2],
'subclass': el[1][2:],
@@ -280,12 +276,11 @@ def findByClass(self, cls, subcls = None):
class, subclass
[class1, class2, ... classN]"""
if subcls:
- assert isinstance(cls, str)
- return filter(lambda x: x['class'] == cls and
- x['subclass'] == subcls, self.devs.values())
+ assert isinstance(cls, six.string_types)
+ return [x for x in self.devs.values() if x['class'] == cls and x['subclass'] == subcls]
else:
assert isinstance(cls, list)
- return filter(lambda x: x['class'] in cls, self.devs.values())
+ return [x for x in self.devs.values() if x['class'] in cls]
def findRelatedFunctions(self, dev):
""" return other devices that share the same bus & slot"""
@@ -293,8 +288,7 @@ def slot(dev):
left, _ = dev.rsplit('.', 1)
return left
- return filter(lambda x: x != dev and slot(x) == slot(dev),
- self.devs.keys())
+ return [x for x in self.devs if x != dev and slot(x) == slot(dev)]
def pci_sbdfi_to_nic(sbdfi, nics):
@@ -314,17 +308,3 @@ def pci_sbdfi_to_nic(sbdfi, nics):
raise Exception("Insufficient NICs with PCI SBDF %s (Found %d, wanted at least %d)" % (value, len(matching_nics), index))
return matching_nics[index]
-
-
-if __name__ == "__main__":
- IDS = PCIIds.read()
- VIDEO_CLASS = IDS.lookupClass('Display controller')
-
- DEVS = PCIDevices()
- for video_dev in DEVS.findByClass(VIDEO_CLASS):
- print video_dev['id'], IDS.findVendor(video_dev['vendor']), \
- IDS.findDevice(video_dev['vendor'], video_dev['device'])
- print DEVS.findRelatedFunctions(video_dev['id'])
- print DEVS.findRelatedFunctions('00:1d.1')
-
-
diff --git a/xcp/repository.py b/xcp/repository.py
index 3da2f17b..35a2c08d 100644
--- a/xcp/repository.py
+++ b/xcp/repository.py
@@ -23,15 +23,19 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import md5
+from hashlib import md5
+import io
import os.path
import xml.dom.minidom
-import ConfigParser
+import configparser
+import sys
+
+import six
import xcp.version as version
import xcp.xmlunwrap as xmlunwrap
-class Package:
+class Package(object): # pylint: disable=too-few-public-methods
pass
class BzippedPackage(Package):
@@ -44,7 +48,7 @@ def __init__(self, repository, label, size, md5sum, optional, fname, root):
self.optional,
self.filename,
self.destination
- ) = ( repository, label, long(size), md5sum, (optional==True), fname, root )
+ ) = (repository, label, size, md5sum, optional is True, fname, root)
def __repr__(self):
return "" % self.label
@@ -59,7 +63,7 @@ def __init__(self, repository, label, size, md5sum, optional, fname, options):
self.optional,
self.filename,
self.options
- ) = ( repository, label, long(size), md5sum, (optional==True), fname, options )
+ ) = (repository, label, size, md5sum, optional is True, fname, options)
def __repr__(self):
return "" % self.label
@@ -74,7 +78,7 @@ def __init__(self, repository, label, size, md5sum, fname, kernel, options):
self.filename,
self.kernel,
self.options
- ) = ( repository, label, long(size), md5sum, fname, kernel, options )
+ ) = (repository, label, size, md5sum, fname, kernel, options)
def __repr__(self):
return "" % (self.label, self.kernel)
@@ -88,7 +92,7 @@ def __init__(self, repository, label, size, md5sum, fname, root):
self.md5sum,
self.filename,
self.destination
- ) = ( repository, label, long(size), md5sum, fname, root )
+ ) = (repository, label, size, md5sum, fname, root)
def __repr__(self):
return "" % self.label
@@ -101,7 +105,7 @@ def __init__(self, repository, label, size, md5sum, fname):
self.size,
self.md5sum,
self.filename
- ) = ( repository, label, long(size), md5sum, fname )
+ ) = (repository, label, size, md5sum, fname)
def __repr__(self):
return "" % self.label
@@ -168,8 +172,8 @@ def __init__(self, access, base = ""):
def isRepo(cls, access, base):
""" Return whether there is a repository at base address
'base' accessible using accessor."""
- return False not in map(lambda x: access.access(os.path.join(base, x)),
- [cls.TREEINFO_FILENAME, cls.REPOMD_FILENAME])
+ return False not in (access.access(os.path.join(base, x))
+ for x in [cls.TREEINFO_FILENAME, cls.REPOMD_FILENAME])
@classmethod
def _getVersion(cls, access, category):
@@ -177,18 +181,26 @@ def _getVersion(cls, access, category):
access.start()
try:
- treeinfofp = access.openAddress(cls.TREEINFO_FILENAME)
- treeinfo = ConfigParser.SafeConfigParser()
- treeinfo.readfp(treeinfofp)
- treeinfofp.close()
+ rawtreeinfofp = access.openAddress(cls.TREEINFO_FILENAME)
+ if sys.version_info < (3, 0) or isinstance(rawtreeinfofp, io.TextIOBase):
+ # e.g. with FileAccessor
+ treeinfofp = rawtreeinfofp
+ else:
+ # e.g. with HTTPAccessor
+ treeinfofp = io.TextIOWrapper(rawtreeinfofp, encoding='utf-8')
+ treeinfo = configparser.ConfigParser()
+ treeinfo.read_file(treeinfofp)
+ treeinfofp = None
+ rawtreeinfofp.close()
if treeinfo.has_section('system-v1'):
ver_str = treeinfo.get('system-v1', category_map[category])
else:
ver_str = treeinfo.get(category, 'version')
repo_ver = version.Version.from_string(ver_str)
- except Exception, e:
- raise RepoFormatError, "Failed to open %s: %s" % (cls.TREEINFO_FILENAME, str(e))
+ except Exception as e:
+ six.raise_from(RepoFormatError("Failed to open %s: %s" %
+ (cls.TREEINFO_FILENAME, str(e))), e)
access.finish()
return repo_ver
@@ -230,8 +242,9 @@ def findRepositories(cls, access):
for line in extra:
package_list.append(line.strip())
extra.close()
- except Exception, e:
- raise RepoFormatError, "Failed to open %s: %s" % (cls.REPOLIST_FILENAME, str(e))
+ except Exception as e:
+ six.raise_from(RepoFormatError("Failed to open %s: %s" %
+ (cls.REPOLIST_FILENAME, str(e))), e)
for loc in package_list:
if cls.isRepo(access, loc):
@@ -242,7 +255,7 @@ def findRepositories(cls, access):
def __init__(self, access, base, is_group = False):
BaseRepository.__init__(self, access, base)
self.is_group = is_group
- self._md5 = md5.new()
+ self._md5 = md5()
self.requires = []
self.packages = []
@@ -250,17 +263,17 @@ def __init__(self, access, base, is_group = False):
try:
repofile = access.openAddress(os.path.join(base, self.REPOSITORY_FILENAME))
- except Exception, e:
+ except Exception as e:
access.finish()
- raise NoRepository, e
+ six.raise_from(NoRepository(), e)
self._parse_repofile(repofile)
repofile.close()
try:
pkgfile = access.openAddress(os.path.join(base, self.PKGDATA_FILENAME))
- except Exception, e:
+ except Exception as e:
access.finish()
- raise NoRepository, e
+ six.raise_from(NoRepository(), e)
self._parse_packages(pkgfile)
pkgfile.close()
@@ -284,13 +297,13 @@ def _parse_repofile(self, repofile):
repofile.close()
# update md5sum for repo
- self._md5.update(repofile_contents)
+ self._md5.update(repofile_contents.encode())
# build xml doc object
try:
xmldoc = xml.dom.minidom.parseString(repofile_contents)
- except:
- raise RepoFormatError, "%s not in XML" % self.REPOSITORY_FILENAME
+ except Exception as e:
+ six.raise_from(RepoFormatError("%s not in XML" % self.REPOSITORY_FILENAME), e)
try:
repo_node = xmlunwrap.getElementsByTagName(xmldoc, ['repository'], mandatory = True)
@@ -313,8 +326,8 @@ def _parse_repofile(self, repofile):
del req['build']
assert req['test'] in self.OPER_MAP
self.requires.append(req)
- except:
- raise RepoFormatError, "%s format error" % self.REPOSITORY_FILENAME
+ except Exception as e:
+ six.raise_from(RepoFormatError("%s format error" % self.REPOSITORY_FILENAME), e)
self.identifier = "%s:%s" % (self.originator, self.name)
ver_str = self.version
@@ -332,8 +345,8 @@ def _parse_packages(self, pkgfile):
# build xml doc object
try:
xmldoc = xml.dom.minidom.parseString(pkgfile_contents)
- except:
- raise RepoFormatError, "%s not in XML" % self.PKGDATA_FILENAME
+ except Exception as e:
+ six.raise_from(RepoFormatError("%s not in XML" % self.PKGDATA_FILENAME), e)
for pkg_node in xmlunwrap.getElementsByTagName(xmldoc, ['package']):
pkg = self._create_package(pkg_node)
@@ -364,8 +377,8 @@ def _create_package(self, node):
def isRepo(cls, access, base):
""" Return whether there is a repository at base address
'base' accessible using accessor."""
- return False not in map(lambda x: access.access(os.path.join(base, x)),
- [cls.REPOSITORY_FILENAME, cls.PKGDATA_FILENAME])
+ return False not in (access.access(os.path.join(base, x))
+ for x in [cls.REPOSITORY_FILENAME, cls.PKGDATA_FILENAME])
@classmethod
def getRepoVer(cls, access):
diff --git a/xcp/version.py b/xcp/version.py
index aaa7fcd6..e19d3c96 100644
--- a/xcp/version.py
+++ b/xcp/version.py
@@ -25,7 +25,7 @@
"""version - version comparison methods"""
-class Version:
+class Version(object):
def __init__(self, ver, build = None):
self.ver = ver
self.build = build
@@ -52,7 +52,7 @@ def from_string(cls, ver_str):
if '-' in ver_str:
ver_str, build = ver_str.split('-', 1)
- ver = map(cls.intify, ver_str.split('.'))
+ ver = [cls.intify(i) for i in ver_str.split('.')]
return cls(ver, build)
diff --git a/xcp/xmlunwrap.py b/xcp/xmlunwrap.py
index 3256cab3..2832b680 100644
--- a/xcp/xmlunwrap.py
+++ b/xcp/xmlunwrap.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-
# Copyright (c) 2013, Citrix Inc.
# All rights reserved.
#
@@ -25,8 +23,7 @@
"""xmlunwrap - general methods to unwrap XML elements & attributes"""
-import xml.dom.minidom
-
+import six
class XmlUnwrapError(Exception):
pass
@@ -44,18 +41,18 @@ def getElementsByTagName(el, tags, mandatory = False):
for tag in tags:
matching.extend(el.getElementsByTagName(tag))
if mandatory and len(matching) == 0:
- raise XmlUnwrapError, "Missing mandatory element %s" % tags[0]
+ raise XmlUnwrapError("Missing mandatory element %s" % tags[0])
return matching
-def getStrAttribute(el, attrs, default = '', mandatory = False):
+def getStrAttribute(el, attrs, default=b'', mandatory=False):
matching = []
for attr in attrs:
val = el.getAttribute(attr).encode()
- if val != '':
+ if val != b'':
matching.append(val)
if len(matching) == 0:
if mandatory:
- raise XmlUnwrapError, "Missing mandatory attribute %s" % attrs[0]
+ raise XmlUnwrapError("Missing mandatory attribute %s" % attrs[0])
return default
return matching[0]
@@ -73,8 +70,8 @@ def getIntAttribute(el, attrs, default = None):
return default
try:
int_val = int(val, 0)
- except:
- raise XmlUnwrapError, "Invalid integer value for %s" % attrs[0]
+ except Exception as e:
+ six.raise_from(XmlUnwrapError("Invalid integer value for %s" % attrs[0]), e)
return int_val
def getMapAttribute(el, attrs, mapping, default = None):
@@ -83,29 +80,7 @@ def getMapAttribute(el, attrs, mapping, default = None):
key = getStrAttribute(el, attrs, default, mandatory)
if key not in k:
- raise XmlUnwrapError, "Unexpected key %s for attribute" % key
+ raise XmlUnwrapError("Unexpected key %s for attribute" % key)
k_list = list(k)
return v[k_list.index(key)]
-
-if __name__ == '__main__':
-
- a_text = """
- text1
- text2
- """
- xmldoc = xml.dom.minidom.parseString(a_text)
- top_el = xmldoc.documentElement
-
- print top_el.tagName
-
- for el in getElementsByTagName(top_el, ["fred"]):
- print getText(el)
-
- print getMapAttribute(top_el, ["mode"], [('test', 42), ('stuff', 77)])
- print getMapAttribute(top_el, ["made"], [('test', 42), ('stuff', 77)], default = 'stuff')
-
- print getStrAttribute(top_el, ["mode"])
- print getStrAttribute(top_el, ["made"])
- print getStrAttribute(top_el, ["made"], None)
- print getStrAttribute(top_el, ["made"], mandatory = True)