Remove the PowerVM driver

The PowerVM driver was deprecated in November 2021 as part of change
Icdef0a03c3c6f56b08ec9685c6958d6917bc88cb. As noted there, all
indications suggest that this driver is no longer maintained and may be
abandonware. It's been some time and there's still no activity here so
it's time to abandon this for real.

This isn't as tied into the codebase as the old XenAPI driver was, so
removal is mostly a case of deleting large swathes of code. Lovely.

Change-Id: Ibf4f36136f2c65adad64f75d665c00cf2de4b400
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
Stephen Finucane
2022-08-02 15:31:19 +02:00
committed by Balazs Gibizer
parent c36782a96a
commit deae814611
66 changed files with 19 additions and 9350 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ OpenStack Nova
OpenStack Nova provides a cloud computing fabric controller, supporting a wide
variety of compute technologies, including: libvirt (KVM, Xen, LXC and more),
Hyper-V, VMware, OpenStack Ironic and PowerVM.
Hyper-V, VMware and OpenStack Ironic.
Use the following resources to learn more.
-2
View File
@@ -84,8 +84,6 @@ availability zones. Nova supports the following hypervisors:
- `Linux Containers (LXC) <https://linuxcontainers.org>`__
- `PowerVM <https://www.ibm.com/us-en/marketplace/ibm-powervm>`__
- `Quick Emulator (QEMU) <https://wiki.qemu.org/Manual>`__
- `Virtuozzo <https://www.virtuozzo.com/products/vz7.html>`__
+4 -5
View File
@@ -39,9 +39,8 @@ compute host and image.
.. rubric:: Compute host requirements
The following virt drivers support the config drive: libvirt,
Hyper-V, VMware, and (since 17.0.0 Queens) PowerVM. The Bare Metal service also
supports the config drive.
The following virt drivers support the config drive: libvirt, Hyper-V and
VMware. The Bare Metal service also supports the config drive.
- To use config drives with libvirt or VMware, you must first
install the :command:`genisoimage` package on each compute host. Use the
@@ -56,8 +55,8 @@ supports the config drive.
:oslo.config:option:`hyperv.qemu_img_cmd` config option to the full path to an
:command:`qemu-img` command installation.
- To use config drives with PowerVM or the Bare Metal service, you do not need
to prepare anything.
- To use config drives with the Bare Metal service, you do not need to prepare
anything.
.. rubric:: Image requirements
@@ -1,75 +0,0 @@
=======
PowerVM
=======
Introduction
------------
OpenStack Compute supports the PowerVM hypervisor through `NovaLink`_. In the
NovaLink architecture, a thin NovaLink virtual machine running on the Power
system manages virtualization for that system. The ``nova-compute`` service
can be installed on the NovaLink virtual machine and configured to use the
PowerVM compute driver. No external management element (e.g. Hardware
Management Console) is needed.
.. _NovaLink: https://www.ibm.com/support/knowledgecenter/en/POWER8/p8eig/p8eig_kickoff.htm
Configuration
-------------
In order to function properly, the ``nova-compute`` service must be executed
by a member of the ``pvm_admin`` group. Use the ``usermod`` command to add the
user. For example, to add the ``stacker`` user to the ``pvm_admin`` group, execute:
.. code-block:: console
# usermod -a -G pvm_admin stacker
The user must re-login for the change to take effect.
To enable the PowerVM compute driver, configure
:oslo.config:option:`DEFAULT.compute_driver` = ``powervm.PowerVMDriver``. For
example:
.. code-block:: ini
[DEFAULT]
compute_driver = powervm.PowerVMDriver
The PowerVM driver supports two types of storage for ephemeral disks:
``localdisk`` or ``ssp``. If ``localdisk`` is selected, you must specify which
volume group should be used. E.g.:
.. code-block:: ini
[powervm]
disk_driver = localdisk
volume_group_name = openstackvg
.. note::
Using the ``rootvg`` volume group is strongly discouraged since ``rootvg``
is used by the management partition and filling this will cause failures.
The PowerVM driver also supports configuring the default amount of physical
processor compute power (known as "proc units") which will be given to each
vCPU. This value will be used if the requested flavor does not specify the
``powervm:proc_units`` extra-spec. A factor value of 1.0 means a whole physical
processor, whereas 0.05 means 1/20th of a physical processor. E.g.:
.. code-block:: ini
[powervm]
proc_units_factor = 0.1
Volume Support
--------------
Volume support is provided for the PowerVM virt driver via Cinder. Currently,
the only supported volume protocol is `vSCSI`__ Fibre Channel. Attach, detach,
and extend are the operations supported by the PowerVM vSCSI FC volume adapter.
:term:`Boot From Volume` is not yet supported.
.. __: https://www.ibm.com/support/knowledgecenter/en/POWER8/p8hat/p8hat_virtualscsi.htm
@@ -11,7 +11,6 @@ Hypervisors
hypervisor-vmware
hypervisor-hyper-v
hypervisor-virtuozzo
hypervisor-powervm
hypervisor-zvm
hypervisor-ironic
@@ -44,9 +43,6 @@ The following hypervisors are supported:
* `Virtuozzo`_ 7.0.0 and newer - OS Containers and Kernel-based Virtual
Machines supported. The supported formats include ploop and qcow2 images.
* `PowerVM`_ - Server virtualization with IBM PowerVM for AIX, IBM i, and Linux
workloads on the Power Systems platform.
* `zVM`_ - Server virtualization on z Systems and IBM LinuxONE, it can run Linux,
z/OS and more.
@@ -68,8 +64,6 @@ virt drivers:
* :oslo.config:option:`compute_driver` = ``hyperv.HyperVDriver``
* :oslo.config:option:`compute_driver` = ``powervm.PowerVMDriver``
* :oslo.config:option:`compute_driver` = ``zvm.ZVMDriver``
* :oslo.config:option:`compute_driver` = ``fake.FakeDriver``
@@ -83,6 +77,5 @@ virt drivers:
.. _VMware vSphere: https://www.vmware.com/support/vsphere-hypervisor.html
.. _Hyper-V: https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/hyper-v-technology-overview
.. _Virtuozzo: https://www.virtuozzo.com/products/vz7.html
.. _PowerVM: https://www.ibm.com/us-en/marketplace/ibm-powervm
.. _zVM: https://www.ibm.com/it-infrastructure/z/zvm
.. _Ironic: https://docs.openstack.org/ironic/latest/
+1 -1
View File
@@ -406,7 +406,7 @@ Some of attributes that can be used as useful key and their values contains:
* ``free_ram_mb`` (compared with a number, values like ``>= 4096``)
* ``free_disk_mb`` (compared with a number, values like ``>= 10240``)
* ``host`` (compared with a string, values like ``<in> compute``, ``s== compute_01``)
* ``hypervisor_type`` (compared with a string, values like ``s== QEMU``, ``s== powervm``)
* ``hypervisor_type`` (compared with a string, values like ``s== QEMU``, ``s== ironic``)
* ``hypervisor_version`` (compared with a number, values like ``>= 1005003``, ``== 2000000``)
* ``num_instances`` (compared with a number, values like ``<= 10``)
* ``num_io_ops`` (compared with a number, values like ``<= 5``)
-10
View File
@@ -183,16 +183,6 @@ They are only supported by the HyperV virt driver.
.. extra-specs:: os
``powervm``
~~~~~~~~~~~
The following extra specs are used to configure various attributes of
instances when using the PowerVM virt driver.
They are only supported by the PowerVM virt driver.
.. extra-specs:: powervm
``vmware``
~~~~~~~~~~
-21
View File
@@ -34,10 +34,6 @@ link=https://wiki.openstack.org/wiki/ThirdPartySystems/IBM_z/VM_CI
title=Ironic CI
link=
[target.powervm]
title=IBM PowerVM CI
link=https://wiki.openstack.org/wiki/ThirdPartySystems/IBM_PowerVM_CI
#
# Lists all features
#
@@ -70,7 +66,6 @@ driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is i
vmware=complete
hyperv=complete
ironic=unknown
powervm=complete
zvm=complete
[operation.snapshot-server]
@@ -90,7 +85,6 @@ driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is i
vmware=unknown
hyperv=unknown
ironic=unknown
powervm=complete
zvm=complete
[operation.power-ops]
@@ -109,7 +103,6 @@ driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is i
vmware=complete
hyperv=complete
ironic=unknown
powervm=complete
zvm=complete
[operation.rebuild-server]
@@ -128,7 +121,6 @@ driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is i
vmware=complete
hyperv=complete
ironic=unknown
powervm=missing
zvm=missing
[operation.resize-server]
@@ -147,7 +139,6 @@ driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is i
vmware=complete
hyperv=complete
ironic=unknown
powervm=missing
zvm=missing
[operation.server-volume-ops]
@@ -165,9 +156,6 @@ libvirt-virtuozzo-vm=complete
vmware=complete
hyperv=complete
ironic=missing
powervm=complete
driver-notes-powervm=This is not tested for every CI run. Add a
"powervm:volume-check" comment to trigger a CI job running volume tests.
zvm=missing
[operation.server-bdm]
@@ -189,7 +177,6 @@ vmware=partial
driver-notes-vmware=This is not tested in a CI system, but it is implemented.
hyperv=complete:n
ironic=missing
powervm=missing
zvm=missing
[operation.server-neutron]
@@ -211,7 +198,6 @@ driver-notes-vmware=This is not tested in a CI system, but it is implemented.
hyperv=partial
driver-notes-hyperv=This is not tested in a CI system, but it is implemented.
ironic=missing
powervm=complete
zvm=partial
driver-notes-zvm=This is not tested in a CI system, but it is implemented.
@@ -232,7 +218,6 @@ vmware=partial
driver-notes-vmware=This is not tested in a CI system, but it is implemented.
hyperv=complete
ironic=missing
powervm=missing
zvm=complete
[operation.server-suspend]
@@ -252,7 +237,6 @@ driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is i
vmware=complete
hyperv=complete
ironic=missing
powervm=missing
zvm=missing
[operation.server-consoleoutput]
@@ -272,7 +256,6 @@ driver-notes-vmware=This is not tested in a CI system, but it is implemented.
hyperv=partial
driver-notes-hyperv=This is not tested in a CI system, but it is implemented.
ironic=missing
powervm=complete
zvm=complete
[operation.server-rescue]
@@ -293,7 +276,6 @@ vmware=complete
hyperv=partial
driver-notes-hyperv=This is not tested in a CI system, but it is implemented.
ironic=missing
powervm=missing
zvm=missing
[operation.server-configdrive]
@@ -314,7 +296,6 @@ vmware=complete
hyperv=complete
ironic=partial
driver-notes-ironic=This is not tested in a CI system, but it is implemented.
powervm=complete
zvm=complete
[operation.server-changepassword]
@@ -334,7 +315,6 @@ vmware=missing
hyperv=partial
driver-notes-hyperv=This is not tested in a CI system, but it is implemented.
ironic=missing
powervm=missing
zvm=missing
[operation.server-shelve]
@@ -354,5 +334,4 @@ libvirt-virtuozzo-vm=complete
vmware=missing
hyperv=complete
ironic=missing
powervm=complete
zvm=missing
-6
View File
@@ -26,10 +26,6 @@ link=https://wiki.openstack.org/wiki/ThirdPartySystems/Hyper-V_CI
title=Ironic
link=http://docs.openstack.org/infra/manual/developers.html#project-gating
[target.powervm]
title=PowerVM CI
link=https://wiki.openstack.org/wiki/ThirdPartySystems/IBM_PowerVM_CI
[operation.gpu-passthrough]
title=GPU Passthrough
@@ -51,7 +47,6 @@ driver-notes-libvirt-virtuozzo-vm=This is not tested in a CI system, but it is i
vmware=missing
hyperv=missing
ironic=unknown
powervm=missing
[operation.virtual-gpu]
@@ -67,4 +62,3 @@ libvirt-virtuozzo-vm=unknown
vmware=missing
hyperv=missing
ironic=missing
powervm=missing
-77
View File
@@ -104,9 +104,6 @@ title=Hyper-V
[driver.ironic]
title=Ironic
[driver.powervm]
title=PowerVM
[driver.zvm]
title=zVM
@@ -133,9 +130,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=complete
driver-notes.powervm=This is not tested for every CI run. Add a
"powervm:volume-check" comment to trigger a CI job running volume tests.
driver.zvm=missing
[operation.attach-tagged-volume]
@@ -155,7 +149,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.detach-volume]
@@ -174,9 +167,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=complete
driver-notes.powervm=This is not tested for every CI run. Add a
"powervm:volume-check" comment to trigger a CI job running volume tests.
driver.zvm=missing
[operation.extend-volume]
@@ -202,9 +192,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=unknown
driver.libvirt-vz-ct=missing
driver.powervm=complete
driver-notes.powervm=This is not tested for every CI run. Add a
"powervm:volume-check" comment to trigger a CI job running volume tests.
driver.zvm=missing
[operation.attach-interface]
@@ -232,7 +219,6 @@ driver-notes.hyperv=Works without issue if instance is off. When
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=missing
[operation.attach-tagged-interface]
@@ -252,7 +238,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.detach-interface]
@@ -274,7 +259,6 @@ driver-notes.hyperv=Works without issue if instance is off. When
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=missing
[operation.maintenance-mode]
@@ -299,7 +283,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.evacuate]
@@ -325,7 +308,6 @@ driver.hyperv=unknown
driver.ironic=unknown
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=unknown
[operation.rebuild]
@@ -348,7 +330,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=missing
driver.zvm=unknown
[operation.get-guest-info]
@@ -370,7 +351,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=complete
[operation.get-host-uptime]
@@ -390,7 +370,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=complete
[operation.get-host-ip]
@@ -410,7 +389,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=complete
[operation.live-migrate]
@@ -439,7 +417,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=missing
driver.zvm=missing
[operation.force-live-migration-to-complete]
@@ -471,7 +448,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.abort-in-progress-live-migration]
@@ -500,7 +476,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=unknown
driver.libvirt-vz-ct=unknown
driver.powervm=missing
driver.zvm=missing
[operation.launch]
@@ -521,7 +496,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=complete
[operation.pause]
@@ -548,7 +522,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=complete
[operation.reboot]
@@ -571,7 +544,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=complete
[operation.rescue]
@@ -597,7 +569,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=missing
driver.zvm=missing
[operation.resize]
@@ -625,7 +596,6 @@ driver.libvirt-vz-vm=complete
driver-notes.vz-vm=Resizing Virtuozzo instances implies guest filesystem resize also
driver.libvirt-vz-ct=complete
driver-notes.vz-ct=Resizing Virtuozzo instances implies guest filesystem resize also
driver.powervm=missing
driver.zvm=missing
[operation.resume]
@@ -644,7 +614,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=missing
driver.zvm=missing
[operation.set-admin-password]
@@ -677,7 +646,6 @@ driver.libvirt-vz-vm=complete
driver-notes.libvirt-vz-vm=Requires libvirt>=2.0.0
driver.libvirt-vz-ct=complete
driver-notes.libvirt-vz-ct=Requires libvirt>=2.0.0
driver.powervm=missing
driver.zvm=missing
[operation.snapshot]
@@ -705,11 +673,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver-notes.powervm=When using the localdisk disk driver, snapshot is only
supported if I/O is being hosted by the management partition. If hosting I/O
on traditional VIOS, we are limited by the fact that a VSCSI device can't be
mapped to two partitions (the VIOS and the management) at once.
driver.zvm=complete
[operation.suspend]
@@ -742,7 +705,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=missing
driver.zvm=missing
[operation.swap-volume]
@@ -768,7 +730,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.terminate]
@@ -794,7 +755,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=complete
[operation.trigger-crash-dump]
@@ -817,7 +777,6 @@ driver.hyperv=missing
driver.ironic=complete
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.unpause]
@@ -836,7 +795,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=missing
driver.zvm=complete
[guest.disk.autoconfig]
@@ -857,7 +815,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=complete
[guest.disk.rate-limit]
@@ -881,7 +838,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[guest.setup.configdrive]
@@ -909,7 +865,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=complete
driver.zvm=complete
[guest.setup.inject.file]
@@ -936,7 +891,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[guest.setup.inject.networking]
@@ -967,7 +921,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[console.rdp]
@@ -993,7 +946,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[console.serial.log]
@@ -1020,7 +972,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=complete
[console.serial.interactive]
@@ -1048,7 +999,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[console.spice]
@@ -1074,7 +1024,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[console.vnc]
@@ -1100,7 +1049,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=missing
[storage.block]
@@ -1128,9 +1076,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=partial
driver.libvirt-vz-ct=missing
driver.powervm=complete
driver-notes.powervm=This is not tested for every CI run. Add a
"powervm:volume-check" comment to trigger a CI job running volume tests.
driver.zvm=missing
[storage.block.backend.fibrechannel]
@@ -1152,9 +1097,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=complete
driver-notes.powervm=This is not tested for every CI run. Add a
"powervm:volume-check" comment to trigger a CI job running volume tests.
driver.zvm=missing
[storage.block.backend.iscsi]
@@ -1179,7 +1121,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[storage.block.backend.iscsi.auth.chap]
@@ -1201,7 +1142,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[storage.image]
@@ -1225,7 +1165,6 @@ driver.hyperv=complete
driver.ironic=complete
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=complete
driver.zvm=complete
[operation.uefi-boot]
@@ -1247,7 +1186,6 @@ driver.ironic=partial
driver-notes.ironic=depends on hardware support
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.device-tags]
@@ -1277,7 +1215,6 @@ driver.hyperv=complete
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=unknown
driver.powervm=missing
driver.zvm=missing
[operation.quiesce]
@@ -1298,7 +1235,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.unquiesce]
@@ -1317,7 +1253,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.multiattach-volume]
@@ -1341,7 +1276,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.encrypted-volume]
@@ -1371,7 +1305,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=unknown
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.trusted-certs]
@@ -1394,7 +1327,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=missing
driver.zvm=missing
[operation.file-backed-memory]
@@ -1417,7 +1349,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.report-cpu-traits]
@@ -1438,7 +1369,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.port-with-resource-request]
@@ -1461,7 +1391,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.boot-encrypted-vm]
@@ -1487,7 +1416,6 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
[operation.cache-images]
@@ -1510,10 +1438,6 @@ driver.hyperv=partial
driver.ironic=missing
driver.libvirt-vz-vm=complete
driver.libvirt-vz-ct=complete
driver.powervm=partial
driver-notes.powervm=The PowerVM driver does image caching natively when using
the SSP disk driver. It does not use the config options in the [image_cache]
group.
driver.zvm=missing
[operation.boot-emulated-tpm]
@@ -1537,5 +1461,4 @@ driver.hyperv=missing
driver.ironic=missing
driver.libvirt-vz-vm=missing
driver.libvirt-vz-ct=missing
driver.powervm=missing
driver.zvm=missing
-271
View File
@@ -1,271 +0,0 @@
# Copyright 2020 Red Hat, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Validators for ``powervm`` namespaced extra specs.
These were all taken from the IBM documentation.
https://www.ibm.com/support/knowledgecenter/SSXK2N_1.4.4/com.ibm.powervc.standard.help.doc/powervc_pg_flavorsextraspecs_hmc.html
"""
from nova.api.validation.extra_specs import base
# TODO(stephenfin): A lot of these seem to overlap with existing 'hw:' extra
# specs and could be deprecated in favour of those.
EXTRA_SPEC_VALIDATORS = [
base.ExtraSpecValidator(
name='powervm:min_mem',
description=(
'Minimum memory (MB). If you do not specify the value, the value '
'is defaulted to the value for ``memory_mb``.'
),
value={
'type': int,
'min': 256,
'description': 'Integer >=256 divisible by LMB size of the target',
},
),
base.ExtraSpecValidator(
name='powervm:max_mem',
description=(
'Maximum memory (MB). If you do not specify the value, the value '
'is defaulted to the value for ``memory_mb``.'
),
value={
'type': int,
'min': 256,
'description': 'Integer >=256 divisible by LMB size of the target',
},
),
base.ExtraSpecValidator(
name='powervm:min_vcpu',
description=(
'Minimum virtual processors. Minimum resource that is required '
'for LPAR to boot is 1. The maximum value can be equal to the '
'value, which is set to vCPUs. If you specify the value of the '
'attribute, you must also specify value of powervm:max_vcpu. '
'Defaults to value set for vCPUs.'
),
value={
'type': int,
'min': 1,
},
),
base.ExtraSpecValidator(
name='powervm:max_vcpu',
description=(
'Minimum virtual processors. Minimum resource that is required '
'for LPAR to boot is 1. The maximum value can be equal to the '
'value, which is set to vCPUs. If you specify the value of the '
'attribute, you must also specify value of powervm:max_vcpu. '
'Defaults to value set for vCPUs.'
),
value={
'type': int,
'min': 1,
},
),
base.ExtraSpecValidator(
name='powervm:proc_units',
description=(
'The wanted ``proc_units``. The value for the attribute cannot be '
'less than 1/10 of the value that is specified for Virtual '
'CPUs (vCPUs) for hosts with firmware level 7.5 or earlier and '
'1/20 of the value that is specified for vCPUs for hosts with '
'firmware level 7.6 or later. If the value is not specified '
'during deployment, it is defaulted to vCPUs * 0.5.'
),
value={
'type': str,
'pattern': r'\d+\.\d+',
'description': (
'Float (divisible by 0.1 for hosts with firmware level 7.5 or '
'earlier and 0.05 for hosts with firmware level 7.6 or later)'
),
},
),
base.ExtraSpecValidator(
name='powervm:min_proc_units',
description=(
'Minimum ``proc_units``. The minimum value for the attribute is '
'0.1 for hosts with firmware level 7.5 or earlier and 0.05 for '
'hosts with firmware level 7.6 or later. The maximum value must '
'be equal to the maximum value of ``powervm:proc_units``. If you '
'specify the attribute, you must also specify '
'``powervm:proc_units``, ``powervm:max_proc_units``, '
'``powervm:min_vcpu``, `powervm:max_vcpu``, and '
'``powervm:dedicated_proc``. Set the ``powervm:dedicated_proc`` '
'to false.'
'\n'
'The value for the attribute cannot be less than 1/10 of the '
'value that is specified for powervm:min_vcpu for hosts with '
'firmware level 7.5 or earlier and 1/20 of the value that is '
'specified for ``powervm:min_vcpu`` for hosts with firmware '
'level 7.6 or later. If you do not specify the value of the '
'attribute during deployment, it is defaulted to equal the value '
'of ``powervm:proc_units``.'
),
value={
'type': str,
'pattern': r'\d+\.\d+',
'description': (
'Float (divisible by 0.1 for hosts with firmware level 7.5 or '
'earlier and 0.05 for hosts with firmware level 7.6 or later)'
),
},
),
base.ExtraSpecValidator(
name='powervm:max_proc_units',
description=(
'Maximum ``proc_units``. The minimum value can be equal to `` '
'``powervm:proc_units``. The maximum value for the attribute '
'cannot be more than the value of the host for maximum allowed '
'processors per partition. If you specify this attribute, you '
'must also specify ``powervm:proc_units``, '
'``powervm:min_proc_units``, ``powervm:min_vcpu``, '
'``powervm:max_vcpu``, and ``powervm:dedicated_proc``. Set the '
'``powervm:dedicated_proc`` to false.'
'\n'
'The value for the attribute cannot be less than 1/10 of the '
'value that is specified for powervm:max_vcpu for hosts with '
'firmware level 7.5 or earlier and 1/20 of the value that is '
'specified for ``powervm:max_vcpu`` for hosts with firmware '
'level 7.6 or later. If you do not specify the value of the '
'attribute during deployment, the value is defaulted to equal the '
'value of ``powervm:proc_units``.'
),
value={
'type': str,
'pattern': r'\d+\.\d+',
'description': (
'Float (divisible by 0.1 for hosts with firmware level 7.5 or '
'earlier and 0.05 for hosts with firmware level 7.6 or later)'
),
},
),
base.ExtraSpecValidator(
name='powervm:dedicated_proc',
description=(
'Use dedicated processors. The attribute defaults to false.'
),
value={
'type': bool,
},
),
base.ExtraSpecValidator(
name='powervm:shared_weight',
description=(
'Shared processor weight. When ``powervm:dedicated_proc`` is set '
'to true and ``powervm:uncapped`` is also set to true, the value '
'of the attribute defaults to 128.'
),
value={
'type': int,
'min': 0,
'max': 255,
},
),
base.ExtraSpecValidator(
name='powervm:availability_priority',
description=(
'Availability priority. The attribute priority of the server if '
'there is a processor failure and there are not enough resources '
'for all servers. VIOS and i5 need to remain high priority '
'default of 191. The value of the attribute defaults to 128.'
),
value={
'type': int,
'min': 0,
'max': 255,
},
),
base.ExtraSpecValidator(
name='powervm:uncapped',
description=(
'LPAR can use unused processor cycles that are beyond or exceed '
'the wanted setting of the attribute. This attribute is '
'supported only when ``powervm:dedicated_proc`` is set to false. '
'When ``powervm:dedicated_proc`` is set to false, '
'``powervm:uncapped`` defaults to true.'
),
value={
'type': bool,
},
),
base.ExtraSpecValidator(
name='powervm:dedicated_sharing_mode',
description=(
'Sharing mode for dedicated processors. The attribute is '
'supported only when ``powervm:dedicated_proc`` is set to true.'
),
value={
'type': str,
'enum': (
'share_idle_procs',
'keep_idle_procs',
'share_idle_procs_active',
'share_idle_procs_always',
)
},
),
base.ExtraSpecValidator(
name='powervm:processor_compatibility',
description=(
'A processor compatibility mode is a value that is assigned to a '
'logical partition by the hypervisor that specifies the processor '
'environment in which the logical partition can successfully '
'operate.'
),
value={
'type': str,
'enum': (
'default',
'POWER6',
'POWER6+',
'POWER6_Enhanced',
'POWER6+_Enhanced',
'POWER7',
'POWER8'
),
},
),
base.ExtraSpecValidator(
name='powervm:shared_proc_pool_name',
description=(
'Specifies the shared processor pool to be targeted during '
'deployment of a virtual machine.'
),
value={
'type': str,
'description': 'String with upper limit of 14 characters',
},
),
base.ExtraSpecValidator(
name='powervm:srr_capability',
description=(
'If the value of simplified remote restart capability is set to '
'true for the LPAR, you can remote restart the LPAR to supported '
'CEC or host when the source CEC or host is down. The attribute '
'defaults to false.'
),
value={
'type': bool,
},
),
]
def register():
return EXTRA_SPEC_VALIDATORS
-2
View File
@@ -49,7 +49,6 @@ from nova.conf import novnc
from nova.conf import paths
from nova.conf import pci
from nova.conf import placement
from nova.conf import powervm
from nova.conf import quota
from nova.conf import rdp
from nova.conf import rpc
@@ -99,7 +98,6 @@ novnc.register_opts(CONF)
paths.register_opts(CONF)
pci.register_opts(CONF)
placement.register_opts(CONF)
powervm.register_opts(CONF)
quota.register_opts(CONF)
rdp.register_opts(CONF)
rpc.register_opts(CONF)
-1
View File
@@ -41,7 +41,6 @@ Possible values:
* ``ironic.IronicDriver``
* ``vmwareapi.VMwareVCDriver``
* ``hyperv.HyperVDriver``
* ``powervm.PowerVMDriver``
* ``zvm.ZVMDriver``
"""),
cfg.BoolOpt('allow_resize_to_same_host',
-66
View File
@@ -1,66 +0,0 @@
# Copyright 2018 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
powervm_group = cfg.OptGroup(
name="powervm",
title="PowerVM Options",
help="""
PowerVM options allow cloud administrators to configure how OpenStack will work
with the PowerVM hypervisor.
""")
powervm_opts = [
cfg.FloatOpt(
'proc_units_factor',
default=0.1,
min=0.05,
max=1,
help="""
Factor used to calculate the amount of physical processor compute power given
to each vCPU. E.g. A value of 1.0 means a whole physical processor, whereas
0.05 means 1/20th of a physical processor.
"""),
cfg.StrOpt('disk_driver',
choices=['localdisk', 'ssp'], ignore_case=True,
default='localdisk',
help="""
The disk driver to use for PowerVM disks. PowerVM provides support for
localdisk and PowerVM Shared Storage Pool disk drivers.
Related options:
* volume_group_name - required when using localdisk
"""),
cfg.StrOpt('volume_group_name',
default='',
help="""
Volume Group to use for block device operations. If disk_driver is localdisk,
then this attribute must be specified. It is strongly recommended NOT to use
rootvg since that is used by the management partition and filling it will cause
failures.
"""),
]
def register_opts(conf):
conf.register_group(powervm_group)
conf.register_opts(powervm_opts, group=powervm_group)
def list_opts():
return {powervm_group: powervm_opts}
-5
View File
@@ -2140,11 +2140,6 @@ class InvalidPCINUMAAffinity(Invalid):
msg_fmt = _("Invalid PCI NUMA affinity configured: %(policy)s")
class PowerVMAPIFailed(NovaException):
msg_fmt = _("PowerVM API failed to complete for instance=%(inst_name)s. "
"%(reason)s")
class TraitRetrievalFailed(NovaException):
msg_fmt = _("Failed to retrieve traits from the placement API: %(error)s")
-36
View File
@@ -340,42 +340,6 @@ class HyperVLiveMigrateData(LiveMigrateData):
del primitive['is_shared_instance_path']
@obj_base.NovaObjectRegistry.register
class PowerVMLiveMigrateData(LiveMigrateData):
# Version 1.0: Initial version
# Version 1.1: Added the Virtual Ethernet Adapter VLAN mappings.
# Version 1.2: Added old_vol_attachment_ids
# Version 1.3: Added wait_for_vif_plugged
# Version 1.4: Inherited vifs from LiveMigrateData
VERSION = '1.4'
fields = {
'host_mig_data': fields.DictOfNullableStringsField(),
'dest_ip': fields.StringField(),
'dest_user_id': fields.StringField(),
'dest_sys_name': fields.StringField(),
'public_key': fields.StringField(),
'dest_proc_compat': fields.StringField(),
'vol_data': fields.DictOfNullableStringsField(),
'vea_vlan_mappings': fields.DictOfNullableStringsField(),
}
def obj_make_compatible(self, primitive, target_version):
super(PowerVMLiveMigrateData, self).obj_make_compatible(
primitive, target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if target_version < (1, 4) and 'vifs' in primitive:
del primitive['vifs']
if target_version < (1, 3) and 'wait_for_vif_plugged' in primitive:
del primitive['wait_for_vif_plugged']
if target_version < (1, 2):
if 'old_vol_attachment_ids' in primitive:
del primitive['old_vol_attachment_ids']
if target_version < (1, 1):
if 'vea_vlan_mappings' in primitive:
del primitive['vea_vlan_mappings']
@obj_base.NovaObjectRegistry.register
class VMwareLiveMigrateData(LiveMigrateData):
VERSION = '1.0'
+4 -8
View File
@@ -29,7 +29,6 @@ import warnings
import eventlet
import fixtures
import futurist
import mock as mock_the_lib
from openstack import service_description
from oslo_concurrency import lockutils
from oslo_config import cfg
@@ -1609,13 +1608,10 @@ class GenericPoisonFixture(fixtures.Fixture):
for component in components[1:]:
current = getattr(current, component)
# TODO(stephenfin): Remove mock_the_lib check once pypowervm is
# no longer using mock and we no longer have mock in
# requirements
if not isinstance(
getattr(current, attribute),
(mock.Mock, mock_the_lib.Mock),
):
# NOTE(stephenfin): There are a couple of mock libraries in use
# (including mocked versions of mock from oslotest) so we can't
# use isinstance checks here
if 'mock' not in str(type(getattr(current, attribute))):
self.useFixture(fixtures.MonkeyPatch(
meth, poison_configure(meth, why)))
except ImportError:
@@ -28,7 +28,7 @@ class TestValidators(test.NoDBTestCase):
"""
namespaces = {
'accel', 'aggregate_instance_extra_specs', 'capabilities', 'hw',
'hw_rng', 'hw_video', 'os', 'pci_passthrough', 'powervm', 'quota',
'hw_rng', 'hw_video', 'os', 'pci_passthrough', 'quota',
'resources(?P<group>([a-zA-Z0-9_-]{1,64})?)',
'trait(?P<group>([a-zA-Z0-9_-]{1,64})?)', 'vmware',
}
@@ -101,9 +101,7 @@ class TestValidators(test.NoDBTestCase):
valid_specs = (
('hw:numa_nodes', '1'),
('os:monitors', '1'),
('powervm:shared_weight', '1'),
('os:monitors', '8'),
('powervm:shared_weight', '255'),
)
for key, value in valid_specs:
validators.validate(key, value)
@@ -113,9 +111,7 @@ class TestValidators(test.NoDBTestCase):
('hw:serial_port_count', '!'), # NaN
('hw:numa_nodes', '0'), # has min
('os:monitors', '0'), # has min
('powervm:shared_weight', '-1'), # has min
('os:monitors', '9'), # has max
('powervm:shared_weight', '256'), # has max
)
for key, value in invalid_specs:
with testtools.ExpectedException(exception.ValidationError):
+2 -2
View File
@@ -168,7 +168,7 @@ class BaseTestCase(test.TestCase):
'uuid': uuids.fake_compute_node,
'vcpus_used': 0,
'deleted': 0,
'hypervisor_type': 'powervm',
'hypervisor_type': 'libvirt',
'created_at': '2013-04-01T00:27:06.000000',
'local_gb_used': 0,
'updated_at': '2013-04-03T00:35:41.000000',
@@ -178,7 +178,7 @@ class BaseTestCase(test.TestCase):
'current_workload': 0,
'vcpus': 16,
'mapped': 1,
'cpu_info': 'ppc64,powervm,3940',
'cpu_info': 'ppc64,libvirt,3940',
'running_vms': 0,
'free_disk_gb': 259,
'service_id': 7,
@@ -219,67 +219,6 @@ class TestRemoteHyperVLiveMigrateData(test_objects._RemoteTest,
pass
class _TestPowerVMLiveMigrateData(object):
@staticmethod
def _mk_obj():
return migrate_data.PowerVMLiveMigrateData(
host_mig_data=dict(one=2),
dest_ip='1.2.3.4',
dest_user_id='a_user',
dest_sys_name='a_sys',
public_key='a_key',
dest_proc_compat='POWER7',
vol_data=dict(three=4),
vea_vlan_mappings=dict(five=6),
old_vol_attachment_ids=dict(seven=8),
wait_for_vif_plugged=True)
@staticmethod
def _mk_leg():
return {
'host_mig_data': {'one': '2'},
'dest_ip': '1.2.3.4',
'dest_user_id': 'a_user',
'dest_sys_name': 'a_sys',
'public_key': 'a_key',
'dest_proc_compat': 'POWER7',
'vol_data': {'three': '4'},
'vea_vlan_mappings': {'five': '6'},
'old_vol_attachment_ids': {'seven': '8'},
'wait_for_vif_plugged': True
}
def test_migrate_data(self):
obj = self._mk_obj()
self.assertEqual('a_key', obj.public_key)
obj.public_key = 'key2'
self.assertEqual('key2', obj.public_key)
def test_obj_make_compatible(self):
obj = self._mk_obj()
data = lambda x: x['nova_object.data']
primitive = data(obj.obj_to_primitive())
self.assertIn('vea_vlan_mappings', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.0'))
self.assertNotIn('vea_vlan_mappings', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.1'))
self.assertNotIn('old_vol_attachment_ids', primitive)
primitive = data(obj.obj_to_primitive(target_version='1.2'))
self.assertNotIn('wait_for_vif_plugged', primitive)
class TestPowerVMLiveMigrateData(test_objects._LocalTest,
_TestPowerVMLiveMigrateData):
pass
class TestRemotePowerVMLiveMigrateData(test_objects._RemoteTest,
_TestPowerVMLiveMigrateData):
pass
class TestVIFMigrateData(test.NoDBTestCase):
def test_get_dest_vif_source_vif_not_set(self):
-1
View File
@@ -1117,7 +1117,6 @@ object_data = {
'PciDeviceList': '1.3-52ff14355491c8c580bdc0ba34c26210',
'PciDevicePool': '1.1-3f5ddc3ff7bfa14da7f6c7e9904cc000',
'PciDevicePoolList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'PowerVMLiveMigrateData': '1.4-a745f4eda16b45e1bc5686a0c498f27e',
'Quotas': '1.3-3b2b91371f60e788035778fc5f87797d',
'QuotasNoOp': '1.3-d1593cf969c81846bc8192255ea95cce',
'RequestGroup': '1.3-0458d350a8ec9d0673f9be5640a990ce',
-65
View File
@@ -1,65 +0,0 @@
# Copyright 2014, 2017 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
from oslo_utils.fixture import uuidsentinel
from nova.compute import power_state
from nova.compute import vm_states
from nova import objects
try:
import powervm # noqa: F401
except ImportError:
raise unittest.SkipTest(
"The 'pypowervm' dependency is not installed."
)
TEST_FLAVOR = objects.flavor.Flavor(
memory_mb=2048,
swap=0,
vcpu_weight=None,
root_gb=10, id=2,
name=u'm1.small',
ephemeral_gb=0,
rxtx_factor=1.0,
flavorid=uuidsentinel.flav_id,
vcpus=1)
TEST_INSTANCE = objects.Instance(
id=1,
uuid=uuidsentinel.inst_id,
display_name='Fake Instance',
root_gb=10,
ephemeral_gb=0,
instance_type_id=TEST_FLAVOR.id,
system_metadata={'image_os_distro': 'rhel'},
host='host1',
flavor=TEST_FLAVOR,
task_state=None,
vm_state=vm_states.STOPPED,
power_state=power_state.SHUTDOWN,
)
IMAGE1 = {
'id': uuidsentinel.img_id,
'name': 'image1',
'size': 300,
'container_format': 'bare',
'disk_format': 'raw',
'checksum': 'b518a8ba2b152b5607aceb5703fac072',
}
TEST_IMAGE1 = objects.image_meta.ImageMeta.from_dict(IMAGE1)
@@ -1,52 +0,0 @@
# Copyright 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova.virt.powervm.disk import driver as disk_dvr
class FakeDiskAdapter(disk_dvr.DiskAdapter):
"""A fake subclass of DiskAdapter.
This is done so that the abstract methods/properties can be stubbed and the
class can be instantiated for testing.
"""
def _vios_uuids(self):
pass
def _disk_match_func(self, disk_type, instance):
pass
def disconnect_disk_from_mgmt(self, vios_uuid, disk_name):
pass
def capacity(self):
pass
def capacity_used(self):
pass
def detach_disk(self, instance):
pass
def delete_disks(self, storage_elems):
pass
def create_disk_from_image(self, context, instance, image_meta):
pass
def attach_disk(self, instance, disk_info, stg_ftsk):
pass
@@ -1,60 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import fixtures
from pypowervm import const as pvm_const
from nova import test
from nova.tests.unit.virt.powervm.disk import fake_adapter
class TestDiskAdapter(test.NoDBTestCase):
"""Unit Tests for the generic storage driver."""
def setUp(self):
super(TestDiskAdapter, self).setUp()
# Return the mgmt uuid
self.mgmt_uuid = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.mgmt.mgmt_uuid')).mock
self.mgmt_uuid.return_value = 'mp_uuid'
# The values (adapter and host uuid) are not used in the base.
# Default them to None. We use the fake adapter here because we can't
# instantiate DiskAdapter which is an abstract base class.
self.st_adpt = fake_adapter.FakeDiskAdapter(None, None)
@mock.patch("pypowervm.util.sanitize_file_name_for_api")
def test_get_disk_name(self, mock_san):
inst = mock.Mock()
inst.configure_mock(name='a_name_that_is_longer_than_eight',
uuid='01234567-abcd-abcd-abcd-123412341234')
# Long
self.assertEqual(mock_san.return_value,
self.st_adpt._get_disk_name('type', inst))
mock_san.assert_called_with(inst.name, prefix='type_',
max_len=pvm_const.MaxLen.FILENAME_DEFAULT)
mock_san.reset_mock()
# Short
self.assertEqual(mock_san.return_value,
self.st_adpt._get_disk_name('type', inst, short=True))
mock_san.assert_called_with('a_name_t_0123', prefix='t_',
max_len=pvm_const.MaxLen.VDISK_NAME)
@@ -1,313 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import fixtures
from nova import exception
from nova import test
from oslo_utils.fixture import uuidsentinel as uuids
from pypowervm import const as pvm_const
from pypowervm.tasks import storage as tsk_stg
from pypowervm.utils import transaction as pvm_tx
from pypowervm.wrappers import storage as pvm_stg
from pypowervm.wrappers import virtual_io_server as pvm_vios
from nova.virt.powervm.disk import driver as disk_dvr
from nova.virt.powervm.disk import localdisk
class TestLocalDisk(test.NoDBTestCase):
"""Unit Tests for the LocalDisk storage driver."""
def setUp(self):
super(TestLocalDisk, self).setUp()
self.adpt = mock.Mock()
# The mock VIOS needs to have scsi_mappings as a list. Internals are
# set by individual test cases as needed.
smaps = [mock.Mock()]
self.vio_wrap = mock.create_autospec(
pvm_vios.VIOS, instance=True, scsi_mappings=smaps,
uuid='vios-uuid')
# Return the mgmt uuid.
self.mgmt_uuid = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.mgmt.mgmt_uuid', autospec=True)).mock
self.mgmt_uuid.return_value = 'mgmt_uuid'
self.pvm_uuid = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.vm.get_pvm_uuid')).mock
self.pvm_uuid.return_value = 'pvm_uuid'
# Set up for the mocks for the disk adapter.
self.mock_find_vg = self.useFixture(fixtures.MockPatch(
'pypowervm.tasks.storage.find_vg', autospec=True)).mock
self.vg_uuid = uuids.vg_uuid
self.vg = mock.Mock(spec=pvm_stg.VG, uuid=self.vg_uuid)
self.mock_find_vg.return_value = (self.vio_wrap, self.vg)
self.flags(volume_group_name='fakevg', group='powervm')
# Mock the feed tasks.
self.mock_afs = self.useFixture(fixtures.MockPatch(
'pypowervm.utils.transaction.FeedTask.add_functor_subtask',
autospec=True)).mock
self.mock_wtsk = mock.create_autospec(
pvm_tx.WrapperTask, instance=True)
self.mock_wtsk.configure_mock(wrapper=self.vio_wrap)
self.mock_ftsk = mock.create_autospec(pvm_tx.FeedTask, instance=True)
self.mock_ftsk.configure_mock(
wrapper_tasks={'vios-uuid': self.mock_wtsk})
# Create the adapter.
self.ld_adpt = localdisk.LocalStorage(self.adpt, 'host_uuid')
def test_init(self):
# Localdisk adapter already initialized in setUp()
# From super __init__()
self.assertEqual(self.adpt, self.ld_adpt._adapter)
self.assertEqual('host_uuid', self.ld_adpt._host_uuid)
self.assertEqual('mgmt_uuid', self.ld_adpt.mp_uuid)
# From LocalStorage __init__()
self.assertEqual('fakevg', self.ld_adpt.vg_name)
self.mock_find_vg.assert_called_once_with(self.adpt, 'fakevg')
self.assertEqual('vios-uuid', self.ld_adpt._vios_uuid)
self.assertEqual(self.vg_uuid, self.ld_adpt.vg_uuid)
self.assertFalse(self.ld_adpt.capabilities['shared_storage'])
self.assertFalse(self.ld_adpt.capabilities['has_imagecache'])
self.assertFalse(self.ld_adpt.capabilities['snapshot'])
# Assert snapshot capability is true if hosting I/O on mgmt partition.
self.mgmt_uuid.return_value = 'vios-uuid'
self.ld_adpt = localdisk.LocalStorage(self.adpt, 'host_uuid')
self.assertTrue(self.ld_adpt.capabilities['snapshot'])
# Assert volume_group_name is required.
self.flags(volume_group_name=None, group='powervm')
self.assertRaises(exception.OptRequiredIfOtherOptValue,
localdisk.LocalStorage, self.adpt, 'host_uuid')
def test_vios_uuids(self):
self.assertEqual(['vios-uuid'], self.ld_adpt._vios_uuids)
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func', autospec=True)
@mock.patch('nova.virt.powervm.disk.driver.DiskAdapter._get_disk_name')
def test_disk_match_func(self, mock_disk_name, mock_gen_match):
mock_disk_name.return_value = 'disk_name'
func = self.ld_adpt._disk_match_func('disk_type', 'instance')
mock_disk_name.assert_called_once_with(
'disk_type', 'instance', short=True)
mock_gen_match.assert_called_once_with(
pvm_stg.VDisk, names=['disk_name'])
self.assertEqual(mock_gen_match.return_value, func)
@mock.patch('nova.virt.powervm.disk.localdisk.LocalStorage._get_vg_wrap')
def test_capacity(self, mock_vg):
"""Tests the capacity methods."""
mock_vg.return_value = mock.Mock(
capacity='5120', available_size='2048')
self.assertEqual(5120.0, self.ld_adpt.capacity)
self.assertEqual(3072.0, self.ld_adpt.capacity_used)
@mock.patch('pypowervm.tasks.storage.rm_vg_storage', autospec=True)
@mock.patch('nova.virt.powervm.disk.localdisk.LocalStorage._get_vg_wrap')
def test_delete_disks(self, mock_vg, mock_rm_vg):
self.ld_adpt.delete_disks('storage_elems')
mock_vg.assert_called_once_with()
mock_rm_vg.assert_called_once_with(
mock_vg.return_value, vdisks='storage_elems')
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.get')
@mock.patch('pypowervm.tasks.scsi_mapper.remove_maps', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func', autospec=True)
def test_detach_disk(self, mock_match_fn, mock_rm_maps, mock_vios):
mock_match_fn.return_value = 'match_func'
mock_vios.return_value = self.vio_wrap
mock_map1 = mock.Mock(backing_storage='back_stor1')
mock_map2 = mock.Mock(backing_storage='back_stor2')
mock_rm_maps.return_value = [mock_map1, mock_map2]
back_stores = self.ld_adpt.detach_disk('instance')
self.assertEqual(['back_stor1', 'back_stor2'], back_stores)
mock_match_fn.assert_called_once_with(pvm_stg.VDisk)
mock_vios.assert_called_once_with(
self.ld_adpt._adapter, uuid='vios-uuid',
xag=[pvm_const.XAG.VIO_SMAP])
mock_rm_maps.assert_called_with(self.vio_wrap, 'pvm_uuid',
match_func=mock_match_fn.return_value)
mock_vios.return_value.update.assert_called_once()
@mock.patch('pypowervm.tasks.scsi_mapper.remove_vdisk_mapping',
autospec=True)
def test_disconnect_disk_from_mgmt(self, mock_rm_vdisk_map):
self.ld_adpt.disconnect_disk_from_mgmt('vios-uuid', 'disk_name')
mock_rm_vdisk_map.assert_called_with(
self.ld_adpt._adapter, 'vios-uuid', 'mgmt_uuid',
disk_names=['disk_name'])
@mock.patch('nova.virt.powervm.disk.localdisk.LocalStorage._upload_image')
def test_create_disk_from_image(self, mock_upload_image):
mock_image_meta = mock.Mock()
mock_image_meta.size = 30
mock_upload_image.return_value = 'mock_img'
self.ld_adpt.create_disk_from_image(
'context', 'instance', mock_image_meta)
mock_upload_image.assert_called_once_with(
'context', 'instance', mock_image_meta)
@mock.patch('nova.image.glance.API.download')
@mock.patch('nova.virt.powervm.disk.driver.IterableToFileAdapter')
@mock.patch('pypowervm.tasks.storage.upload_new_vdisk')
@mock.patch('nova.virt.powervm.disk.driver.DiskAdapter._get_disk_name')
def test_upload_image(self, mock_name, mock_upload, mock_iter, mock_dl):
mock_meta = mock.Mock(id='1', size=1073741824, disk_format='raw')
mock_upload.return_value = ['mock_img']
mock_img = self.ld_adpt._upload_image('context', 'inst', mock_meta)
self.assertEqual('mock_img', mock_img)
mock_name.assert_called_once_with(
disk_dvr.DiskType.BOOT, 'inst', short=True)
mock_dl.assert_called_once_with('context', '1')
mock_iter.assert_called_once_with(mock_dl.return_value)
mock_upload.assert_called_once_with(
self.adpt, 'vios-uuid', self.vg_uuid, mock_iter.return_value,
mock_name.return_value, 1073741824, d_size=1073741824,
upload_type=tsk_stg.UploadType.IO_STREAM, file_format='raw')
@mock.patch('pypowervm.tasks.scsi_mapper.add_map', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.build_vscsi_mapping',
autospec=True)
def test_attach_disk(self, mock_bldmap, mock_addmap):
def test_afs(add_func):
# Verify the internal add_func
self.assertEqual(mock_addmap.return_value, add_func(self.vio_wrap))
mock_bldmap.assert_called_once_with(
self.ld_adpt._host_uuid, self.vio_wrap, 'pvm_uuid',
'disk_info')
mock_addmap.assert_called_once_with(
self.vio_wrap, mock_bldmap.return_value)
self.mock_wtsk.add_functor_subtask.side_effect = test_afs
self.ld_adpt.attach_disk('instance', 'disk_info', self.mock_ftsk)
self.pvm_uuid.assert_called_once_with('instance')
self.assertEqual(1, self.mock_wtsk.add_functor_subtask.call_count)
@mock.patch('pypowervm.wrappers.storage.VG.get')
def test_get_vg_wrap(self, mock_vg):
vg_wrap = self.ld_adpt._get_vg_wrap()
self.assertEqual(mock_vg.return_value, vg_wrap)
mock_vg.assert_called_once_with(
self.adpt, uuid=self.vg_uuid, parent_type=pvm_vios.VIOS,
parent_uuid='vios-uuid')
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.get')
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps', autospec=True)
@mock.patch('nova.virt.powervm.disk.localdisk.LocalStorage.'
'_disk_match_func')
def test_get_bootdisk_path(self, mock_match_fn, mock_findmaps,
mock_vios):
mock_vios.return_value = self.vio_wrap
# No maps found
mock_findmaps.return_value = None
devname = self.ld_adpt.get_bootdisk_path('inst', 'vios_uuid')
self.pvm_uuid.assert_called_once_with('inst')
mock_match_fn.assert_called_once_with(disk_dvr.DiskType.BOOT, 'inst')
mock_vios.assert_called_once_with(
self.adpt, uuid='vios_uuid', xag=[pvm_const.XAG.VIO_SMAP])
mock_findmaps.assert_called_once_with(
self.vio_wrap.scsi_mappings,
client_lpar_id='pvm_uuid',
match_func=mock_match_fn.return_value)
self.assertIsNone(devname)
# Good map
mock_lu = mock.Mock()
mock_lu.server_adapter.backing_dev_name = 'devname'
mock_findmaps.return_value = [mock_lu]
devname = self.ld_adpt.get_bootdisk_path('inst', 'vios_uuid')
self.assertEqual('devname', devname)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps')
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.get')
@mock.patch('pypowervm.wrappers.storage.VG.get', new=mock.Mock())
def test_get_bootdisk_iter(self, mock_vios, mock_find_maps, mock_lw):
inst, lpar_wrap, vios1 = self._bld_mocks_for_instance_disk()
mock_lw.return_value = lpar_wrap
# Good path
mock_vios.return_value = vios1
for vdisk, vios in self.ld_adpt._get_bootdisk_iter(inst):
self.assertEqual(vios1.scsi_mappings[0].backing_storage, vdisk)
self.assertEqual(vios1.uuid, vios.uuid)
mock_vios.assert_called_once_with(
self.adpt, uuid='vios-uuid', xag=[pvm_const.XAG.VIO_SMAP])
# Not found, no storage of that name.
mock_vios.reset_mock()
mock_find_maps.return_value = []
for vdisk, vios in self.ld_adpt._get_bootdisk_iter(inst):
self.fail('Should not have found any storage elements.')
mock_vios.assert_called_once_with(
self.adpt, uuid='vios-uuid', xag=[pvm_const.XAG.VIO_SMAP])
@mock.patch('nova.virt.powervm.disk.driver.DiskAdapter._get_bootdisk_iter',
autospec=True)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.add_vscsi_mapping', autospec=True)
def test_connect_instance_disk_to_mgmt(self, mock_add, mock_lw, mock_iter):
inst, lpar_wrap, vios1 = self._bld_mocks_for_instance_disk()
mock_lw.return_value = lpar_wrap
# Good path
mock_iter.return_value = [(vios1.scsi_mappings[0].backing_storage,
vios1)]
vdisk, vios = self.ld_adpt.connect_instance_disk_to_mgmt(inst)
self.assertEqual(vios1.scsi_mappings[0].backing_storage, vdisk)
self.assertIs(vios1, vios)
self.assertEqual(1, mock_add.call_count)
mock_add.assert_called_with('host_uuid', vios, 'mgmt_uuid', vdisk)
# add_vscsi_mapping raises. Show-stopper since only one VIOS.
mock_add.reset_mock()
mock_add.side_effect = Exception
self.assertRaises(exception.InstanceDiskMappingFailed,
self.ld_adpt.connect_instance_disk_to_mgmt, inst)
self.assertEqual(1, mock_add.call_count)
# Not found
mock_add.reset_mock()
mock_iter.return_value = []
self.assertRaises(exception.InstanceDiskMappingFailed,
self.ld_adpt.connect_instance_disk_to_mgmt, inst)
self.assertFalse(mock_add.called)
def _bld_mocks_for_instance_disk(self):
inst = mock.Mock()
inst.name = 'Name Of Instance'
inst.uuid = uuids.inst_uuid
lpar_wrap = mock.Mock()
lpar_wrap.id = 2
vios1 = self.vio_wrap
back_stor_name = 'b_Name_Of__' + inst.uuid[:4]
vios1.scsi_mappings[0].backing_storage.name = back_stor_name
return inst, lpar_wrap, vios1
@@ -1,425 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import fixtures
from oslo_utils import uuidutils
from pypowervm import const as pvm_const
from pypowervm import exceptions as pvm_exc
from pypowervm.tasks import storage as tsk_stg
from pypowervm.utils import transaction as pvm_tx
from pypowervm.wrappers import cluster as pvm_clust
from pypowervm.wrappers import storage as pvm_stg
from pypowervm.wrappers import virtual_io_server as pvm_vios
from nova import exception
from nova import test
from nova.tests.unit.virt import powervm
from nova.virt.powervm.disk import ssp as ssp_dvr
from nova.virt.powervm import vm
FAKE_INST_UUID = uuidutils.generate_uuid(dashed=True)
FAKE_INST_UUID_PVM = vm.get_pvm_uuid(mock.Mock(uuid=FAKE_INST_UUID))
class TestSSPDiskAdapter(test.NoDBTestCase):
"""Unit Tests for the LocalDisk storage driver."""
def setUp(self):
super(TestSSPDiskAdapter, self).setUp()
self.inst = powervm.TEST_INSTANCE
self.apt = mock.Mock()
self.host_uuid = 'host_uuid'
self.ssp_wrap = mock.create_autospec(pvm_stg.SSP, instance=True)
# SSP.refresh() returns itself
self.ssp_wrap.refresh.return_value = self.ssp_wrap
self.node1 = mock.create_autospec(pvm_clust.Node, instance=True)
self.node2 = mock.create_autospec(pvm_clust.Node, instance=True)
self.clust_wrap = mock.create_autospec(
pvm_clust.Cluster, instance=True)
self.clust_wrap.nodes = [self.node1, self.node2]
self.clust_wrap.refresh.return_value = self.clust_wrap
self.tier_wrap = mock.create_autospec(pvm_stg.Tier, instance=True)
# Tier.refresh() returns itself
self.tier_wrap.refresh.return_value = self.tier_wrap
self.vio_wrap = mock.create_autospec(pvm_vios.VIOS, instance=True)
# For _cluster
self.mock_clust = self.useFixture(fixtures.MockPatch(
'pypowervm.wrappers.cluster.Cluster', autospec=True)).mock
self.mock_clust.get.return_value = [self.clust_wrap]
# For _ssp
self.mock_ssp_gbhref = self.useFixture(fixtures.MockPatch(
'pypowervm.wrappers.storage.SSP.get_by_href')).mock
self.mock_ssp_gbhref.return_value = self.ssp_wrap
# For _tier
self.mock_get_tier = self.useFixture(fixtures.MockPatch(
'pypowervm.tasks.storage.default_tier_for_ssp',
autospec=True)).mock
self.mock_get_tier.return_value = self.tier_wrap
# A FeedTask
self.mock_wtsk = mock.create_autospec(
pvm_tx.WrapperTask, instance=True)
self.mock_wtsk.configure_mock(wrapper=self.vio_wrap)
self.mock_ftsk = mock.create_autospec(pvm_tx.FeedTask, instance=True)
self.mock_afs = self.mock_ftsk.add_functor_subtask
self.mock_ftsk.configure_mock(
wrapper_tasks={self.vio_wrap.uuid: self.mock_wtsk})
self.pvm_uuid = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.vm.get_pvm_uuid')).mock
# Return the mgmt uuid
self.mgmt_uuid = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.mgmt.mgmt_uuid')).mock
self.mgmt_uuid.return_value = 'mp_uuid'
# The SSP disk adapter
self.ssp_drv = ssp_dvr.SSPDiskAdapter(self.apt, self.host_uuid)
def test_init(self):
self.assertEqual(self.apt, self.ssp_drv._adapter)
self.assertEqual(self.host_uuid, self.ssp_drv._host_uuid)
self.mock_clust.get.assert_called_once_with(self.apt)
self.assertEqual(self.mock_clust.get.return_value,
[self.ssp_drv._clust])
self.mock_ssp_gbhref.assert_called_once_with(
self.apt, self.clust_wrap.ssp_uri)
self.assertEqual(self.mock_ssp_gbhref.return_value, self.ssp_drv._ssp)
self.mock_get_tier.assert_called_once_with(self.ssp_wrap)
self.assertEqual(self.mock_get_tier.return_value, self.ssp_drv._tier)
def test_init_error(self):
# Do these in reverse order to verify we trap all of 'em
for raiser in (self.mock_get_tier, self.mock_ssp_gbhref,
self.mock_clust.get):
raiser.side_effect = pvm_exc.TimeoutError("timed out")
self.assertRaises(exception.NotFound,
ssp_dvr.SSPDiskAdapter, self.apt, self.host_uuid)
raiser.side_effect = ValueError
self.assertRaises(ValueError,
ssp_dvr.SSPDiskAdapter, self.apt, self.host_uuid)
def test_capabilities(self):
self.assertTrue(self.ssp_drv.capabilities.get('shared_storage'))
self.assertFalse(self.ssp_drv.capabilities.get('has_imagecache'))
self.assertTrue(self.ssp_drv.capabilities.get('snapshot'))
@mock.patch('pypowervm.util.get_req_path_uuid', autospec=True)
def test_vios_uuids(self, mock_rpu):
mock_rpu.return_value = self.host_uuid
vios_uuids = self.ssp_drv._vios_uuids
self.assertEqual({self.node1.vios_uuid, self.node2.vios_uuid},
set(vios_uuids))
mock_rpu.assert_has_calls(
[mock.call(node.vios_uri, preserve_case=True, root=True)
for node in [self.node1, self.node2]])
mock_rpu.reset_mock()
# Test VIOSes on other nodes, which won't have uuid or url
node1 = mock.Mock(vios_uuid=None, vios_uri='uri1')
node2 = mock.Mock(vios_uuid='2', vios_uri=None)
# This mock is good and should be returned
node3 = mock.Mock(vios_uuid='3', vios_uri='uri3')
self.clust_wrap.nodes = [node1, node2, node3]
self.assertEqual(['3'], self.ssp_drv._vios_uuids)
# get_req_path_uuid was only called on the good one
mock_rpu.assert_called_once_with('uri3', preserve_case=True, root=True)
def test_capacity(self):
self.tier_wrap.capacity = 10
self.assertAlmostEqual(10.0, self.ssp_drv.capacity)
self.tier_wrap.refresh.assert_called_once_with()
def test_capacity_used(self):
self.ssp_wrap.capacity = 4.56
self.ssp_wrap.free_space = 1.23
self.assertAlmostEqual((4.56 - 1.23), self.ssp_drv.capacity_used)
self.ssp_wrap.refresh.assert_called_once_with()
@mock.patch('pypowervm.tasks.cluster_ssp.get_or_upload_image_lu',
autospec=True)
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter._vios_uuids',
new_callable=mock.PropertyMock)
@mock.patch('pypowervm.util.sanitize_file_name_for_api', autospec=True)
@mock.patch('pypowervm.tasks.storage.crt_lu', autospec=True)
@mock.patch('nova.image.glance.API.download')
@mock.patch('nova.virt.powervm.disk.driver.IterableToFileAdapter',
autospec=True)
def test_create_disk_from_image(self, mock_it2f, mock_dl, mock_crt_lu,
mock_san, mock_vuuid, mock_goru):
img = powervm.TEST_IMAGE1
mock_crt_lu.return_value = self.ssp_drv._ssp, 'boot_lu'
mock_san.return_value = 'disk_name'
mock_vuuid.return_value = ['vuuid']
self.assertEqual('boot_lu', self.ssp_drv.create_disk_from_image(
'context', self.inst, img))
mock_dl.assert_called_once_with('context', img.id)
mock_san.assert_has_calls([
mock.call(img.name, prefix='image_', suffix='_' + img.checksum),
mock.call(self.inst.name, prefix='boot_')])
mock_it2f.assert_called_once_with(mock_dl.return_value)
mock_goru.assert_called_once_with(
self.ssp_drv._tier, 'disk_name', 'vuuid',
mock_it2f.return_value, img.size,
upload_type=tsk_stg.UploadType.IO_STREAM)
mock_crt_lu.assert_called_once_with(
self.mock_get_tier.return_value, mock_san.return_value,
self.inst.flavor.root_gb, typ=pvm_stg.LUType.DISK,
clone=mock_goru.return_value)
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter._vios_uuids',
new_callable=mock.PropertyMock)
@mock.patch('pypowervm.tasks.scsi_mapper.add_map', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.build_vscsi_mapping',
autospec=True)
@mock.patch('pypowervm.wrappers.storage.LU', autospec=True)
def test_connect_disk(self, mock_lu, mock_bldmap, mock_addmap,
mock_vio_uuids):
disk_info = mock.Mock()
disk_info.configure_mock(name='dname', udid='dudid')
mock_vio_uuids.return_value = [self.vio_wrap.uuid]
def test_afs(add_func):
# Verify the internal add_func
self.assertEqual(mock_addmap.return_value, add_func(self.vio_wrap))
mock_bldmap.assert_called_once_with(
self.host_uuid, self.vio_wrap, self.pvm_uuid.return_value,
mock_lu.bld_ref.return_value)
mock_addmap.assert_called_once_with(
self.vio_wrap, mock_bldmap.return_value)
self.mock_wtsk.add_functor_subtask.side_effect = test_afs
self.ssp_drv.attach_disk(self.inst, disk_info, self.mock_ftsk)
mock_lu.bld_ref.assert_called_once_with(self.apt, 'dname', 'dudid')
self.pvm_uuid.assert_called_once_with(self.inst)
self.assertEqual(1, self.mock_wtsk.add_functor_subtask.call_count)
@mock.patch('pypowervm.tasks.storage.rm_tier_storage', autospec=True)
def test_delete_disks(self, mock_rm_tstor):
self.ssp_drv.delete_disks(['disk1', 'disk2'])
mock_rm_tstor.assert_called_once_with(['disk1', 'disk2'],
tier=self.ssp_drv._tier)
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter._vios_uuids',
new_callable=mock.PropertyMock)
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.remove_maps', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func', autospec=True)
@mock.patch('pypowervm.tasks.partition.build_active_vio_feed_task',
autospec=True)
def test_disconnect_disk(self, mock_bld_ftsk, mock_gmf, mock_rmmaps,
mock_findmaps, mock_vio_uuids):
mock_vio_uuids.return_value = [self.vio_wrap.uuid]
mock_bld_ftsk.return_value = self.mock_ftsk
lu1, lu2 = [mock.create_autospec(pvm_stg.LU, instance=True)] * 2
# Two mappings have the same LU, to verify set behavior
mock_findmaps.return_value = [
mock.Mock(spec=pvm_vios.VSCSIMapping, backing_storage=lu)
for lu in (lu1, lu2, lu1)]
def test_afs(rm_func):
# verify the internal rm_func
self.assertEqual(mock_rmmaps.return_value, rm_func(self.vio_wrap))
mock_rmmaps.assert_called_once_with(
self.vio_wrap, self.pvm_uuid.return_value,
match_func=mock_gmf.return_value)
self.mock_wtsk.add_functor_subtask.side_effect = test_afs
self.assertEqual(
{lu1, lu2}, set(self.ssp_drv.detach_disk(self.inst)))
mock_bld_ftsk.assert_called_once_with(
self.apt, name='ssp', xag=[pvm_const.XAG.VIO_SMAP])
self.pvm_uuid.assert_called_once_with(self.inst)
mock_gmf.assert_called_once_with(pvm_stg.LU)
self.assertEqual(1, self.mock_wtsk.add_functor_subtask.call_count)
mock_findmaps.assert_called_once_with(
self.vio_wrap.scsi_mappings,
client_lpar_id=self.pvm_uuid.return_value,
match_func=mock_gmf.return_value)
self.mock_ftsk.execute.assert_called_once_with()
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.get')
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps', autospec=True)
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter._disk_match_func')
def test_get_bootdisk_path(self, mock_match_fn, mock_findmaps,
mock_vios):
mock_vios.return_value = self.vio_wrap
# No maps found
mock_findmaps.return_value = None
devname = self.ssp_drv.get_bootdisk_path('inst', 'vios_uuid')
mock_vios.assert_called_once_with(
self.apt, uuid='vios_uuid', xag=[pvm_const.XAG.VIO_SMAP])
mock_findmaps.assert_called_once_with(
self.vio_wrap.scsi_mappings,
client_lpar_id=self.pvm_uuid.return_value,
match_func=mock_match_fn.return_value)
self.assertIsNone(devname)
# Good map
mock_lu = mock.Mock()
mock_lu.server_adapter.backing_dev_name = 'devname'
mock_findmaps.return_value = [mock_lu]
devname = self.ssp_drv.get_bootdisk_path('inst', 'vios_uuid')
self.assertEqual('devname', devname)
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter.'
'_vios_uuids', new_callable=mock.PropertyMock)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper', autospec=True)
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.get')
@mock.patch('pypowervm.tasks.scsi_mapper.add_vscsi_mapping', autospec=True)
def test_connect_instance_disk_to_mgmt(self, mock_add, mock_vio_get,
mock_lw, mock_vio_uuids):
inst, lpar_wrap, vio1, vio2, vio3 = self._bld_mocks_for_instance_disk()
mock_lw.return_value = lpar_wrap
mock_vio_uuids.return_value = [1, 2]
# Test with two VIOSes, both of which contain the mapping
mock_vio_get.side_effect = [vio1, vio2]
lu, vios = self.ssp_drv.connect_instance_disk_to_mgmt(inst)
self.assertEqual('lu_udid', lu.udid)
# Should hit on the first VIOS
self.assertIs(vio1, vios)
mock_add.assert_called_once_with(self.host_uuid, vio1, 'mp_uuid', lu)
# Now the first VIOS doesn't have the mapping, but the second does
mock_add.reset_mock()
mock_vio_get.side_effect = [vio3, vio2]
lu, vios = self.ssp_drv.connect_instance_disk_to_mgmt(inst)
self.assertEqual('lu_udid', lu.udid)
# Should hit on the second VIOS
self.assertIs(vio2, vios)
self.assertEqual(1, mock_add.call_count)
mock_add.assert_called_once_with(self.host_uuid, vio2, 'mp_uuid', lu)
# No hits
mock_add.reset_mock()
mock_vio_get.side_effect = [vio3, vio3]
self.assertRaises(exception.InstanceDiskMappingFailed,
self.ssp_drv.connect_instance_disk_to_mgmt, inst)
self.assertEqual(0, mock_add.call_count)
# First add_vscsi_mapping call raises
mock_vio_get.side_effect = [vio1, vio2]
mock_add.side_effect = [Exception("mapping failed"), None]
# Should hit on the second VIOS
self.assertIs(vio2, vios)
@mock.patch('pypowervm.tasks.scsi_mapper.remove_lu_mapping', autospec=True)
def test_disconnect_disk_from_mgmt(self, mock_rm_lu_map):
self.ssp_drv.disconnect_disk_from_mgmt('vios_uuid', 'disk_name')
mock_rm_lu_map.assert_called_with(self.apt, 'vios_uuid',
'mp_uuid', disk_names=['disk_name'])
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func', autospec=True)
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter._get_disk_name')
def test_disk_match_func(self, mock_disk_name, mock_gen_match):
mock_disk_name.return_value = 'disk_name'
self.ssp_drv._disk_match_func('disk_type', 'instance')
mock_disk_name.assert_called_once_with('disk_type', 'instance')
mock_gen_match.assert_called_with(pvm_stg.LU, names=['disk_name'])
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter.'
'_vios_uuids', new_callable=mock.PropertyMock)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper', autospec=True)
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.get')
def test_get_bootdisk_iter(self, mock_vio_get, mock_lw, mock_vio_uuids):
inst, lpar_wrap, vio1, vio2, vio3 = self._bld_mocks_for_instance_disk()
mock_lw.return_value = lpar_wrap
mock_vio_uuids.return_value = [1, 2]
# Test with two VIOSes, both of which contain the mapping. Force the
# method to get the lpar_wrap.
mock_vio_get.side_effect = [vio1, vio2]
idi = self.ssp_drv._get_bootdisk_iter(inst)
lu, vios = next(idi)
self.assertEqual('lu_udid', lu.udid)
self.assertEqual('vios1', vios.name)
mock_vio_get.assert_called_once_with(self.apt, uuid=1,
xag=[pvm_const.XAG.VIO_SMAP])
lu, vios = next(idi)
self.assertEqual('lu_udid', lu.udid)
self.assertEqual('vios2', vios.name)
mock_vio_get.assert_called_with(self.apt, uuid=2,
xag=[pvm_const.XAG.VIO_SMAP])
self.assertRaises(StopIteration, next, idi)
self.assertEqual(2, mock_vio_get.call_count)
mock_lw.assert_called_once_with(self.apt, inst)
# Same, but prove that breaking out of the loop early avoids the second
# get call. Supply lpar_wrap from here on, and prove no calls to
# get_instance_wrapper
mock_vio_get.reset_mock()
mock_lw.reset_mock()
mock_vio_get.side_effect = [vio1, vio2]
for lu, vios in self.ssp_drv._get_bootdisk_iter(inst):
self.assertEqual('lu_udid', lu.udid)
self.assertEqual('vios1', vios.name)
break
mock_vio_get.assert_called_once_with(self.apt, uuid=1,
xag=[pvm_const.XAG.VIO_SMAP])
# Now the first VIOS doesn't have the mapping, but the second does
mock_vio_get.reset_mock()
mock_vio_get.side_effect = [vio3, vio2]
idi = self.ssp_drv._get_bootdisk_iter(inst)
lu, vios = next(idi)
self.assertEqual('lu_udid', lu.udid)
self.assertEqual('vios2', vios.name)
mock_vio_get.assert_has_calls(
[mock.call(self.apt, uuid=uuid, xag=[pvm_const.XAG.VIO_SMAP])
for uuid in (1, 2)])
self.assertRaises(StopIteration, next, idi)
self.assertEqual(2, mock_vio_get.call_count)
# No hits
mock_vio_get.reset_mock()
mock_vio_get.side_effect = [vio3, vio3]
self.assertEqual([], list(self.ssp_drv._get_bootdisk_iter(inst)))
self.assertEqual(2, mock_vio_get.call_count)
def _bld_mocks_for_instance_disk(self):
inst = mock.Mock()
inst.name = 'my-instance-name'
lpar_wrap = mock.Mock()
lpar_wrap.id = 4
lu_wrap = mock.Mock(spec=pvm_stg.LU)
lu_wrap.configure_mock(name='boot_my_instance_name', udid='lu_udid')
smap = mock.Mock(backing_storage=lu_wrap,
server_adapter=mock.Mock(lpar_id=4))
# Build mock VIOS Wrappers as the returns from VIOS.wrap.
# vios1 and vios2 will both have the mapping for client ID 4 and LU
# named boot_my_instance_name.
smaps = [mock.Mock(), mock.Mock(), mock.Mock(), smap]
vios1 = mock.Mock(spec=pvm_vios.VIOS)
vios1.configure_mock(name='vios1', uuid='uuid1', scsi_mappings=smaps)
vios2 = mock.Mock(spec=pvm_vios.VIOS)
vios2.configure_mock(name='vios2', uuid='uuid2', scsi_mappings=smaps)
# vios3 will not have the mapping
vios3 = mock.Mock(spec=pvm_vios.VIOS)
vios3.configure_mock(name='vios3', uuid='uuid3',
scsi_mappings=[mock.Mock(), mock.Mock()])
return inst, lpar_wrap, vios1, vios2, vios3
@@ -1,68 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from nova import test
from nova.virt.powervm.tasks import image as tsk_img
class TestImage(test.TestCase):
def test_update_task_state(self):
def func(task_state, expected_state='delirious'):
self.assertEqual('task_state', task_state)
self.assertEqual('delirious', expected_state)
tf = tsk_img.UpdateTaskState(func, 'task_state')
self.assertEqual('update_task_state_task_state', tf.name)
tf.execute()
def func2(task_state, expected_state=None):
self.assertEqual('task_state', task_state)
self.assertEqual('expected_state', expected_state)
tf = tsk_img.UpdateTaskState(func2, 'task_state',
expected_state='expected_state')
tf.execute()
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tsk_img.UpdateTaskState(func, 'task_state')
tf.assert_called_once_with(
name='update_task_state_task_state')
@mock.patch('nova.virt.powervm.image.stream_blockdev_to_glance',
autospec=True)
@mock.patch('nova.virt.powervm.image.generate_snapshot_metadata',
autospec=True)
def test_stream_to_glance(self, mock_metadata, mock_stream):
mock_metadata.return_value = 'metadata'
mock_inst = mock.Mock()
mock_inst.name = 'instance_name'
tf = tsk_img.StreamToGlance('context', 'image_api', 'image_id',
mock_inst)
self.assertEqual('stream_to_glance', tf.name)
tf.execute('disk_path')
mock_metadata.assert_called_with('context', 'image_api', 'image_id',
mock_inst)
mock_stream.assert_called_with('context', 'image_api', 'image_id',
'metadata', 'disk_path')
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tsk_img.StreamToGlance(
'context', 'image_api', 'image_id', mock_inst)
tf.assert_called_once_with(
name='stream_to_glance', requires='disk_path')
@@ -1,323 +0,0 @@
# Copyright 2015, 2017 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from unittest import mock
import eventlet
from pypowervm.wrappers import network as pvm_net
from nova import exception
from nova import test
from nova.tests.unit.virt import powervm
from nova.virt.powervm.tasks import network as tf_net
def cna(mac):
"""Builds a mock Client Network Adapter for unit tests."""
return mock.MagicMock(mac=mac, vswitch_uri='fake_href')
class TestNetwork(test.NoDBTestCase):
def setUp(self):
super(TestNetwork, self).setUp()
self.flags(host='host1')
self.apt = mock.Mock()
self.mock_lpar_wrap = mock.MagicMock()
self.mock_lpar_wrap.can_modify_io.return_value = True, None
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
@mock.patch('nova.virt.powervm.vif.unplug')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_unplug_vifs(self, mock_vm_get, mock_unplug, mock_get_wrap):
"""Tests that a delete of the vif can be done."""
inst = powervm.TEST_INSTANCE
# Mock up the CNA responses.
cnas = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11'), cna('AABBCCDDEE22')]
mock_vm_get.return_value = cnas
# Mock up the network info. This also validates that they will be
# sanitized to upper case.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff'}, {'address': 'aa:bb:cc:dd:ee:22'},
{'address': 'aa:bb:cc:dd:ee:33'}
]
# Mock out the instance wrapper
mock_get_wrap.return_value = self.mock_lpar_wrap
# Mock out the vif driver
def validate_unplug(adapter, instance, vif, cna_w_list=None):
self.assertEqual(adapter, self.apt)
self.assertEqual(instance, inst)
self.assertIn(vif, net_info)
self.assertEqual(cna_w_list, cnas)
mock_unplug.side_effect = validate_unplug
# Run method
p_vifs = tf_net.UnplugVifs(self.apt, inst, net_info)
p_vifs.execute()
# Make sure the unplug was invoked, so that we know that the validation
# code was called
self.assertEqual(3, mock_unplug.call_count)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_net.UnplugVifs(self.apt, inst, net_info)
tf.assert_called_once_with(name='unplug_vifs')
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
def test_unplug_vifs_invalid_state(self, mock_get_wrap):
"""Tests that the delete raises an exception if bad VM state."""
inst = powervm.TEST_INSTANCE
# Mock out the instance wrapper
mock_get_wrap.return_value = self.mock_lpar_wrap
# Mock that the state is incorrect
self.mock_lpar_wrap.can_modify_io.return_value = False, 'bad'
# Run method
p_vifs = tf_net.UnplugVifs(self.apt, inst, mock.Mock())
self.assertRaises(exception.VirtualInterfaceUnplugException,
p_vifs.execute)
@mock.patch('nova.virt.powervm.vif.plug')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_plug_vifs_rmc(self, mock_cna_get, mock_plug):
"""Tests that a crt vif can be done with secure RMC."""
inst = powervm.TEST_INSTANCE
# Mock up the CNA response. One should already exist, the other
# should not.
pre_cnas = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11')]
mock_cna_get.return_value = copy.deepcopy(pre_cnas)
# Mock up the network info. This also validates that they will be
# sanitized to upper case.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:22', 'vnic_type': 'normal'},
]
# First run the CNA update, then the CNA create.
mock_new_cna = mock.Mock(spec=pvm_net.CNA)
mock_plug.side_effect = ['upd_cna', mock_new_cna]
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info)
all_cnas = p_vifs.execute(self.mock_lpar_wrap)
# new vif should be created twice.
mock_plug.assert_any_call(self.apt, inst, net_info[0], new_vif=False)
mock_plug.assert_any_call(self.apt, inst, net_info[1], new_vif=True)
# The Task provides the list of original CNAs plus only CNAs that were
# created.
self.assertEqual(pre_cnas + [mock_new_cna], all_cnas)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info)
tf.assert_called_once_with(
name='plug_vifs', provides='vm_cnas', requires=['lpar_wrap'])
@mock.patch('nova.virt.powervm.vif.plug')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_plug_vifs_rmc_no_create(self, mock_vm_get, mock_plug):
"""Verifies if no creates are needed, none are done."""
inst = powervm.TEST_INSTANCE
# Mock up the CNA response. Both should already exist.
mock_vm_get.return_value = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11')]
# Mock up the network info. This also validates that they will be
# sanitized to upper case. This also validates that we don't call
# get_vnics if no nets have vnic_type 'direct'.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:11', 'vnic_type': 'normal'}
]
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info)
p_vifs.execute(self.mock_lpar_wrap)
# The create should have been called with new_vif as False.
mock_plug.assert_any_call(self.apt, inst, net_info[0], new_vif=False)
mock_plug.assert_any_call(self.apt, inst, net_info[1], new_vif=False)
@mock.patch('nova.virt.powervm.vif.plug')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_plug_vifs_invalid_state(self, mock_vm_get, mock_plug):
"""Tests that a crt_vif fails when the LPAR state is bad."""
inst = powervm.TEST_INSTANCE
# Mock up the CNA response. Only doing one for simplicity
mock_vm_get.return_value = []
net_info = [{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'}]
# Mock that the state is incorrect
self.mock_lpar_wrap.can_modify_io.return_value = False, 'bad'
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info)
self.assertRaises(exception.VirtualInterfaceCreateException,
p_vifs.execute, self.mock_lpar_wrap)
# The create should not have been invoked
self.assertEqual(0, mock_plug.call_count)
@mock.patch('nova.virt.powervm.vif.plug')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_plug_vifs_timeout(self, mock_vm_get, mock_plug):
"""Tests that crt vif failure via loss of neutron callback."""
inst = powervm.TEST_INSTANCE
# Mock up the CNA response. Only doing one for simplicity
mock_vm_get.return_value = [cna('AABBCCDDEE11')]
# Mock up the network info.
net_info = [{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'}]
# Ensure that an exception is raised by a timeout.
mock_plug.side_effect = eventlet.timeout.Timeout()
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info)
self.assertRaises(exception.VirtualInterfaceCreateException,
p_vifs.execute, self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(1, mock_plug.call_count)
@mock.patch('nova.virt.powervm.vif.unplug')
@mock.patch('nova.virt.powervm.vif.plug')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_plug_vifs_revert(self, mock_vm_get, mock_plug, mock_unplug):
"""Tests that the revert flow works properly."""
inst = powervm.TEST_INSTANCE
# Fake CNA list. The one pre-existing VIF should *not* get reverted.
cna_list = [cna('AABBCCDDEEFF'), cna('FFEEDDCCBBAA')]
mock_vm_get.return_value = cna_list
# Mock up the network info. Three roll backs.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:22', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:33', 'vnic_type': 'normal'}
]
# Make sure we test raising an exception
mock_unplug.side_effect = [exception.NovaException(), None]
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info)
p_vifs.execute(self.mock_lpar_wrap)
p_vifs.revert(self.mock_lpar_wrap, mock.Mock(), mock.Mock())
# The unplug should be called twice. The exception shouldn't stop the
# second call.
self.assertEqual(2, mock_unplug.call_count)
# Make sure each call is invoked correctly. The first plug was not a
# new vif, so it should not be reverted.
c2 = mock.call(self.apt, inst, net_info[1], cna_w_list=cna_list)
c3 = mock.call(self.apt, inst, net_info[2], cna_w_list=cna_list)
mock_unplug.assert_has_calls([c2, c3])
@mock.patch('pypowervm.tasks.cna.crt_cna')
@mock.patch('pypowervm.wrappers.network.VSwitch.search')
@mock.patch('nova.virt.powervm.vif.plug')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_plug_mgmt_vif(self, mock_vm_get, mock_plug, mock_vs_search,
mock_crt_cna):
"""Tests that a mgmt vif can be created."""
inst = powervm.TEST_INSTANCE
# Mock up the rmc vswitch
vswitch_w = mock.MagicMock()
vswitch_w.href = 'fake_mgmt_uri'
mock_vs_search.return_value = [vswitch_w]
# Run method such that it triggers a fresh CNA search
p_vifs = tf_net.PlugMgmtVif(self.apt, inst)
p_vifs.execute(None)
# With the default get_cnas mock (which returns a Mock()), we think we
# found an existing management CNA.
mock_crt_cna.assert_not_called()
mock_vm_get.assert_called_once_with(
self.apt, inst, vswitch_uri='fake_mgmt_uri')
# Now mock get_cnas to return no hits
mock_vm_get.reset_mock()
mock_vm_get.return_value = []
p_vifs.execute(None)
# Get was called; and since it didn't have the mgmt CNA, so was plug.
self.assertEqual(1, mock_crt_cna.call_count)
mock_vm_get.assert_called_once_with(
self.apt, inst, vswitch_uri='fake_mgmt_uri')
# Now pass CNAs, but not the mgmt vif, "from PlugVifs"
cnas = [mock.Mock(vswitch_uri='uri1'), mock.Mock(vswitch_uri='uri2')]
mock_crt_cna.reset_mock()
mock_vm_get.reset_mock()
p_vifs.execute(cnas)
# Get wasn't called, since the CNAs were passed "from PlugVifs"; but
# since the mgmt vif wasn't included, plug was called.
mock_vm_get.assert_not_called()
mock_crt_cna.assert_called()
# Finally, pass CNAs including the mgmt.
cnas.append(mock.Mock(vswitch_uri='fake_mgmt_uri'))
mock_crt_cna.reset_mock()
p_vifs.execute(cnas)
# Neither get nor plug was called.
mock_vm_get.assert_not_called()
mock_crt_cna.assert_not_called()
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_net.PlugMgmtVif(self.apt, inst)
tf.assert_called_once_with(
name='plug_mgmt_vif', provides='mgmt_cna', requires=['vm_cnas'])
def test_get_vif_events(self):
# Set up common mocks.
inst = powervm.TEST_INSTANCE
net_info = [mock.MagicMock(), mock.MagicMock()]
net_info[0]['id'] = 'a'
net_info[0].get.return_value = False
net_info[1]['id'] = 'b'
net_info[1].get.return_value = True
# Set up the runner.
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info)
p_vifs.crt_network_infos = net_info
resp = p_vifs._get_vif_events()
# Only one should be returned since only one was active.
self.assertEqual(1, len(resp))
@@ -1,355 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import fixtures
from pypowervm import exceptions as pvm_exc
from nova import exception
from nova import test
from nova.virt.powervm.tasks import storage as tf_stg
class TestStorage(test.NoDBTestCase):
def setUp(self):
super(TestStorage, self).setUp()
self.adapter = mock.Mock()
self.disk_dvr = mock.MagicMock()
self.mock_cfg_drv = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.media.ConfigDrivePowerVM')).mock
self.mock_mb = self.mock_cfg_drv.return_value
self.instance = mock.MagicMock()
self.context = 'context'
def test_create_and_connect_cfg_drive(self):
# With a specified FeedTask
task = tf_stg.CreateAndConnectCfgDrive(
self.adapter, self.instance, 'injected_files',
'network_info', 'stg_ftsk', admin_pass='admin_pass')
task.execute('mgmt_cna')
self.mock_cfg_drv.assert_called_once_with(self.adapter)
self.mock_mb.create_cfg_drv_vopt.assert_called_once_with(
self.instance, 'injected_files', 'network_info', 'stg_ftsk',
admin_pass='admin_pass', mgmt_cna='mgmt_cna')
# Normal revert
task.revert('mgmt_cna', 'result', 'flow_failures')
self.mock_mb.dlt_vopt.assert_called_once_with(self.instance,
'stg_ftsk')
self.mock_mb.reset_mock()
# Revert when dlt_vopt fails
self.mock_mb.dlt_vopt.side_effect = pvm_exc.Error('fake-exc')
task.revert('mgmt_cna', 'result', 'flow_failures')
self.mock_mb.dlt_vopt.assert_called_once()
self.mock_mb.reset_mock()
# Revert when media builder not created
task.mb = None
task.revert('mgmt_cna', 'result', 'flow_failures')
self.mock_mb.assert_not_called()
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.CreateAndConnectCfgDrive(
self.adapter, self.instance, 'injected_files',
'network_info', 'stg_ftsk', admin_pass='admin_pass')
tf.assert_called_once_with(name='cfg_drive', requires=['mgmt_cna'])
def test_delete_vopt(self):
# Test with no FeedTask
task = tf_stg.DeleteVOpt(self.adapter, self.instance)
task.execute()
self.mock_cfg_drv.assert_called_once_with(self.adapter)
self.mock_mb.dlt_vopt.assert_called_once_with(
self.instance, stg_ftsk=None)
self.mock_cfg_drv.reset_mock()
self.mock_mb.reset_mock()
# With a specified FeedTask
task = tf_stg.DeleteVOpt(self.adapter, self.instance, stg_ftsk='ftsk')
task.execute()
self.mock_cfg_drv.assert_called_once_with(self.adapter)
self.mock_mb.dlt_vopt.assert_called_once_with(
self.instance, stg_ftsk='ftsk')
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.DeleteVOpt(self.adapter, self.instance)
tf.assert_called_once_with(name='vopt_delete')
def test_delete_disk(self):
stor_adpt_mappings = mock.Mock()
task = tf_stg.DeleteDisk(self.disk_dvr)
task.execute(stor_adpt_mappings)
self.disk_dvr.delete_disks.assert_called_once_with(stor_adpt_mappings)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.DeleteDisk(self.disk_dvr)
tf.assert_called_once_with(
name='delete_disk', requires=['stor_adpt_mappings'])
def test_detach_disk(self):
task = tf_stg.DetachDisk(self.disk_dvr, self.instance)
task.execute()
self.disk_dvr.detach_disk.assert_called_once_with(self.instance)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.DetachDisk(self.disk_dvr, self.instance)
tf.assert_called_once_with(
name='detach_disk', provides='stor_adpt_mappings')
def test_attach_disk(self):
stg_ftsk = mock.Mock()
disk_dev_info = mock.Mock()
task = tf_stg.AttachDisk(self.disk_dvr, self.instance, stg_ftsk)
task.execute(disk_dev_info)
self.disk_dvr.attach_disk.assert_called_once_with(
self.instance, disk_dev_info, stg_ftsk)
task.revert(disk_dev_info, 'result', 'flow failures')
self.disk_dvr.detach_disk.assert_called_once_with(self.instance)
self.disk_dvr.detach_disk.reset_mock()
# Revert failures are not raised
self.disk_dvr.detach_disk.side_effect = pvm_exc.TimeoutError(
"timed out")
task.revert(disk_dev_info, 'result', 'flow failures')
self.disk_dvr.detach_disk.assert_called_once_with(self.instance)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.AttachDisk(self.disk_dvr, self.instance, stg_ftsk)
tf.assert_called_once_with(
name='attach_disk', requires=['disk_dev_info'])
def test_create_disk_for_img(self):
image_meta = mock.Mock()
task = tf_stg.CreateDiskForImg(
self.disk_dvr, self.context, self.instance, image_meta)
task.execute()
self.disk_dvr.create_disk_from_image.assert_called_once_with(
self.context, self.instance, image_meta)
task.revert('result', 'flow failures')
self.disk_dvr.delete_disks.assert_called_once_with(['result'])
self.disk_dvr.delete_disks.reset_mock()
# Delete not called if no result
task.revert(None, None)
self.disk_dvr.delete_disks.assert_not_called()
# Delete exception doesn't raise
self.disk_dvr.delete_disks.side_effect = pvm_exc.TimeoutError(
"timed out")
task.revert('result', 'flow failures')
self.disk_dvr.delete_disks.assert_called_once_with(['result'])
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.CreateDiskForImg(
self.disk_dvr, self.context, self.instance, image_meta)
tf.assert_called_once_with(
name='create_disk_from_img', provides='disk_dev_info')
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps', autospec=True)
@mock.patch('nova.virt.powervm.mgmt.discover_vscsi_disk', autospec=True)
@mock.patch('nova.virt.powervm.mgmt.remove_block_dev', autospec=True)
def test_instance_disk_to_mgmt(self, mock_rm, mock_discover, mock_find):
mock_discover.return_value = '/dev/disk'
mock_instance = mock.Mock()
mock_instance.name = 'instance_name'
mock_stg = mock.Mock()
mock_stg.name = 'stg_name'
mock_vwrap = mock.Mock()
mock_vwrap.name = 'vios_name'
mock_vwrap.uuid = 'vios_uuid'
mock_vwrap.scsi_mappings = ['mapping1']
disk_dvr = mock.MagicMock()
disk_dvr.mp_uuid = 'mp_uuid'
disk_dvr.connect_instance_disk_to_mgmt.return_value = (mock_stg,
mock_vwrap)
def reset_mocks():
mock_find.reset_mock()
mock_discover.reset_mock()
mock_rm.reset_mock()
disk_dvr.reset_mock()
# Good path - find_maps returns one result
mock_find.return_value = ['one_mapping']
tf = tf_stg.InstanceDiskToMgmt(disk_dvr, mock_instance)
self.assertEqual('instance_disk_to_mgmt', tf.name)
self.assertEqual((mock_stg, mock_vwrap, '/dev/disk'), tf.execute())
disk_dvr.connect_instance_disk_to_mgmt.assert_called_with(
mock_instance)
mock_find.assert_called_with(['mapping1'], client_lpar_id='mp_uuid',
stg_elem=mock_stg)
mock_discover.assert_called_with('one_mapping')
tf.revert('result', 'failures')
disk_dvr.disconnect_disk_from_mgmt.assert_called_with('vios_uuid',
'stg_name')
mock_rm.assert_called_with('/dev/disk')
# Good path - find_maps returns >1 result
reset_mocks()
mock_find.return_value = ['first_mapping', 'second_mapping']
tf = tf_stg.InstanceDiskToMgmt(disk_dvr, mock_instance)
self.assertEqual((mock_stg, mock_vwrap, '/dev/disk'), tf.execute())
disk_dvr.connect_instance_disk_to_mgmt.assert_called_with(
mock_instance)
mock_find.assert_called_with(['mapping1'], client_lpar_id='mp_uuid',
stg_elem=mock_stg)
mock_discover.assert_called_with('first_mapping')
tf.revert('result', 'failures')
disk_dvr.disconnect_disk_from_mgmt.assert_called_with('vios_uuid',
'stg_name')
mock_rm.assert_called_with('/dev/disk')
# Management Partition is VIOS and NovaLink hosted storage
reset_mocks()
disk_dvr._vios_uuids = ['mp_uuid']
dev_name = '/dev/vg/fake_name'
disk_dvr.get_bootdisk_path.return_value = dev_name
tf = tf_stg.InstanceDiskToMgmt(disk_dvr, mock_instance)
self.assertEqual((None, None, dev_name), tf.execute())
# Management Partition is VIOS and not NovaLink hosted storage
reset_mocks()
disk_dvr._vios_uuids = ['mp_uuid']
disk_dvr.get_bootdisk_path.return_value = None
tf = tf_stg.InstanceDiskToMgmt(disk_dvr, mock_instance)
tf.execute()
disk_dvr.connect_instance_disk_to_mgmt.assert_called_with(
mock_instance)
# Bad path - find_maps returns no results
reset_mocks()
mock_find.return_value = []
tf = tf_stg.InstanceDiskToMgmt(disk_dvr, mock_instance)
self.assertRaises(exception.NewMgmtMappingNotFoundException,
tf.execute)
disk_dvr.connect_instance_disk_to_mgmt.assert_called_with(
mock_instance)
# find_maps was still called
mock_find.assert_called_with(['mapping1'], client_lpar_id='mp_uuid',
stg_elem=mock_stg)
# discover_vscsi_disk didn't get called
self.assertEqual(0, mock_discover.call_count)
tf.revert('result', 'failures')
# disconnect_disk_from_mgmt got called
disk_dvr.disconnect_disk_from_mgmt.assert_called_with('vios_uuid',
'stg_name')
# ...but remove_block_dev did not.
self.assertEqual(0, mock_rm.call_count)
# Bad path - connect raises
reset_mocks()
disk_dvr.connect_instance_disk_to_mgmt.side_effect = (
exception.InstanceDiskMappingFailed(instance_name='inst_name'))
tf = tf_stg.InstanceDiskToMgmt(disk_dvr, mock_instance)
self.assertRaises(exception.InstanceDiskMappingFailed, tf.execute)
disk_dvr.connect_instance_disk_to_mgmt.assert_called_with(
mock_instance)
self.assertEqual(0, mock_find.call_count)
self.assertEqual(0, mock_discover.call_count)
# revert shouldn't call disconnect or remove
tf.revert('result', 'failures')
self.assertEqual(0, disk_dvr.disconnect_disk_from_mgmt.call_count)
self.assertEqual(0, mock_rm.call_count)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.InstanceDiskToMgmt(disk_dvr, mock_instance)
tf.assert_called_once_with(
name='instance_disk_to_mgmt',
provides=['stg_elem', 'vios_wrap', 'disk_path'])
@mock.patch('nova.virt.powervm.mgmt.remove_block_dev', autospec=True)
def test_remove_instance_disk_from_mgmt(self, mock_rm):
disk_dvr = mock.MagicMock()
mock_instance = mock.Mock()
mock_instance.name = 'instance_name'
mock_stg = mock.Mock()
mock_stg.name = 'stg_name'
mock_vwrap = mock.Mock()
mock_vwrap.name = 'vios_name'
mock_vwrap.uuid = 'vios_uuid'
tf = tf_stg.RemoveInstanceDiskFromMgmt(disk_dvr, mock_instance)
self.assertEqual('remove_inst_disk_from_mgmt', tf.name)
# Boot disk not mapped to mgmt partition
tf.execute(None, mock_vwrap, '/dev/disk')
self.assertEqual(disk_dvr.disconnect_disk_from_mgmt.call_count, 0)
self.assertEqual(mock_rm.call_count, 0)
# Boot disk mapped to mgmt partition
tf.execute(mock_stg, mock_vwrap, '/dev/disk')
disk_dvr.disconnect_disk_from_mgmt.assert_called_with('vios_uuid',
'stg_name')
mock_rm.assert_called_with('/dev/disk')
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.RemoveInstanceDiskFromMgmt(disk_dvr, mock_instance)
tf.assert_called_once_with(
name='remove_inst_disk_from_mgmt',
requires=['stg_elem', 'vios_wrap', 'disk_path'])
def test_attach_volume(self):
vol_dvr = mock.Mock(connection_info={'data': {'volume_id': '1'}})
task = tf_stg.AttachVolume(vol_dvr)
task.execute()
vol_dvr.attach_volume.assert_called_once_with()
task.revert('result', 'flow failures')
vol_dvr.reset_stg_ftsk.assert_called_once_with()
vol_dvr.detach_volume.assert_called_once_with()
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.AttachVolume(vol_dvr)
tf.assert_called_once_with(name='attach_vol_1')
def test_detach_volume(self):
vol_dvr = mock.Mock(connection_info={'data': {'volume_id': '1'}})
task = tf_stg.DetachVolume(vol_dvr)
task.execute()
vol_dvr.detach_volume.assert_called_once_with()
task.revert('result', 'flow failures')
vol_dvr.reset_stg_ftsk.assert_called_once_with()
vol_dvr.detach_volume.assert_called_once_with()
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_stg.DetachVolume(vol_dvr)
tf.assert_called_once_with(name='detach_vol_1')
@@ -1,134 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from taskflow import engines as tf_eng
from taskflow.patterns import linear_flow as tf_lf
from taskflow import task as tf_tsk
from unittest import mock
from nova import exception
from nova import test
from nova.virt.powervm.tasks import vm as tf_vm
class TestVMTasks(test.NoDBTestCase):
def setUp(self):
super(TestVMTasks, self).setUp()
self.apt = mock.Mock()
self.instance = mock.Mock()
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper', autospec=True)
def test_get(self, mock_get_wrap):
get = tf_vm.Get(self.apt, self.instance)
get.execute()
mock_get_wrap.assert_called_once_with(self.apt, self.instance)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_vm.Get(self.apt, self.instance)
tf.assert_called_once_with(name='get_vm', provides='lpar_wrap')
@mock.patch('pypowervm.tasks.storage.add_lpar_storage_scrub_tasks',
autospec=True)
@mock.patch('nova.virt.powervm.vm.create_lpar')
def test_create(self, mock_vm_crt, mock_stg):
lpar_entry = mock.Mock()
# Test create with normal (non-recreate) ftsk
crt = tf_vm.Create(self.apt, 'host_wrapper', self.instance, 'ftsk')
mock_vm_crt.return_value = lpar_entry
crt.execute()
mock_vm_crt.assert_called_once_with(self.apt, 'host_wrapper',
self.instance)
mock_stg.assert_called_once_with(
[lpar_entry.id], 'ftsk', lpars_exist=True)
mock_stg.assert_called_once_with([mock_vm_crt.return_value.id], 'ftsk',
lpars_exist=True)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_vm.Create(self.apt, 'host_wrapper', self.instance, 'ftsk')
tf.assert_called_once_with(name='crt_vm', provides='lpar_wrap')
@mock.patch('nova.virt.powervm.vm.power_on')
def test_power_on(self, mock_pwron):
pwron = tf_vm.PowerOn(self.apt, self.instance)
pwron.execute()
mock_pwron.assert_called_once_with(self.apt, self.instance)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_vm.PowerOn(self.apt, self.instance)
tf.assert_called_once_with(name='pwr_vm')
@mock.patch('nova.virt.powervm.vm.power_on')
@mock.patch('nova.virt.powervm.vm.power_off')
def test_power_on_revert(self, mock_pwroff, mock_pwron):
flow = tf_lf.Flow('revert_power_on')
pwron = tf_vm.PowerOn(self.apt, self.instance)
flow.add(pwron)
# Dummy Task that fails, triggering flow revert
def failure(*a, **k):
raise ValueError()
flow.add(tf_tsk.FunctorTask(failure))
# When PowerOn.execute doesn't fail, revert calls power_off
self.assertRaises(ValueError, tf_eng.run, flow)
mock_pwron.assert_called_once_with(self.apt, self.instance)
mock_pwroff.assert_called_once_with(self.apt, self.instance,
force_immediate=True)
mock_pwron.reset_mock()
mock_pwroff.reset_mock()
# When PowerOn.execute fails, revert doesn't call power_off
mock_pwron.side_effect = exception.NovaException()
self.assertRaises(exception.NovaException, tf_eng.run, flow)
mock_pwron.assert_called_once_with(self.apt, self.instance)
mock_pwroff.assert_not_called()
@mock.patch('nova.virt.powervm.vm.power_off')
def test_power_off(self, mock_pwroff):
# Default force_immediate
pwroff = tf_vm.PowerOff(self.apt, self.instance)
pwroff.execute()
mock_pwroff.assert_called_once_with(self.apt, self.instance,
force_immediate=False)
mock_pwroff.reset_mock()
# Explicit force_immediate
pwroff = tf_vm.PowerOff(self.apt, self.instance, force_immediate=True)
pwroff.execute()
mock_pwroff.assert_called_once_with(self.apt, self.instance,
force_immediate=True)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_vm.PowerOff(self.apt, self.instance)
tf.assert_called_once_with(name='pwr_off_vm')
@mock.patch('nova.virt.powervm.vm.delete_lpar')
def test_delete(self, mock_dlt):
delete = tf_vm.Delete(self.apt, self.instance)
delete.execute()
mock_dlt.assert_called_once_with(self.apt, self.instance)
# Validate args on taskflow.task.Task instantiation
with mock.patch('taskflow.task.Task.__init__') as tf:
tf_vm.Delete(self.apt, self.instance)
tf.assert_called_once_with(name='dlt_vm')
-649
View File
@@ -1,649 +0,0 @@
# Copyright 2016, 2018 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
from unittest import mock
import fixtures
from oslo_serialization import jsonutils
from oslo_utils.fixture import uuidsentinel as uuids
from pypowervm import const as pvm_const
from pypowervm import exceptions as pvm_exc
from pypowervm.helpers import log_helper as pvm_hlp_log
from pypowervm.helpers import vios_busy as pvm_hlp_vbusy
from pypowervm.utils import transaction as pvm_tx
from pypowervm.wrappers import virtual_io_server as pvm_vios
from nova import block_device as nova_block_device
from nova.compute import provider_tree
from nova import conf as cfg
from nova import exception
from nova.objects import block_device as bdmobj
from nova import test
from nova.tests.unit.virt import powervm
from nova.virt import block_device as nova_virt_bdm
from nova.virt import driver as nova_driver
from nova.virt.driver import ComputeDriver
from nova.virt import hardware
from nova.virt.powervm.disk import ssp
from nova.virt.powervm import driver
CONF = cfg.CONF
class TestPowerVMDriver(test.NoDBTestCase):
def setUp(self):
super(TestPowerVMDriver, self).setUp()
self.drv = driver.PowerVMDriver('virtapi')
self.adp = self.useFixture(fixtures.MockPatch(
'pypowervm.adapter.Adapter', autospec=True)).mock
self.drv.adapter = self.adp
self.sess = self.useFixture(fixtures.MockPatch(
'pypowervm.adapter.Session', autospec=True)).mock
self.pwron = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.vm.power_on')).mock
self.pwroff = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.vm.power_off')).mock
# Create an instance to test with
self.inst = powervm.TEST_INSTANCE
def test_driver_capabilities(self):
"""Test the driver capabilities."""
# check that the driver reports all capabilities
self.assertEqual(set(ComputeDriver.capabilities),
set(self.drv.capabilities))
# check the values for each capability
self.assertFalse(self.drv.capabilities['has_imagecache'])
self.assertFalse(self.drv.capabilities['supports_evacuate'])
self.assertFalse(
self.drv.capabilities['supports_migrate_to_same_host'])
self.assertTrue(self.drv.capabilities['supports_attach_interface'])
self.assertFalse(self.drv.capabilities['supports_device_tagging'])
self.assertFalse(
self.drv.capabilities['supports_tagged_attach_interface'])
self.assertFalse(
self.drv.capabilities['supports_tagged_attach_volume'])
self.assertTrue(self.drv.capabilities['supports_extend_volume'])
self.assertFalse(self.drv.capabilities['supports_multiattach'])
@mock.patch('nova.image.glance.API')
@mock.patch('pypowervm.tasks.storage.ComprehensiveScrub', autospec=True)
@mock.patch('oslo_utils.importutils.import_object_ns', autospec=True)
@mock.patch('pypowervm.wrappers.managed_system.System', autospec=True)
@mock.patch('pypowervm.tasks.partition.validate_vios_ready', autospec=True)
def test_init_host(self, mock_vvr, mock_sys, mock_import, mock_scrub,
mock_img):
mock_hostw = mock.Mock(uuid='uuid')
mock_sys.get.return_value = [mock_hostw]
self.drv.init_host('host')
self.sess.assert_called_once_with(conn_tries=60)
self.adp.assert_called_once_with(
self.sess.return_value, helpers=[
pvm_hlp_log.log_helper, pvm_hlp_vbusy.vios_busy_retry_helper])
mock_vvr.assert_called_once_with(self.drv.adapter)
mock_sys.get.assert_called_once_with(self.drv.adapter)
self.assertEqual(mock_hostw, self.drv.host_wrapper)
mock_scrub.assert_called_once_with(self.drv.adapter)
mock_scrub.return_value.execute.assert_called_once_with()
mock_import.assert_called_once_with(
'nova.virt.powervm.disk', 'localdisk.LocalStorage',
self.drv.adapter, 'uuid')
self.assertEqual(mock_import.return_value, self.drv.disk_dvr)
mock_img.assert_called_once_with()
self.assertEqual(mock_img.return_value, self.drv.image_api)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova.virt.powervm.vm.get_vm_qp')
@mock.patch('nova.virt.powervm.vm._translate_vm_state')
def test_get_info(self, mock_tx_state, mock_qp, mock_uuid):
mock_tx_state.return_value = 'fake-state'
self.assertEqual(hardware.InstanceInfo('fake-state'),
self.drv.get_info('inst'))
mock_uuid.assert_called_once_with('inst')
mock_qp.assert_called_once_with(
self.drv.adapter, mock_uuid.return_value, 'PartitionState')
mock_tx_state.assert_called_once_with(mock_qp.return_value)
@mock.patch('nova.virt.powervm.vm.get_lpar_names')
def test_list_instances(self, mock_names):
mock_names.return_value = ['one', 'two', 'three']
self.assertEqual(['one', 'two', 'three'], self.drv.list_instances())
mock_names.assert_called_once_with(self.adp)
def test_get_available_nodes(self):
self.flags(host='hostname')
self.assertEqual(['hostname'], self.drv.get_available_nodes('node'))
@mock.patch('pypowervm.wrappers.managed_system.System', autospec=True)
@mock.patch('nova.virt.powervm.host.build_host_resource_from_ms')
def test_get_available_resource(self, mock_bhrfm, mock_sys):
mock_sys.get.return_value = ['sys']
mock_bhrfm.return_value = {'foo': 'bar'}
self.drv.disk_dvr = mock.create_autospec(ssp.SSPDiskAdapter,
instance=True)
self.assertEqual(
{'foo': 'bar', 'local_gb': self.drv.disk_dvr.capacity,
'local_gb_used': self.drv.disk_dvr.capacity_used},
self.drv.get_available_resource('node'))
mock_sys.get.assert_called_once_with(self.adp)
mock_bhrfm.assert_called_once_with('sys')
self.assertEqual('sys', self.drv.host_wrapper)
@contextlib.contextmanager
def _update_provider_tree(self, allocations=None):
"""Host resource dict gets converted properly to provider tree inv."""
with mock.patch('nova.virt.powervm.host.'
'build_host_resource_from_ms') as mock_bhrfm:
mock_bhrfm.return_value = {
'vcpus': 8,
'memory_mb': 2048,
}
self.drv.host_wrapper = 'host_wrapper'
# Validate that this gets converted to int with floor
self.drv.disk_dvr = mock.Mock(capacity=2091.8)
exp_inv = {
'VCPU': {
'total': 8,
'max_unit': 8,
'allocation_ratio': 16.0,
'reserved': 0,
},
'MEMORY_MB': {
'total': 2048,
'max_unit': 2048,
'allocation_ratio': 1.5,
'reserved': 512,
},
'DISK_GB': {
'total': 2091,
'max_unit': 2091,
'allocation_ratio': 1.0,
'reserved': 0,
},
}
ptree = provider_tree.ProviderTree()
ptree.new_root('compute_host', uuids.cn)
# Let the caller muck with these
yield ptree, exp_inv
self.drv.update_provider_tree(ptree, 'compute_host',
allocations=allocations)
self.assertEqual(exp_inv, ptree.data('compute_host').inventory)
mock_bhrfm.assert_called_once_with('host_wrapper')
def test_update_provider_tree(self):
# Basic: no inventory already on the provider, no extra providers, no
# aggregates or traits.
with self._update_provider_tree():
pass
def test_update_provider_tree_ignore_allocations(self):
with self._update_provider_tree(allocations="This is ignored"):
pass
def test_update_provider_tree_conf_overrides(self):
# Non-default CONF values for allocation ratios and reserved.
self.flags(cpu_allocation_ratio=12.3,
reserved_host_cpus=4,
ram_allocation_ratio=4.5,
reserved_host_memory_mb=32,
disk_allocation_ratio=6.7,
# This gets int(ceil)'d
reserved_host_disk_mb=5432.1)
with self._update_provider_tree() as (_, exp_inv):
exp_inv['VCPU']['allocation_ratio'] = 12.3
exp_inv['VCPU']['reserved'] = 4
exp_inv['MEMORY_MB']['allocation_ratio'] = 4.5
exp_inv['MEMORY_MB']['reserved'] = 32
exp_inv['DISK_GB']['allocation_ratio'] = 6.7
exp_inv['DISK_GB']['reserved'] = 6
def test_update_provider_tree_complex_ptree(self):
# Overrides inventory already on the provider; leaves other providers
# and aggregates/traits alone.
with self._update_provider_tree() as (ptree, exp_inv):
ptree.update_inventory('compute_host', {
# these should get blown away
'VCPU': {
'total': 16,
'max_unit': 2,
'allocation_ratio': 1.0,
'reserved': 10,
},
'CUSTOM_BOGUS': {
'total': 1234,
}
})
ptree.update_aggregates('compute_host',
[uuids.ss_agg, uuids.other_agg])
ptree.update_traits('compute_host', ['CUSTOM_FOO', 'CUSTOM_BAR'])
ptree.new_root('ssp', uuids.ssp)
ptree.update_inventory('ssp', {'sentinel': 'inventory',
'for': 'ssp'})
ptree.update_aggregates('ssp', [uuids.ss_agg])
ptree.new_child('sriov', 'compute_host', uuid=uuids.sriov)
# Since CONF.cpu_allocation_ratio is not set and this is not
# the initial upt call (so CONF.initial_cpu_allocation_ratio would
# be used), the existing allocation ratio value from the tree is
# used.
exp_inv['VCPU']['allocation_ratio'] = 1.0
# Make sure the compute's agg and traits were left alone
cndata = ptree.data('compute_host')
self.assertEqual(set([uuids.ss_agg, uuids.other_agg]),
cndata.aggregates)
self.assertEqual(set(['CUSTOM_FOO', 'CUSTOM_BAR']), cndata.traits)
# And the other providers were left alone
self.assertEqual(set([uuids.cn, uuids.ssp, uuids.sriov]),
set(ptree.get_provider_uuids()))
# ...including the ssp's aggregates
self.assertEqual(set([uuids.ss_agg]), ptree.data('ssp').aggregates)
@mock.patch('nova.virt.powervm.tasks.storage.AttachVolume.execute')
@mock.patch('nova.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.powervm.media.ConfigDrivePowerVM')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.virt.powervm.vm.create_lpar')
@mock.patch('pypowervm.tasks.partition.build_active_vio_feed_task',
autospec=True)
@mock.patch('pypowervm.tasks.storage.add_lpar_storage_scrub_tasks',
autospec=True)
def test_spawn_ops(self, mock_scrub, mock_bldftsk, mock_crt_lpar,
mock_cdrb, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_attach_vol):
"""Validates the 'typical' spawn flow of the spawn of an instance. """
mock_cdrb.return_value = True
self.drv.host_wrapper = mock.Mock()
self.drv.disk_dvr = mock.create_autospec(ssp.SSPDiskAdapter,
instance=True)
mock_ftsk = pvm_tx.FeedTask('fake', [mock.Mock(spec=pvm_vios.VIOS)])
mock_bldftsk.return_value = mock_ftsk
block_device_info = self._fake_bdms()
self.drv.spawn('context', self.inst, 'img_meta', 'files', 'password',
'allocs', network_info='netinfo',
block_device_info=block_device_info)
mock_crt_lpar.assert_called_once_with(
self.adp, self.drv.host_wrapper, self.inst)
mock_bldftsk.assert_called_once_with(
self.adp, xag={pvm_const.XAG.VIO_SMAP, pvm_const.XAG.VIO_FMAP})
self.assertTrue(mock_plug_vifs.called)
self.assertTrue(mock_plug_mgmt_vif.called)
mock_scrub.assert_called_once_with(
[mock_crt_lpar.return_value.id], mock_ftsk, lpars_exist=True)
self.drv.disk_dvr.create_disk_from_image.assert_called_once_with(
'context', self.inst, 'img_meta')
self.drv.disk_dvr.attach_disk.assert_called_once_with(
self.inst, self.drv.disk_dvr.create_disk_from_image.return_value,
mock_ftsk)
self.assertEqual(2, mock_attach_vol.call_count)
mock_cfg_drv.assert_called_once_with(self.adp)
mock_cfg_drv.return_value.create_cfg_drv_vopt.assert_called_once_with(
self.inst, 'files', 'netinfo', mock_ftsk, admin_pass='password',
mgmt_cna=mock.ANY)
self.pwron.assert_called_once_with(self.adp, self.inst)
mock_cfg_drv.reset_mock()
mock_attach_vol.reset_mock()
# No config drive, no bdms
mock_cdrb.return_value = False
self.drv.spawn('context', self.inst, 'img_meta', 'files', 'password',
'allocs')
mock_cfg_drv.assert_not_called()
mock_attach_vol.assert_not_called()
@mock.patch('nova.virt.powervm.tasks.storage.DetachVolume.execute')
@mock.patch('nova.virt.powervm.tasks.network.UnplugVifs.execute')
@mock.patch('nova.virt.powervm.vm.delete_lpar')
@mock.patch('nova.virt.powervm.media.ConfigDrivePowerVM')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('pypowervm.tasks.partition.build_active_vio_feed_task',
autospec=True)
def test_destroy(self, mock_bldftsk, mock_cdrb, mock_cfgdrv,
mock_dlt_lpar, mock_unplug, mock_detach_vol):
"""Validates PowerVM destroy."""
self.drv.host_wrapper = mock.Mock()
self.drv.disk_dvr = mock.create_autospec(ssp.SSPDiskAdapter,
instance=True)
mock_ftsk = pvm_tx.FeedTask('fake', [mock.Mock(spec=pvm_vios.VIOS)])
mock_bldftsk.return_value = mock_ftsk
block_device_info = self._fake_bdms()
# Good path, with config drive, destroy disks
mock_cdrb.return_value = True
self.drv.destroy('context', self.inst, [],
block_device_info=block_device_info)
self.pwroff.assert_called_once_with(
self.adp, self.inst, force_immediate=True)
mock_bldftsk.assert_called_once_with(
self.adp, xag=[pvm_const.XAG.VIO_SMAP])
mock_unplug.assert_called_once()
mock_cdrb.assert_called_once_with(self.inst)
mock_cfgdrv.assert_called_once_with(self.adp)
mock_cfgdrv.return_value.dlt_vopt.assert_called_once_with(
self.inst, stg_ftsk=mock_bldftsk.return_value)
self.assertEqual(2, mock_detach_vol.call_count)
self.drv.disk_dvr.detach_disk.assert_called_once_with(
self.inst)
self.drv.disk_dvr.delete_disks.assert_called_once_with(
self.drv.disk_dvr.detach_disk.return_value)
mock_dlt_lpar.assert_called_once_with(self.adp, self.inst)
self.pwroff.reset_mock()
mock_bldftsk.reset_mock()
mock_unplug.reset_mock()
mock_cdrb.reset_mock()
mock_cfgdrv.reset_mock()
self.drv.disk_dvr.detach_disk.reset_mock()
self.drv.disk_dvr.delete_disks.reset_mock()
mock_detach_vol.reset_mock()
mock_dlt_lpar.reset_mock()
# No config drive, preserve disks, no block device info
mock_cdrb.return_value = False
self.drv.destroy('context', self.inst, [], block_device_info={},
destroy_disks=False)
mock_cfgdrv.return_value.dlt_vopt.assert_not_called()
mock_detach_vol.assert_not_called()
self.drv.disk_dvr.delete_disks.assert_not_called()
# Non-forced power_off, since preserving disks
self.pwroff.assert_called_once_with(
self.adp, self.inst, force_immediate=False)
mock_bldftsk.assert_called_once_with(
self.adp, xag=[pvm_const.XAG.VIO_SMAP])
mock_unplug.assert_called_once()
mock_cdrb.assert_called_once_with(self.inst)
mock_cfgdrv.assert_not_called()
mock_cfgdrv.return_value.dlt_vopt.assert_not_called()
self.drv.disk_dvr.detach_disk.assert_called_once_with(
self.inst)
self.drv.disk_dvr.delete_disks.assert_not_called()
mock_dlt_lpar.assert_called_once_with(self.adp, self.inst)
self.pwroff.reset_mock()
mock_bldftsk.reset_mock()
mock_unplug.reset_mock()
mock_cdrb.reset_mock()
mock_cfgdrv.reset_mock()
self.drv.disk_dvr.detach_disk.reset_mock()
self.drv.disk_dvr.delete_disks.reset_mock()
mock_dlt_lpar.reset_mock()
# InstanceNotFound exception, non-forced
self.pwroff.side_effect = exception.InstanceNotFound(
instance_id='something')
self.drv.destroy('context', self.inst, [], block_device_info={},
destroy_disks=False)
self.pwroff.assert_called_once_with(
self.adp, self.inst, force_immediate=False)
self.drv.disk_dvr.detach_disk.assert_not_called()
mock_unplug.assert_not_called()
self.drv.disk_dvr.delete_disks.assert_not_called()
mock_dlt_lpar.assert_not_called()
self.pwroff.reset_mock()
self.pwroff.side_effect = None
mock_unplug.reset_mock()
# Convertible (PowerVM) exception
mock_dlt_lpar.side_effect = pvm_exc.TimeoutError("Timed out")
self.assertRaises(exception.InstanceTerminationFailure,
self.drv.destroy, 'context', self.inst, [],
block_device_info={})
# Everything got called
self.pwroff.assert_called_once_with(
self.adp, self.inst, force_immediate=True)
mock_unplug.assert_called_once()
self.drv.disk_dvr.detach_disk.assert_called_once_with(self.inst)
self.drv.disk_dvr.delete_disks.assert_called_once_with(
self.drv.disk_dvr.detach_disk.return_value)
mock_dlt_lpar.assert_called_once_with(self.adp, self.inst)
# Other random exception raises directly
mock_dlt_lpar.side_effect = ValueError()
self.assertRaises(ValueError,
self.drv.destroy, 'context', self.inst, [],
block_device_info={})
@mock.patch('nova.virt.powervm.tasks.image.UpdateTaskState.'
'execute', autospec=True)
@mock.patch('nova.virt.powervm.tasks.storage.InstanceDiskToMgmt.'
'execute', autospec=True)
@mock.patch('nova.virt.powervm.tasks.image.StreamToGlance.execute')
@mock.patch('nova.virt.powervm.tasks.storage.RemoveInstanceDiskFromMgmt.'
'execute')
def test_snapshot(self, mock_rm, mock_stream, mock_conn, mock_update):
self.drv.disk_dvr = mock.Mock()
self.drv.image_api = mock.Mock()
mock_conn.return_value = 'stg_elem', 'vios_wrap', 'disk_path'
self.drv.snapshot('context', self.inst, 'image_id',
'update_task_state')
self.assertEqual(2, mock_update.call_count)
self.assertEqual(1, mock_conn.call_count)
mock_stream.assert_called_once_with(disk_path='disk_path')
mock_rm.assert_called_once_with(
stg_elem='stg_elem', vios_wrap='vios_wrap', disk_path='disk_path')
self.drv.disk_dvr.capabilities = {'snapshot': False}
self.assertRaises(exception.NotSupportedWithOption, self.drv.snapshot,
'context', self.inst, 'image_id', 'update_task_state')
def test_power_on(self):
self.drv.power_on('context', self.inst, 'network_info')
self.pwron.assert_called_once_with(self.adp, self.inst)
def test_power_off(self):
self.drv.power_off(self.inst)
self.pwroff.assert_called_once_with(
self.adp, self.inst, force_immediate=True, timeout=None)
def test_power_off_timeout(self):
# Long timeout (retry interval means nothing on powervm)
self.drv.power_off(self.inst, timeout=500, retry_interval=10)
self.pwroff.assert_called_once_with(
self.adp, self.inst, force_immediate=False, timeout=500)
@mock.patch('nova.virt.powervm.vm.reboot')
def test_reboot_soft(self, mock_reboot):
inst = mock.Mock()
self.drv.reboot('context', inst, 'network_info', 'SOFT')
mock_reboot.assert_called_once_with(self.adp, inst, False)
@mock.patch('nova.virt.powervm.vm.reboot')
def test_reboot_hard(self, mock_reboot):
inst = mock.Mock()
self.drv.reboot('context', inst, 'network_info', 'HARD')
mock_reboot.assert_called_once_with(self.adp, inst, True)
@mock.patch('nova.virt.powervm.driver.PowerVMDriver.plug_vifs')
def test_attach_interface(self, mock_plug_vifs):
self.drv.attach_interface('context', 'inst', 'image_meta', 'vif')
mock_plug_vifs.assert_called_once_with('inst', ['vif'])
@mock.patch('nova.virt.powervm.driver.PowerVMDriver.unplug_vifs')
def test_detach_interface(self, mock_unplug_vifs):
self.drv.detach_interface('context', 'inst', 'vif')
mock_unplug_vifs.assert_called_once_with('inst', ['vif'])
@mock.patch('nova.virt.powervm.tasks.vm.Get', autospec=True)
@mock.patch('nova.virt.powervm.tasks.base.run', autospec=True)
@mock.patch('nova.virt.powervm.tasks.network.PlugVifs', autospec=True)
@mock.patch('taskflow.patterns.linear_flow.Flow', autospec=True)
def test_plug_vifs(self, mock_tf, mock_plug_vifs, mock_tf_run, mock_get):
# Successful plug
mock_inst = mock.Mock()
self.drv.plug_vifs(mock_inst, 'net_info')
mock_get.assert_called_once_with(self.adp, mock_inst)
mock_plug_vifs.assert_called_once_with(
self.drv.virtapi, self.adp, mock_inst, 'net_info')
add_calls = [mock.call(mock_get.return_value),
mock.call(mock_plug_vifs.return_value)]
mock_tf.return_value.add.assert_has_calls(add_calls)
mock_tf_run.assert_called_once_with(
mock_tf.return_value, instance=mock_inst)
# InstanceNotFound and generic exception both raise
mock_tf_run.side_effect = exception.InstanceNotFound('id')
exc = self.assertRaises(exception.VirtualInterfacePlugException,
self.drv.plug_vifs, mock_inst, 'net_info')
self.assertIn('instance', str(exc))
mock_tf_run.side_effect = Exception
exc = self.assertRaises(exception.VirtualInterfacePlugException,
self.drv.plug_vifs, mock_inst, 'net_info')
self.assertIn('unexpected', str(exc))
@mock.patch('nova.virt.powervm.tasks.base.run', autospec=True)
@mock.patch('nova.virt.powervm.tasks.network.UnplugVifs', autospec=True)
@mock.patch('taskflow.patterns.linear_flow.Flow', autospec=True)
def test_unplug_vifs(self, mock_tf, mock_unplug_vifs, mock_tf_run):
# Successful unplug
mock_inst = mock.Mock()
self.drv.unplug_vifs(mock_inst, 'net_info')
mock_unplug_vifs.assert_called_once_with(self.adp, mock_inst,
'net_info')
mock_tf.return_value.add.assert_called_once_with(
mock_unplug_vifs.return_value)
mock_tf_run.assert_called_once_with(mock_tf.return_value,
instance=mock_inst)
# InstanceNotFound should pass
mock_tf_run.side_effect = exception.InstanceNotFound(instance_id='1')
self.drv.unplug_vifs(mock_inst, 'net_info')
# Raise InterfaceDetachFailed otherwise
mock_tf_run.side_effect = Exception
self.assertRaises(exception.InterfaceDetachFailed,
self.drv.unplug_vifs, mock_inst, 'net_info')
@mock.patch('pypowervm.tasks.vterm.open_remotable_vnc_vterm',
autospec=True)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid',
new=mock.Mock(return_value='uuid'))
def test_get_vnc_console(self, mock_vterm):
# Success
mock_vterm.return_value = '10'
resp = self.drv.get_vnc_console(mock.ANY, self.inst)
self.assertEqual('127.0.0.1', resp.host)
self.assertEqual('10', resp.port)
self.assertEqual('uuid', resp.internal_access_path)
mock_vterm.assert_called_once_with(
mock.ANY, 'uuid', mock.ANY, vnc_path='uuid')
# VNC failure - exception is raised directly
mock_vterm.side_effect = pvm_exc.VNCBasedTerminalFailedToOpen(err='xx')
self.assertRaises(pvm_exc.VNCBasedTerminalFailedToOpen,
self.drv.get_vnc_console, mock.ANY, self.inst)
# 404
mock_vterm.side_effect = pvm_exc.HttpError(mock.Mock(status=404))
self.assertRaises(exception.InstanceNotFound, self.drv.get_vnc_console,
mock.ANY, self.inst)
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter')
def test_attach_volume(self, mock_vscsi_adpt):
"""Validates the basic PowerVM attach volume."""
# BDMs
mock_bdm = self._fake_bdms()['block_device_mapping'][0]
with mock.patch.object(self.inst, 'save') as mock_save:
# Invoke the method.
self.drv.attach_volume('context', mock_bdm.get('connection_info'),
self.inst, mock.sentinel.stg_ftsk)
# Verify the connect volume was invoked
mock_vscsi_adpt.return_value.attach_volume.assert_called_once_with()
mock_save.assert_called_once_with()
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter')
def test_detach_volume(self, mock_vscsi_adpt):
"""Validates the basic PowerVM detach volume."""
# BDMs
mock_bdm = self._fake_bdms()['block_device_mapping'][0]
# Invoke the method, good path test.
self.drv.detach_volume('context', mock_bdm.get('connection_info'),
self.inst, mock.sentinel.stg_ftsk)
# Verify the disconnect volume was invoked
mock_vscsi_adpt.return_value.detach_volume.assert_called_once_with()
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter')
def test_extend_volume(self, mock_vscsi_adpt):
mock_bdm = self._fake_bdms()['block_device_mapping'][0]
self.drv.extend_volume(
'context', mock_bdm.get('connection_info'), self.inst, 0)
mock_vscsi_adpt.return_value.extend_volume.assert_called_once_with()
def test_vol_drv_iter(self):
block_device_info = self._fake_bdms()
bdms = nova_driver.block_device_info_get_mapping(block_device_info)
vol_adpt = mock.Mock()
def _get_results(bdms):
# Patch so we get the same mock back each time.
with mock.patch('nova.virt.powervm.volume.fcvscsi.'
'FCVscsiVolumeAdapter', return_value=vol_adpt):
return [
(bdm, vol_drv) for bdm, vol_drv in self.drv._vol_drv_iter(
'context', self.inst, bdms)]
results = _get_results(bdms)
self.assertEqual(
'fake_vol1',
results[0][0]['connection_info']['data']['volume_id'])
self.assertEqual(vol_adpt, results[0][1])
self.assertEqual(
'fake_vol2',
results[1][0]['connection_info']['data']['volume_id'])
self.assertEqual(vol_adpt, results[1][1])
# Test with empty bdms
self.assertEqual([], _get_results([]))
@staticmethod
def _fake_bdms():
def _fake_bdm(volume_id, target_lun):
connection_info = {'driver_volume_type': 'fibre_channel',
'data': {'volume_id': volume_id,
'target_lun': target_lun,
'initiator_target_map':
{'21000024F5': ['50050768']}}}
mapping_dict = {'source_type': 'volume', 'volume_id': volume_id,
'destination_type': 'volume',
'connection_info':
jsonutils.dumps(connection_info),
}
bdm_dict = nova_block_device.BlockDeviceDict(mapping_dict)
bdm_obj = bdmobj.BlockDeviceMapping(**bdm_dict)
return nova_virt_bdm.DriverVolumeBlockDevice(bdm_obj)
bdm_list = [_fake_bdm('fake_vol1', 0), _fake_bdm('fake_vol2', 1)]
block_device_info = {'block_device_mapping': bdm_list}
return block_device_info
@mock.patch('nova.virt.powervm.volume.fcvscsi.wwpns', autospec=True)
def test_get_volume_connector(self, mock_wwpns):
vol_connector = self.drv.get_volume_connector(mock.Mock())
self.assertEqual(mock_wwpns.return_value, vol_connector['wwpns'])
self.assertFalse(vol_connector['multipath'])
self.assertEqual(vol_connector['host'], CONF.host)
self.assertIsNone(vol_connector['initiator'])
-62
View File
@@ -1,62 +0,0 @@
# Copyright 2016 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from pypowervm.wrappers import managed_system as pvm_ms
from unittest import mock
from nova import test
from nova.virt.powervm import host as pvm_host
class TestPowerVMHost(test.NoDBTestCase):
def test_host_resources(self):
# Create objects to test with
ms_wrapper = mock.create_autospec(pvm_ms.System, spec_set=True)
asio = mock.create_autospec(pvm_ms.ASIOConfig, spec_set=True)
ms_wrapper.configure_mock(
proc_units_configurable=500,
proc_units_avail=500,
memory_configurable=5242880,
memory_free=5242752,
memory_region_size='big',
asio_config=asio)
self.flags(host='the_hostname')
# Run the actual test
stats = pvm_host.build_host_resource_from_ms(ms_wrapper)
self.assertIsNotNone(stats)
# Check for the presence of fields
fields = (('vcpus', 500), ('vcpus_used', 0),
('memory_mb', 5242880), ('memory_mb_used', 128),
'hypervisor_type', 'hypervisor_version',
('hypervisor_hostname', 'the_hostname'), 'cpu_info',
'supported_instances', 'stats')
for fld in fields:
if isinstance(fld, tuple):
value = stats.get(fld[0], None)
self.assertEqual(value, fld[1])
else:
value = stats.get(fld, None)
self.assertIsNotNone(value)
# Check for individual stats
hstats = (('proc_units', '500.00'), ('proc_units_used', '0.00'))
for stat in hstats:
if isinstance(stat, tuple):
value = stats['stats'].get(stat[0], None)
self.assertEqual(value, stat[1])
else:
value = stats['stats'].get(stat, None)
self.assertIsNotNone(value)
@@ -1,55 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from nova import test
from nova.virt.powervm import image
class TestImage(test.TestCase):
@mock.patch('nova.utils.temporary_chown', autospec=True)
@mock.patch('nova.image.glance.API', autospec=True)
def test_stream_blockdev_to_glance(self, mock_api, mock_chown):
mock_open = mock.mock_open()
with mock.patch('builtins.open', new=mock_open):
image.stream_blockdev_to_glance('context', mock_api, 'image_id',
'metadata', '/dev/disk')
mock_chown.assert_called_with('/dev/disk')
mock_open.assert_called_with('/dev/disk', 'rb')
mock_api.update.assert_called_with('context', 'image_id', 'metadata',
mock_open.return_value)
@mock.patch('nova.image.glance.API', autospec=True)
def test_generate_snapshot_metadata(self, mock_api):
mock_api.get.return_value = {'name': 'image_name'}
mock_instance = mock.Mock()
mock_instance.project_id = 'project_id'
ret = image.generate_snapshot_metadata('context', mock_api, 'image_id',
mock_instance)
mock_api.get.assert_called_with('context', 'image_id')
self.assertEqual({
'name': 'image_name',
'status': 'active',
'disk_format': 'raw',
'container_format': 'bare',
'properties': {
'image_location': 'snapshot',
'image_state': 'available',
'owner_id': 'project_id',
}
}, ret)
-204
View File
@@ -1,204 +0,0 @@
# Copyright 2015, 2017 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import fixtures
from oslo_utils.fixture import uuidsentinel
from pypowervm import const as pvm_const
from pypowervm.tasks import scsi_mapper as tsk_map
from pypowervm.tests import test_fixtures as pvm_fx
from pypowervm.utils import transaction as pvm_tx
from pypowervm.wrappers import network as pvm_net
from pypowervm.wrappers import storage as pvm_stg
from pypowervm.wrappers import virtual_io_server as pvm_vios
from nova import test
from nova.virt.powervm import media as m
class TestConfigDrivePowerVM(test.NoDBTestCase):
"""Unit Tests for the ConfigDrivePowerVM class."""
def setUp(self):
super(TestConfigDrivePowerVM, self).setUp()
self.apt = self.useFixture(pvm_fx.AdapterFx()).adpt
self.validate_vopt = self.useFixture(fixtures.MockPatch(
'pypowervm.tasks.vopt.validate_vopt_repo_exists',
autospec=True)).mock
self.validate_vopt.return_value = 'vios_uuid', 'vg_uuid'
@mock.patch('nova.api.metadata.base.InstanceMetadata')
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder.make_drive')
def test_crt_cfg_dr_iso(self, mock_mkdrv, mock_meta):
"""Validates that the image creation method works."""
cfg_dr_builder = m.ConfigDrivePowerVM(self.apt)
self.assertTrue(self.validate_vopt.called)
mock_instance = mock.MagicMock()
mock_instance.uuid = uuidsentinel.inst_id
mock_files = mock.MagicMock()
mock_net = mock.MagicMock()
iso_path = '/tmp/cfgdrv.iso'
cfg_dr_builder._create_cfg_dr_iso(mock_instance, mock_files, mock_net,
iso_path)
self.assertEqual(mock_mkdrv.call_count, 1)
# Test retry iso create
mock_mkdrv.reset_mock()
mock_mkdrv.side_effect = [OSError, mock_mkdrv]
cfg_dr_builder._create_cfg_dr_iso(mock_instance, mock_files, mock_net,
iso_path)
self.assertEqual(mock_mkdrv.call_count, 2)
@mock.patch('tempfile.NamedTemporaryFile')
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
@mock.patch('pypowervm.tasks.scsi_mapper.build_vscsi_mapping')
@mock.patch('pypowervm.tasks.scsi_mapper.add_map')
@mock.patch('os.path.getsize')
@mock.patch('pypowervm.tasks.storage.upload_vopt')
@mock.patch('nova.virt.powervm.media.ConfigDrivePowerVM.'
'_create_cfg_dr_iso')
def test_create_cfg_drv_vopt(self, mock_ccdi, mock_upl, mock_getsize,
mock_addmap, mock_bldmap, mock_vm_id,
mock_ntf):
cfg_dr = m.ConfigDrivePowerVM(self.apt)
mock_instance = mock.MagicMock()
mock_instance.uuid = uuidsentinel.inst_id
mock_upl.return_value = 'vopt', 'f_uuid'
fh = mock_ntf.return_value.__enter__.return_value
fh.name = 'iso_path'
wtsk = mock.create_autospec(pvm_tx.WrapperTask, instance=True)
ftsk = mock.create_autospec(pvm_tx.FeedTask, instance=True)
ftsk.configure_mock(wrapper_tasks={'vios_uuid': wtsk})
def test_afs(add_func):
# Validate the internal add_func
vio = mock.create_autospec(pvm_vios.VIOS)
self.assertEqual(mock_addmap.return_value, add_func(vio))
mock_vm_id.assert_called_once_with(mock_instance)
mock_bldmap.assert_called_once_with(
None, vio, mock_vm_id.return_value, 'vopt')
mock_addmap.assert_called_once_with(vio, mock_bldmap.return_value)
wtsk.add_functor_subtask.side_effect = test_afs
# calculate expected file name
expected_file_name = 'cfg_' + mock_instance.uuid.replace('-', '')
allowed_len = pvm_const.MaxLen.VOPT_NAME - 4 # '.iso' is 4 chars
expected_file_name = expected_file_name[:allowed_len] + '.iso'
cfg_dr.create_cfg_drv_vopt(
mock_instance, 'files', 'netinfo', ftsk, admin_pass='pass')
mock_ntf.assert_called_once_with(mode='rb')
mock_ccdi.assert_called_once_with(mock_instance, 'files', 'netinfo',
'iso_path', admin_pass='pass')
mock_getsize.assert_called_once_with('iso_path')
mock_upl.assert_called_once_with(self.apt, 'vios_uuid', fh,
expected_file_name,
mock_getsize.return_value)
wtsk.add_functor_subtask.assert_called_once()
def test_sanitize_network_info(self):
network_info = [{'type': 'lbr'}, {'type': 'pvm_sea'},
{'type': 'ovs'}]
cfg_dr_builder = m.ConfigDrivePowerVM(self.apt)
resp = cfg_dr_builder._sanitize_network_info(network_info)
expected_ret = [{'type': 'vif'}, {'type': 'vif'},
{'type': 'ovs'}]
self.assertEqual(resp, expected_ret)
@mock.patch('pypowervm.wrappers.storage.VG', autospec=True)
@mock.patch('pypowervm.tasks.storage.rm_vg_storage', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps', autospec=True)
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS', autospec=True)
@mock.patch('taskflow.task.FunctorTask', autospec=True)
def test_dlt_vopt(self, mock_functask, mock_vios, mock_find_maps, mock_gmf,
mock_uuid, mock_rmstg, mock_vg):
cfg_dr = m.ConfigDrivePowerVM(self.apt)
wtsk = mock.create_autospec(pvm_tx.WrapperTask, instance=True)
ftsk = mock.create_autospec(pvm_tx.FeedTask, instance=True)
ftsk.configure_mock(wrapper_tasks={'vios_uuid': wtsk})
# Test with no media to remove
mock_find_maps.return_value = []
cfg_dr.dlt_vopt('inst', ftsk)
mock_uuid.assert_called_once_with('inst')
mock_gmf.assert_called_once_with(pvm_stg.VOptMedia)
wtsk.add_functor_subtask.assert_called_once_with(
tsk_map.remove_maps, mock_uuid.return_value,
match_func=mock_gmf.return_value)
ftsk.get_wrapper.assert_called_once_with('vios_uuid')
mock_find_maps.assert_called_once_with(
ftsk.get_wrapper.return_value.scsi_mappings,
client_lpar_id=mock_uuid.return_value,
match_func=mock_gmf.return_value)
mock_functask.assert_not_called()
# Test with media to remove
mock_find_maps.return_value = [mock.Mock(backing_storage=media)
for media in ['m1', 'm2']]
def test_functor_task(rm_vopt):
# Validate internal rm_vopt function
rm_vopt()
mock_vg.get.assert_called_once_with(
self.apt, uuid='vg_uuid', parent_type=pvm_vios.VIOS,
parent_uuid='vios_uuid')
mock_rmstg.assert_called_once_with(
mock_vg.get.return_value, vopts=['m1', 'm2'])
return 'functor_task'
mock_functask.side_effect = test_functor_task
cfg_dr.dlt_vopt('inst', ftsk)
mock_functask.assert_called_once()
ftsk.add_post_execute.assert_called_once_with('functor_task')
def test_mgmt_cna_to_vif(self):
mock_cna = mock.Mock(spec=pvm_net.CNA, mac="FAD4433ED120")
# Run
cfg_dr_builder = m.ConfigDrivePowerVM(self.apt)
vif = cfg_dr_builder._mgmt_cna_to_vif(mock_cna)
# Validate
self.assertEqual(vif.get('address'), "fa:d4:43:3e:d1:20")
self.assertEqual(vif.get('id'), 'mgmt_vif')
self.assertIsNotNone(vif.get('network'))
self.assertEqual(1, len(vif.get('network').get('subnets')))
subnet = vif.get('network').get('subnets')[0]
self.assertEqual(6, subnet.get('version'))
self.assertEqual('fe80::/64', subnet.get('cidr'))
ip = subnet.get('ips')[0]
self.assertEqual('fe80::f8d4:43ff:fe3e:d120', ip.get('address'))
def test_mac_to_link_local(self):
mac = 'fa:d4:43:3e:d1:20'
self.assertEqual('fe80::f8d4:43ff:fe3e:d120',
m.ConfigDrivePowerVM._mac_to_link_local(mac))
mac = '00:00:00:00:00:00'
self.assertEqual('fe80::0200:00ff:fe00:0000',
m.ConfigDrivePowerVM._mac_to_link_local(mac))
mac = 'ff:ff:ff:ff:ff:ff'
self.assertEqual('fe80::fdff:ffff:feff:ffff',
m.ConfigDrivePowerVM._mac_to_link_local(mac))
-193
View File
@@ -1,193 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import retrying
from unittest import mock
from nova import exception
from nova import test
from pypowervm.tests import test_fixtures as pvm_fx
from pypowervm.tests.test_utils import pvmhttp
from nova.virt.powervm import mgmt
LPAR_HTTPRESP_FILE = "lpar.txt"
class TestMgmt(test.TestCase):
def setUp(self):
super(TestMgmt, self).setUp()
self.apt = self.useFixture(pvm_fx.AdapterFx()).adpt
lpar_http = pvmhttp.load_pvm_resp(LPAR_HTTPRESP_FILE, adapter=self.apt)
self.assertIsNotNone(
lpar_http, "Could not load %s " % LPAR_HTTPRESP_FILE)
self.resp = lpar_http.response
@mock.patch('pypowervm.tasks.partition.get_this_partition', autospec=True)
def test_mgmt_uuid(self, mock_get_partition):
mock_get_partition.return_value = mock.Mock(uuid='mock_mgmt')
adpt = mock.Mock()
# First run should call the partition only once
self.assertEqual('mock_mgmt', mgmt.mgmt_uuid(adpt))
mock_get_partition.assert_called_once_with(adpt)
# But a subsequent call should effectively no-op
mock_get_partition.reset_mock()
self.assertEqual('mock_mgmt', mgmt.mgmt_uuid(adpt))
self.assertEqual(mock_get_partition.call_count, 0)
@mock.patch('glob.glob', autospec=True)
@mock.patch('nova.privsep.path.writefile', autospec=True)
@mock.patch('os.path.realpath', autospec=True)
def test_discover_vscsi_disk(self, mock_realpath, mock_writefile,
mock_glob):
scanpath = '/sys/bus/vio/devices/30000005/host*/scsi_host/host*/scan'
udid = ('275b5d5f88fa5611e48be9000098be9400'
'13fb2aa55a2d7b8d150cb1b7b6bc04d6')
devlink = ('/dev/disk/by-id/scsi-SIBM_3303_NVDISK' + udid)
mapping = mock.Mock()
mapping.client_adapter.lpar_slot_num = 5
mapping.backing_storage.udid = udid
# Realistically, first glob would return e.g. .../host0/.../host0/...
# but it doesn't matter for test purposes.
mock_glob.side_effect = [[scanpath], [devlink]]
mgmt.discover_vscsi_disk(mapping)
mock_glob.assert_has_calls(
[mock.call(scanpath), mock.call('/dev/disk/by-id/*' + udid[-32:])])
mock_writefile.assert_called_once_with(scanpath, 'a', '- - -')
mock_realpath.assert_called_with(devlink)
@mock.patch('retrying.retry', autospec=True)
@mock.patch('glob.glob', autospec=True)
@mock.patch('nova.privsep.path.writefile', autospec=True)
def test_discover_vscsi_disk_not_one_result(self, mock_writefile,
mock_glob, mock_retry):
"""Zero or more than one disk is found by discover_vscsi_disk."""
def validate_retry(kwargs):
self.assertIn('retry_on_result', kwargs)
self.assertEqual(250, kwargs['wait_fixed'])
self.assertEqual(300000, kwargs['stop_max_delay'])
def raiser(unused):
raise retrying.RetryError(mock.Mock(attempt_number=123))
def retry_passthrough(**kwargs):
validate_retry(kwargs)
def wrapped(_poll_for_dev):
return _poll_for_dev
return wrapped
def retry_timeout(**kwargs):
validate_retry(kwargs)
def wrapped(_poll_for_dev):
return raiser
return wrapped
udid = ('275b5d5f88fa5611e48be9000098be9400'
'13fb2aa55a2d7b8d150cb1b7b6bc04d6')
mapping = mock.Mock()
mapping.client_adapter.lpar_slot_num = 5
mapping.backing_storage.udid = udid
# No disks found
mock_retry.side_effect = retry_timeout
mock_glob.side_effect = lambda path: []
self.assertRaises(exception.NoDiskDiscoveryException,
mgmt.discover_vscsi_disk, mapping)
# Multiple disks found
mock_retry.side_effect = retry_passthrough
mock_glob.side_effect = [['path'], ['/dev/sde', '/dev/sdf']]
self.assertRaises(exception.UniqueDiskDiscoveryException,
mgmt.discover_vscsi_disk, mapping)
@mock.patch('time.sleep', autospec=True)
@mock.patch('os.path.realpath', autospec=True)
@mock.patch('os.stat', autospec=True)
@mock.patch('nova.privsep.path.writefile', autospec=True)
def test_remove_block_dev(self, mock_writefile, mock_stat, mock_realpath,
mock_sleep):
link = '/dev/link/foo'
realpath = '/dev/sde'
delpath = '/sys/block/sde/device/delete'
mock_realpath.return_value = realpath
# Good path
mock_stat.side_effect = (None, None, OSError())
mgmt.remove_block_dev(link)
mock_realpath.assert_called_with(link)
mock_stat.assert_has_calls([mock.call(realpath), mock.call(delpath),
mock.call(realpath)])
mock_writefile.assert_called_once_with(delpath, 'a', '1')
self.assertEqual(0, mock_sleep.call_count)
# Device param not found
mock_writefile.reset_mock()
mock_stat.reset_mock()
mock_stat.side_effect = (OSError(), None, None)
self.assertRaises(exception.InvalidDevicePath, mgmt.remove_block_dev,
link)
# stat was called once; exec was not called
self.assertEqual(1, mock_stat.call_count)
self.assertEqual(0, mock_writefile.call_count)
# Delete special file not found
mock_writefile.reset_mock()
mock_stat.reset_mock()
mock_stat.side_effect = (None, OSError(), None)
self.assertRaises(exception.InvalidDevicePath, mgmt.remove_block_dev,
link)
# stat was called twice; exec was not called
self.assertEqual(2, mock_stat.call_count)
self.assertEqual(0, mock_writefile.call_count)
@mock.patch('retrying.retry')
@mock.patch('os.path.realpath')
@mock.patch('os.stat')
@mock.patch('nova.privsep.path.writefile')
def test_remove_block_dev_timeout(self, mock_dacw, mock_stat,
mock_realpath, mock_retry):
def validate_retry(kwargs):
self.assertIn('retry_on_result', kwargs)
self.assertEqual(250, kwargs['wait_fixed'])
self.assertEqual(10000, kwargs['stop_max_delay'])
def raiser(unused):
raise retrying.RetryError(mock.Mock(attempt_number=123))
def retry_timeout(**kwargs):
validate_retry(kwargs)
def wrapped(_poll_for_del):
return raiser
return wrapped
# Deletion was attempted, but device is still there
link = '/dev/link/foo'
delpath = '/sys/block/sde/device/delete'
realpath = '/dev/sde'
mock_realpath.return_value = realpath
mock_stat.side_effect = lambda path: 1
mock_retry.side_effect = retry_timeout
self.assertRaises(
exception.DeviceDeletionException, mgmt.remove_block_dev, link)
mock_realpath.assert_called_once_with(link)
mock_dacw.assert_called_with(delpath, 'a', '1')
-327
View File
@@ -1,327 +0,0 @@
# Copyright 2017 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from pypowervm import exceptions as pvm_ex
from pypowervm.wrappers import network as pvm_net
from unittest import mock
from nova import exception
from nova.network import model
from nova import test
from nova.virt.powervm import vif
def cna(mac):
"""Builds a mock Client Network Adapter for unit tests."""
return mock.Mock(spec=pvm_net.CNA, mac=mac, vswitch_uri='fake_href')
class TestVifFunctions(test.NoDBTestCase):
def setUp(self):
super(TestVifFunctions, self).setUp()
self.adpt = mock.Mock()
@mock.patch('nova.virt.powervm.vif.PvmOvsVifDriver')
def test_build_vif_driver(self, mock_driver):
# Valid vif type
driver = vif._build_vif_driver(self.adpt, 'instance', {'type': 'ovs'})
self.assertEqual(mock_driver.return_value, driver)
mock_driver.reset_mock()
# Fail if no vif type
self.assertRaises(exception.VirtualInterfacePlugException,
vif._build_vif_driver, self.adpt, 'instance',
{'type': None})
mock_driver.assert_not_called()
# Fail if invalid vif type
self.assertRaises(exception.VirtualInterfacePlugException,
vif._build_vif_driver, self.adpt, 'instance',
{'type': 'bad_type'})
mock_driver.assert_not_called()
@mock.patch('oslo_serialization.jsonutils.dumps')
@mock.patch('pypowervm.wrappers.event.Event')
def test_push_vif_event(self, mock_event, mock_dumps):
mock_vif = mock.Mock(mac='MAC', href='HREF')
vif._push_vif_event(self.adpt, 'action', mock_vif, mock.Mock(),
'pvm_sea')
mock_dumps.assert_called_once_with(
{'provider': 'NOVA_PVM_VIF', 'action': 'action', 'mac': 'MAC',
'type': 'pvm_sea'})
mock_event.bld.assert_called_once_with(self.adpt, 'HREF',
mock_dumps.return_value)
mock_event.bld.return_value.create.assert_called_once_with()
mock_dumps.reset_mock()
mock_event.bld.reset_mock()
mock_event.bld.return_value.create.reset_mock()
# Exception reraises
mock_event.bld.return_value.create.side_effect = IndexError
self.assertRaises(IndexError, vif._push_vif_event, self.adpt, 'action',
mock_vif, mock.Mock(), 'pvm_sea')
mock_dumps.assert_called_once_with(
{'provider': 'NOVA_PVM_VIF', 'action': 'action', 'mac': 'MAC',
'type': 'pvm_sea'})
mock_event.bld.assert_called_once_with(self.adpt, 'HREF',
mock_dumps.return_value)
mock_event.bld.return_value.create.assert_called_once_with()
@mock.patch('nova.virt.powervm.vif._push_vif_event')
@mock.patch('nova.virt.powervm.vif._build_vif_driver')
def test_plug(self, mock_bld_drv, mock_event):
"""Test the top-level plug method."""
mock_vif = {'address': 'MAC', 'type': 'pvm_sea'}
# 1) With new_vif=True (default)
vnet = vif.plug(self.adpt, 'instance', mock_vif)
mock_bld_drv.assert_called_once_with(self.adpt, 'instance', mock_vif)
mock_bld_drv.return_value.plug.assert_called_once_with(mock_vif,
new_vif=True)
self.assertEqual(mock_bld_drv.return_value.plug.return_value, vnet)
mock_event.assert_called_once_with(self.adpt, 'plug', vnet, mock.ANY,
'pvm_sea')
# Clean up
mock_bld_drv.reset_mock()
mock_bld_drv.return_value.plug.reset_mock()
mock_event.reset_mock()
# 2) Plug returns None (which it should IRL whenever new_vif=False).
mock_bld_drv.return_value.plug.return_value = None
vnet = vif.plug(self.adpt, 'instance', mock_vif, new_vif=False)
mock_bld_drv.assert_called_once_with(self.adpt, 'instance', mock_vif)
mock_bld_drv.return_value.plug.assert_called_once_with(mock_vif,
new_vif=False)
self.assertIsNone(vnet)
mock_event.assert_not_called()
@mock.patch('nova.virt.powervm.vif._build_vif_driver')
def test_plug_raises(self, mock_vif_drv):
"""HttpError is converted to VirtualInterfacePlugException."""
vif_drv = mock.Mock(plug=mock.Mock(side_effect=pvm_ex.HttpError(
resp=mock.Mock())))
mock_vif_drv.return_value = vif_drv
mock_vif = {'address': 'vifaddr'}
self.assertRaises(exception.VirtualInterfacePlugException,
vif.plug, 'adap', 'inst', mock_vif,
new_vif='new_vif')
mock_vif_drv.assert_called_once_with('adap', 'inst', mock_vif)
vif_drv.plug.assert_called_once_with(mock_vif, new_vif='new_vif')
@mock.patch('nova.virt.powervm.vif._push_vif_event')
@mock.patch('nova.virt.powervm.vif._build_vif_driver')
def test_unplug(self, mock_bld_drv, mock_event):
"""Test the top-level unplug method."""
mock_vif = {'address': 'MAC', 'type': 'pvm_sea'}
# 1) With default cna_w_list
mock_bld_drv.return_value.unplug.return_value = 'vnet_w'
vif.unplug(self.adpt, 'instance', mock_vif)
mock_bld_drv.assert_called_once_with(self.adpt, 'instance', mock_vif)
mock_bld_drv.return_value.unplug.assert_called_once_with(
mock_vif, cna_w_list=None)
mock_event.assert_called_once_with(self.adpt, 'unplug', 'vnet_w',
mock.ANY, 'pvm_sea')
# Clean up
mock_bld_drv.reset_mock()
mock_bld_drv.return_value.unplug.reset_mock()
mock_event.reset_mock()
# 2) With specified cna_w_list
mock_bld_drv.return_value.unplug.return_value = None
vif.unplug(self.adpt, 'instance', mock_vif, cna_w_list='cnalist')
mock_bld_drv.assert_called_once_with(self.adpt, 'instance', mock_vif)
mock_bld_drv.return_value.unplug.assert_called_once_with(
mock_vif, cna_w_list='cnalist')
mock_event.assert_not_called()
@mock.patch('nova.virt.powervm.vif._build_vif_driver')
def test_unplug_raises(self, mock_vif_drv):
"""HttpError is converted to VirtualInterfacePlugException."""
vif_drv = mock.Mock(unplug=mock.Mock(side_effect=pvm_ex.HttpError(
resp=mock.Mock())))
mock_vif_drv.return_value = vif_drv
mock_vif = {'address': 'vifaddr'}
self.assertRaises(exception.VirtualInterfaceUnplugException,
vif.unplug, 'adap', 'inst', mock_vif,
cna_w_list='cna_w_list')
mock_vif_drv.assert_called_once_with('adap', 'inst', mock_vif)
vif_drv.unplug.assert_called_once_with(
mock_vif, cna_w_list='cna_w_list')
class TestVifOvsDriver(test.NoDBTestCase):
def setUp(self):
super(TestVifOvsDriver, self).setUp()
self.adpt = mock.Mock()
self.inst = mock.MagicMock(uuid='inst_uuid')
self.drv = vif.PvmOvsVifDriver(self.adpt, self.inst)
@mock.patch('pypowervm.tasks.cna.crt_p2p_cna', autospec=True)
@mock.patch('pypowervm.tasks.partition.get_this_partition', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
def test_plug(self, mock_pvm_uuid, mock_mgmt_lpar, mock_p2p_cna,):
# Mock the data
mock_pvm_uuid.return_value = 'lpar_uuid'
mock_mgmt_lpar.return_value = mock.Mock(uuid='mgmt_uuid')
# mock_trunk_dev_name.return_value = 'device'
cna_w, trunk_wraps = mock.MagicMock(), [mock.MagicMock()]
mock_p2p_cna.return_value = cna_w, trunk_wraps
# Run the plug
network_model = model.Model({'bridge': 'br0', 'meta': {'mtu': 1450}})
mock_vif = model.VIF(address='aa:bb:cc:dd:ee:ff', id='vif_id',
network=network_model, devname='device')
self.drv.plug(mock_vif)
# Validate the calls
ovs_ext_ids = ('iface-id=vif_id,iface-status=active,'
'attached-mac=aa:bb:cc:dd:ee:ff,vm-uuid=inst_uuid')
mock_p2p_cna.assert_called_once_with(
self.adpt, None, 'lpar_uuid', ['mgmt_uuid'],
'NovaLinkVEABridge', configured_mtu=1450, crt_vswitch=True,
mac_addr='aa:bb:cc:dd:ee:ff', dev_name='device', ovs_bridge='br0',
ovs_ext_ids=ovs_ext_ids)
@mock.patch('pypowervm.tasks.partition.get_this_partition', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova.virt.powervm.vm.get_cnas')
@mock.patch('pypowervm.tasks.cna.find_trunks', autospec=True)
def test_plug_existing_vif(self, mock_find_trunks, mock_get_cnas,
mock_pvm_uuid, mock_mgmt_lpar):
# Mock the data
t1, t2 = mock.MagicMock(), mock.MagicMock()
mock_find_trunks.return_value = [t1, t2]
mock_cna = mock.Mock(mac='aa:bb:cc:dd:ee:ff')
mock_get_cnas.return_value = [mock_cna]
mock_pvm_uuid.return_value = 'lpar_uuid'
mock_mgmt_lpar.return_value = mock.Mock(uuid='mgmt_uuid')
self.inst = mock.MagicMock(uuid='c2e7ff9f-b9b6-46fa-8716-93bbb795b8b4')
self.drv = vif.PvmOvsVifDriver(self.adpt, self.inst)
# Run the plug
network_model = model.Model({'bridge': 'br0', 'meta': {'mtu': 1500}})
mock_vif = model.VIF(address='aa:bb:cc:dd:ee:ff', id='vif_id',
network=network_model, devname='devname')
resp = self.drv.plug(mock_vif, new_vif=False)
self.assertIsNone(resp)
# Validate if trunk.update got invoked for all trunks of CNA of vif
self.assertTrue(t1.update.called)
self.assertTrue(t2.update.called)
@mock.patch('pypowervm.tasks.cna.find_trunks')
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_unplug(self, mock_get_cnas, mock_find_trunks):
# Set up the mocks
mock_cna = mock.Mock(mac='aa:bb:cc:dd:ee:ff')
mock_get_cnas.return_value = [mock_cna]
t1, t2 = mock.MagicMock(), mock.MagicMock()
mock_find_trunks.return_value = [t1, t2]
# Call the unplug
mock_vif = {'address': 'aa:bb:cc:dd:ee:ff',
'network': {'bridge': 'br-int'}}
self.drv.unplug(mock_vif)
# The trunks and the cna should have been deleted
self.assertTrue(t1.delete.called)
self.assertTrue(t2.delete.called)
self.assertTrue(mock_cna.delete.called)
class TestVifSeaDriver(test.NoDBTestCase):
def setUp(self):
super(TestVifSeaDriver, self).setUp()
self.adpt = mock.Mock()
self.inst = mock.Mock()
self.drv = vif.PvmSeaVifDriver(self.adpt, self.inst)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
@mock.patch('pypowervm.tasks.cna.crt_cna')
def test_plug_from_neutron(self, mock_crt_cna, mock_pvm_uuid):
"""Tests that a VIF can be created. Mocks Neutron net"""
# Set up the mocks. Look like Neutron
fake_vif = {'details': {'vlan': 5}, 'network': {'meta': {}},
'address': 'aabbccddeeff'}
def validate_crt(adpt, host_uuid, lpar_uuid, vlan, mac_addr=None):
self.assertIsNone(host_uuid)
self.assertEqual(5, vlan)
self.assertEqual('aabbccddeeff', mac_addr)
return pvm_net.CNA.bld(self.adpt, 5, 'host_uuid',
mac_addr=mac_addr)
mock_crt_cna.side_effect = validate_crt
# Invoke
resp = self.drv.plug(fake_vif)
# Validate (along with validate method above)
self.assertEqual(1, mock_crt_cna.call_count)
self.assertIsNotNone(resp)
self.assertIsInstance(resp, pvm_net.CNA)
def test_plug_existing_vif(self):
"""Tests that a VIF need not be created."""
# Set up the mocks
fake_vif = {'network': {'meta': {'vlan': 5}},
'address': 'aabbccddeeff'}
# Invoke
resp = self.drv.plug(fake_vif, new_vif=False)
self.assertIsNone(resp)
@mock.patch('nova.virt.powervm.vm.get_cnas')
def test_unplug_vifs(self, mock_vm_get):
"""Tests that a delete of the vif can be done."""
# Mock up the CNA response. Two should already exist, the other
# should not.
cnas = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11'), cna('AABBCCDDEE22')]
mock_vm_get.return_value = cnas
# Run method. The AABBCCDDEE11 won't be unplugged (wasn't invoked
# below) and the last unplug will also just no-op because its not on
# the VM.
self.drv.unplug({'address': 'aa:bb:cc:dd:ee:ff'})
self.drv.unplug({'address': 'aa:bb:cc:dd:ee:22'})
self.drv.unplug({'address': 'aa:bb:cc:dd:ee:33'})
# The delete should have only been called once for each applicable vif.
# The second CNA didn't have a matching mac so it should be skipped.
self.assertEqual(1, cnas[0].delete.call_count)
self.assertEqual(0, cnas[1].delete.call_count)
self.assertEqual(1, cnas[2].delete.call_count)
-564
View File
@@ -1,564 +0,0 @@
# Copyright 2014, 2017 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import fixtures
from pypowervm import exceptions as pvm_exc
from pypowervm.helpers import log_helper as pvm_log
from pypowervm.tests import test_fixtures as pvm_fx
from pypowervm.utils import lpar_builder as lpar_bld
from pypowervm.utils import uuid as pvm_uuid
from pypowervm.wrappers import base_partition as pvm_bp
from pypowervm.wrappers import logical_partition as pvm_lpar
from nova.compute import power_state
from nova import exception
from nova import test
from nova.tests.unit.virt import powervm
from nova.virt.powervm import vm
class TestVMBuilder(test.NoDBTestCase):
def setUp(self):
super(TestVMBuilder, self).setUp()
self.adpt = mock.MagicMock()
self.host_w = mock.MagicMock()
self.lpar_b = vm.VMBuilder(self.host_w, self.adpt)
self.san_lpar_name = self.useFixture(fixtures.MockPatch(
'pypowervm.util.sanitize_partition_name_for_api',
autospec=True)).mock
self.inst = powervm.TEST_INSTANCE
@mock.patch('pypowervm.utils.lpar_builder.DefaultStandardize',
autospec=True)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
@mock.patch('pypowervm.utils.lpar_builder.LPARBuilder', autospec=True)
def test_vm_builder(self, mock_lpar_bldr, mock_uuid2pvm, mock_def_stdz):
inst = mock.Mock()
inst.configure_mock(
name='lpar_name', uuid='lpar_uuid',
flavor=mock.Mock(memory_mb='mem', vcpus='vcpus', extra_specs={}))
vmb = vm.VMBuilder('host', 'adap')
mock_def_stdz.assert_called_once_with('host', proc_units_factor=0.1)
self.assertEqual(mock_lpar_bldr.return_value,
vmb.lpar_builder(inst))
self.san_lpar_name.assert_called_once_with('lpar_name')
mock_uuid2pvm.assert_called_once_with(inst)
mock_lpar_bldr.assert_called_once_with(
'adap', {'name': self.san_lpar_name.return_value,
'uuid': mock_uuid2pvm.return_value,
'memory': 'mem',
'vcpu': 'vcpus',
'srr_capability': True}, mock_def_stdz.return_value)
# Assert non-default proc_units_factor.
mock_def_stdz.reset_mock()
self.flags(proc_units_factor=0.2, group='powervm')
vmb = vm.VMBuilder('host', 'adap')
mock_def_stdz.assert_called_once_with('host', proc_units_factor=0.2)
def test_format_flavor(self):
"""Perform tests against _format_flavor."""
# convert instance uuid to pypowervm uuid
# LP 1561128, simplified remote restart is enabled by default
lpar_attrs = {'memory': 2048,
'name': self.san_lpar_name.return_value,
'uuid': pvm_uuid.convert_uuid_to_pvm(
self.inst.uuid).upper(),
'vcpu': 1, 'srr_capability': True}
# Test dedicated procs
self.inst.flavor.extra_specs = {'powervm:dedicated_proc': 'true'}
test_attrs = dict(lpar_attrs, dedicated_proc='true')
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
self.san_lpar_name.assert_called_with(self.inst.name)
self.san_lpar_name.reset_mock()
# Test dedicated procs, min/max vcpu and sharing mode
self.inst.flavor.extra_specs = {'powervm:dedicated_proc': 'true',
'powervm:dedicated_sharing_mode':
'share_idle_procs_active',
'powervm:min_vcpu': '1',
'powervm:max_vcpu': '3'}
test_attrs = dict(lpar_attrs,
dedicated_proc='true',
sharing_mode='sre idle procs active',
min_vcpu='1', max_vcpu='3')
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
self.san_lpar_name.assert_called_with(self.inst.name)
self.san_lpar_name.reset_mock()
# Test shared proc sharing mode
self.inst.flavor.extra_specs = {'powervm:uncapped': 'true'}
test_attrs = dict(lpar_attrs, sharing_mode='uncapped')
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
self.san_lpar_name.assert_called_with(self.inst.name)
self.san_lpar_name.reset_mock()
# Test availability priority
self.inst.flavor.extra_specs = {'powervm:availability_priority': '150'}
test_attrs = dict(lpar_attrs, avail_priority='150')
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
self.san_lpar_name.assert_called_with(self.inst.name)
self.san_lpar_name.reset_mock()
# Test processor compatibility
self.inst.flavor.extra_specs = {
'powervm:processor_compatibility': 'POWER8'}
test_attrs = dict(lpar_attrs, processor_compatibility='POWER8')
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
self.san_lpar_name.assert_called_with(self.inst.name)
self.san_lpar_name.reset_mock()
# Test min, max proc units
self.inst.flavor.extra_specs = {'powervm:min_proc_units': '0.5',
'powervm:max_proc_units': '2.0'}
test_attrs = dict(lpar_attrs, min_proc_units='0.5',
max_proc_units='2.0')
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
self.san_lpar_name.assert_called_with(self.inst.name)
self.san_lpar_name.reset_mock()
# Test min, max mem
self.inst.flavor.extra_specs = {'powervm:min_mem': '1024',
'powervm:max_mem': '4096'}
test_attrs = dict(lpar_attrs, min_mem='1024', max_mem='4096')
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
self.san_lpar_name.assert_called_with(self.inst.name)
self.san_lpar_name.reset_mock()
# Test remote restart set to false
self.inst.flavor.extra_specs = {'powervm:srr_capability': 'false'}
test_attrs = dict(lpar_attrs, srr_capability=False)
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
# Unhandled powervm: key is ignored
self.inst.flavor.extra_specs = {'powervm:srr_capability': 'false',
'powervm:something_new': 'foo'}
test_attrs = dict(lpar_attrs, srr_capability=False)
self.assertEqual(self.lpar_b._format_flavor(self.inst), test_attrs)
# If we recognize a key, but don't handle it, we raise
with mock.patch.object(self.lpar_b, '_is_pvm_valid_key',
return_value=True):
self.inst.flavor.extra_specs = {'powervm:srr_capability': 'false',
'powervm:something_new': 'foo'}
self.assertRaises(KeyError, self.lpar_b._format_flavor, self.inst)
@mock.patch('pypowervm.wrappers.shared_proc_pool.SharedProcPool.search')
def test_spp_pool_id(self, mock_search):
# The default pool is always zero. Validate the path.
self.assertEqual(0, self.lpar_b._spp_pool_id('DefaultPool'))
self.assertEqual(0, self.lpar_b._spp_pool_id(None))
# Further invocations require calls to the adapter. Build a minimal
# mocked SPP wrapper
spp = mock.MagicMock()
spp.id = 1
# Three invocations. First has too many elems. Second has none.
# Third is just right. :-)
mock_search.side_effect = [[spp, spp], [], [spp]]
self.assertRaises(exception.ValidationError, self.lpar_b._spp_pool_id,
'fake_name')
self.assertRaises(exception.ValidationError, self.lpar_b._spp_pool_id,
'fake_name')
self.assertEqual(1, self.lpar_b._spp_pool_id('fake_name'))
class TestVM(test.NoDBTestCase):
def setUp(self):
super(TestVM, self).setUp()
self.apt = self.useFixture(pvm_fx.AdapterFx(
traits=pvm_fx.LocalPVMTraits)).adpt
self.apt.helpers = [pvm_log.log_helper]
self.san_lpar_name = self.useFixture(fixtures.MockPatch(
'pypowervm.util.sanitize_partition_name_for_api')).mock
self.san_lpar_name.side_effect = lambda name: name
mock_entries = [mock.Mock(), mock.Mock()]
self.resp = mock.MagicMock()
self.resp.feed = mock.MagicMock(entries=mock_entries)
self.get_pvm_uuid = self.useFixture(fixtures.MockPatch(
'nova.virt.powervm.vm.get_pvm_uuid')).mock
self.inst = powervm.TEST_INSTANCE
def test_translate_vm_state(self):
self.assertEqual(power_state.RUNNING,
vm._translate_vm_state('running'))
self.assertEqual(power_state.RUNNING,
vm._translate_vm_state('migrating running'))
self.assertEqual(power_state.RUNNING,
vm._translate_vm_state('starting'))
self.assertEqual(power_state.RUNNING,
vm._translate_vm_state('open firmware'))
self.assertEqual(power_state.RUNNING,
vm._translate_vm_state('shutting down'))
self.assertEqual(power_state.RUNNING,
vm._translate_vm_state('suspending'))
self.assertEqual(power_state.SHUTDOWN,
vm._translate_vm_state('migrating not active'))
self.assertEqual(power_state.SHUTDOWN,
vm._translate_vm_state('not activated'))
self.assertEqual(power_state.NOSTATE,
vm._translate_vm_state('unknown'))
self.assertEqual(power_state.NOSTATE,
vm._translate_vm_state('hardware discovery'))
self.assertEqual(power_state.NOSTATE,
vm._translate_vm_state('not available'))
self.assertEqual(power_state.SUSPENDED,
vm._translate_vm_state('resuming'))
self.assertEqual(power_state.SUSPENDED,
vm._translate_vm_state('suspended'))
self.assertEqual(power_state.CRASHED,
vm._translate_vm_state('error'))
@mock.patch('pypowervm.wrappers.logical_partition.LPAR', autospec=True)
def test_get_lpar_names(self, mock_lpar):
inst1 = mock.Mock()
inst1.configure_mock(name='inst1')
inst2 = mock.Mock()
inst2.configure_mock(name='inst2')
mock_lpar.search.return_value = [inst1, inst2]
self.assertEqual({'inst1', 'inst2'}, set(vm.get_lpar_names('adap')))
mock_lpar.search.assert_called_once_with(
'adap', is_mgmt_partition=False)
@mock.patch('pypowervm.tasks.vterm.close_vterm', autospec=True)
def test_dlt_lpar(self, mock_vterm):
"""Performs a delete LPAR test."""
vm.delete_lpar(self.apt, 'inst')
self.get_pvm_uuid.assert_called_once_with('inst')
self.apt.delete.assert_called_once_with(
pvm_lpar.LPAR.schema_type, root_id=self.get_pvm_uuid.return_value)
self.assertEqual(1, mock_vterm.call_count)
# Test Failure Path
# build a mock response body with the expected HSCL msg
resp = mock.Mock()
resp.body = 'error msg: HSCL151B more text'
self.apt.delete.side_effect = pvm_exc.Error(
'Mock Error Message', response=resp)
# Reset counters
self.apt.reset_mock()
mock_vterm.reset_mock()
self.assertRaises(pvm_exc.Error, vm.delete_lpar, self.apt, 'inst')
self.assertEqual(1, mock_vterm.call_count)
self.assertEqual(1, self.apt.delete.call_count)
self.apt.reset_mock()
mock_vterm.reset_mock()
# Test HttpError 404
resp.status = 404
self.apt.delete.side_effect = pvm_exc.HttpError(resp=resp)
vm.delete_lpar(self.apt, 'inst')
self.assertEqual(1, mock_vterm.call_count)
self.assertEqual(1, self.apt.delete.call_count)
self.apt.reset_mock()
mock_vterm.reset_mock()
# Test Other HttpError
resp.status = 111
self.apt.delete.side_effect = pvm_exc.HttpError(resp=resp)
self.assertRaises(pvm_exc.HttpError, vm.delete_lpar, self.apt, 'inst')
self.assertEqual(1, mock_vterm.call_count)
self.assertEqual(1, self.apt.delete.call_count)
self.apt.reset_mock()
mock_vterm.reset_mock()
# Test HttpError 404 closing vterm
resp.status = 404
mock_vterm.side_effect = pvm_exc.HttpError(resp=resp)
vm.delete_lpar(self.apt, 'inst')
self.assertEqual(1, mock_vterm.call_count)
self.assertEqual(0, self.apt.delete.call_count)
self.apt.reset_mock()
mock_vterm.reset_mock()
# Test Other HttpError closing vterm
resp.status = 111
mock_vterm.side_effect = pvm_exc.HttpError(resp=resp)
self.assertRaises(pvm_exc.HttpError, vm.delete_lpar, self.apt, 'inst')
self.assertEqual(1, mock_vterm.call_count)
self.assertEqual(0, self.apt.delete.call_count)
@mock.patch('nova.virt.powervm.vm.VMBuilder', autospec=True)
@mock.patch('pypowervm.utils.validation.LPARWrapperValidator',
autospec=True)
def test_crt_lpar(self, mock_vld, mock_vmbldr):
self.inst.flavor.extra_specs = {'powervm:dedicated_proc': 'true'}
mock_bldr = mock.Mock(spec=lpar_bld.LPARBuilder)
mock_vmbldr.return_value.lpar_builder.return_value = mock_bldr
mock_pend_lpar = mock.create_autospec(pvm_lpar.LPAR, instance=True)
mock_bldr.build.return_value = mock_pend_lpar
vm.create_lpar(self.apt, 'host', self.inst)
mock_vmbldr.assert_called_once_with('host', self.apt)
mock_vmbldr.return_value.lpar_builder.assert_called_once_with(
self.inst)
mock_bldr.build.assert_called_once_with()
mock_vld.assert_called_once_with(mock_pend_lpar, 'host')
mock_vld.return_value.validate_all.assert_called_once_with()
mock_pend_lpar.create.assert_called_once_with(parent='host')
# Test to verify the LPAR Creation with invalid name specification
mock_vmbldr.side_effect = lpar_bld.LPARBuilderException("Invalid Name")
self.assertRaises(exception.BuildAbortException,
vm.create_lpar, self.apt, 'host', self.inst)
# HttpError
mock_vmbldr.side_effect = pvm_exc.HttpError(mock.Mock())
self.assertRaises(exception.PowerVMAPIFailed,
vm.create_lpar, self.apt, 'host', self.inst)
@mock.patch('pypowervm.wrappers.logical_partition.LPAR', autospec=True)
def test_get_instance_wrapper(self, mock_lpar):
resp = mock.Mock(status=404)
mock_lpar.get.side_effect = pvm_exc.Error('message', response=resp)
# vm.get_instance_wrapper(self.apt, instance, 'lpar_uuid')
self.assertRaises(exception.InstanceNotFound, vm.get_instance_wrapper,
self.apt, self.inst)
@mock.patch('pypowervm.tasks.power.power_on', autospec=True)
@mock.patch('oslo_concurrency.lockutils.lock', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
def test_power_on(self, mock_wrap, mock_lock, mock_power_on):
entry = mock.Mock(state=pvm_bp.LPARState.NOT_ACTIVATED)
mock_wrap.return_value = entry
vm.power_on(None, self.inst)
mock_power_on.assert_called_once_with(entry, None)
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
mock_power_on.reset_mock()
mock_lock.reset_mock()
stop_states = [
pvm_bp.LPARState.RUNNING, pvm_bp.LPARState.STARTING,
pvm_bp.LPARState.OPEN_FIRMWARE, pvm_bp.LPARState.SHUTTING_DOWN,
pvm_bp.LPARState.ERROR, pvm_bp.LPARState.RESUMING,
pvm_bp.LPARState.SUSPENDING]
for stop_state in stop_states:
entry.state = stop_state
vm.power_on(None, self.inst)
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
mock_lock.reset_mock()
self.assertEqual(0, mock_power_on.call_count)
@mock.patch('pypowervm.tasks.power.power_on', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
def test_power_on_negative(self, mock_wrp, mock_power_on):
mock_wrp.return_value = mock.Mock(state=pvm_bp.LPARState.NOT_ACTIVATED)
# Convertible (PowerVM) exception
mock_power_on.side_effect = pvm_exc.VMPowerOnFailure(
reason='Something bad', lpar_nm='TheLPAR')
self.assertRaises(exception.InstancePowerOnFailure,
vm.power_on, None, self.inst)
# Non-pvm error raises directly
mock_power_on.side_effect = ValueError()
self.assertRaises(ValueError, vm.power_on, None, self.inst)
@mock.patch('pypowervm.tasks.power.PowerOp', autospec=True)
@mock.patch('pypowervm.tasks.power.power_off_progressive', autospec=True)
@mock.patch('oslo_concurrency.lockutils.lock', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
def test_power_off(self, mock_wrap, mock_lock, mock_power_off, mock_pop):
entry = mock.Mock(state=pvm_bp.LPARState.NOT_ACTIVATED)
mock_wrap.return_value = entry
vm.power_off(None, self.inst)
self.assertEqual(0, mock_power_off.call_count)
self.assertEqual(0, mock_pop.stop.call_count)
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
stop_states = [
pvm_bp.LPARState.RUNNING, pvm_bp.LPARState.STARTING,
pvm_bp.LPARState.OPEN_FIRMWARE, pvm_bp.LPARState.SHUTTING_DOWN,
pvm_bp.LPARState.ERROR, pvm_bp.LPARState.RESUMING,
pvm_bp.LPARState.SUSPENDING]
for stop_state in stop_states:
entry.state = stop_state
mock_power_off.reset_mock()
mock_pop.stop.reset_mock()
mock_lock.reset_mock()
vm.power_off(None, self.inst)
mock_power_off.assert_called_once_with(entry)
self.assertEqual(0, mock_pop.stop.call_count)
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
mock_power_off.reset_mock()
mock_lock.reset_mock()
vm.power_off(None, self.inst, force_immediate=True, timeout=5)
self.assertEqual(0, mock_power_off.call_count)
mock_pop.stop.assert_called_once_with(
entry, opts=mock.ANY, timeout=5)
self.assertEqual('PowerOff(immediate=true, operation=shutdown)',
str(mock_pop.stop.call_args[1]['opts']))
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
@mock.patch('pypowervm.tasks.power.power_off_progressive', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
def test_power_off_negative(self, mock_wrap, mock_power_off):
"""Negative tests."""
mock_wrap.return_value = mock.Mock(state=pvm_bp.LPARState.RUNNING)
# Raise the expected pypowervm exception
mock_power_off.side_effect = pvm_exc.VMPowerOffFailure(
reason='Something bad.', lpar_nm='TheLPAR')
# We should get a valid Nova exception that the compute manager expects
self.assertRaises(exception.InstancePowerOffFailure,
vm.power_off, None, self.inst)
# Non-pvm error raises directly
mock_power_off.side_effect = ValueError()
self.assertRaises(ValueError, vm.power_off, None, self.inst)
@mock.patch('pypowervm.tasks.power.power_on', autospec=True)
@mock.patch('pypowervm.tasks.power.power_off_progressive', autospec=True)
@mock.patch('pypowervm.tasks.power.PowerOp', autospec=True)
@mock.patch('oslo_concurrency.lockutils.lock', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
def test_reboot(self, mock_wrap, mock_lock, mock_pop, mock_pwroff,
mock_pwron):
entry = mock.Mock(state=pvm_bp.LPARState.NOT_ACTIVATED)
mock_wrap.return_value = entry
# No power_off
vm.reboot('adap', self.inst, False)
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
mock_wrap.assert_called_once_with('adap', self.inst)
mock_pwron.assert_called_once_with(entry, None)
self.assertEqual(0, mock_pwroff.call_count)
self.assertEqual(0, mock_pop.stop.call_count)
mock_pwron.reset_mock()
# power_off (no power_on) hard
entry.state = pvm_bp.LPARState.RUNNING
vm.reboot('adap', self.inst, True)
self.assertEqual(0, mock_pwron.call_count)
self.assertEqual(0, mock_pwroff.call_count)
mock_pop.stop.assert_called_once_with(entry, opts=mock.ANY)
self.assertEqual(
'PowerOff(immediate=true, operation=shutdown, restart=true)',
str(mock_pop.stop.call_args[1]['opts']))
mock_pop.reset_mock()
# power_off (no power_on) soft
entry.state = pvm_bp.LPARState.RUNNING
vm.reboot('adap', self.inst, False)
self.assertEqual(0, mock_pwron.call_count)
mock_pwroff.assert_called_once_with(entry, restart=True)
self.assertEqual(0, mock_pop.stop.call_count)
mock_pwroff.reset_mock()
# PowerVM error is converted
mock_pop.stop.side_effect = pvm_exc.TimeoutError("Timed out")
self.assertRaises(exception.InstanceRebootFailure,
vm.reboot, 'adap', self.inst, True)
# Non-PowerVM error is raised directly
mock_pwroff.side_effect = ValueError
self.assertRaises(ValueError, vm.reboot, 'adap', self.inst, False)
@mock.patch('oslo_serialization.jsonutils.loads')
def test_get_vm_qp(self, mock_loads):
self.apt.helpers = ['helper1', pvm_log.log_helper, 'helper3']
# Defaults
self.assertEqual(mock_loads.return_value,
vm.get_vm_qp(self.apt, 'lpar_uuid'))
self.apt.read.assert_called_once_with(
'LogicalPartition', root_id='lpar_uuid', suffix_type='quick',
suffix_parm=None)
mock_loads.assert_called_once_with(self.apt.read.return_value.body)
self.apt.read.reset_mock()
mock_loads.reset_mock()
# Specific qprop, no logging errors
self.assertEqual(mock_loads.return_value,
vm.get_vm_qp(self.apt, 'lpar_uuid', qprop='Prop',
log_errors=False))
self.apt.read.assert_called_once_with(
'LogicalPartition', root_id='lpar_uuid', suffix_type='quick',
suffix_parm='Prop', helpers=['helper1', 'helper3'])
resp = mock.MagicMock()
resp.status = 404
self.apt.read.side_effect = pvm_exc.HttpError(resp)
self.assertRaises(exception.InstanceNotFound, vm.get_vm_qp, self.apt,
'lpar_uuid', log_errors=False)
self.apt.read.side_effect = pvm_exc.Error("message", response=None)
self.assertRaises(pvm_exc.Error, vm.get_vm_qp, self.apt,
'lpar_uuid', log_errors=False)
resp.status = 500
self.apt.read.side_effect = pvm_exc.Error("message", response=resp)
self.assertRaises(pvm_exc.Error, vm.get_vm_qp, self.apt,
'lpar_uuid', log_errors=False)
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
@mock.patch('pypowervm.wrappers.network.CNA.search')
@mock.patch('pypowervm.wrappers.network.CNA.get')
def test_get_cnas(self, mock_get, mock_search, mock_uuid):
# No kwargs: get
self.assertEqual(mock_get.return_value, vm.get_cnas(self.apt, 'inst'))
mock_uuid.assert_called_once_with('inst')
mock_get.assert_called_once_with(self.apt, parent_type=pvm_lpar.LPAR,
parent_uuid=mock_uuid.return_value)
mock_search.assert_not_called()
# With kwargs: search
mock_get.reset_mock()
mock_uuid.reset_mock()
self.assertEqual(mock_search.return_value, vm.get_cnas(
self.apt, 'inst', one=2, three=4))
mock_uuid.assert_called_once_with('inst')
mock_search.assert_called_once_with(
self.apt, parent_type=pvm_lpar.LPAR,
parent_uuid=mock_uuid.return_value, one=2, three=4)
mock_get.assert_not_called()
def test_norm_mac(self):
EXPECTED = "12:34:56:78:90:ab"
self.assertEqual(EXPECTED, vm.norm_mac("12:34:56:78:90:ab"))
self.assertEqual(EXPECTED, vm.norm_mac("1234567890ab"))
self.assertEqual(EXPECTED, vm.norm_mac("12:34:56:78:90:AB"))
self.assertEqual(EXPECTED, vm.norm_mac("1234567890AB"))
@@ -1,456 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from pypowervm import const as pvm_const
from pypowervm.tasks import hdisk
from pypowervm.tests import test_fixtures as pvm_fx
from pypowervm.utils import transaction as pvm_tx
from pypowervm.wrappers import storage as pvm_stor
from pypowervm.wrappers import virtual_io_server as pvm_vios
from unittest import mock
from nova import conf as cfg
from nova import exception as exc
from nova import test
from nova.virt.powervm.volume import fcvscsi
CONF = cfg.CONF
I_WWPN_1 = '21000024FF649104'
I_WWPN_2 = '21000024FF649105'
class TestVSCSIAdapter(test.NoDBTestCase):
def setUp(self):
super(TestVSCSIAdapter, self).setUp()
self.adpt = self.useFixture(pvm_fx.AdapterFx()).adpt
self.wtsk = mock.create_autospec(pvm_tx.WrapperTask, instance=True)
self.ftsk = mock.create_autospec(pvm_tx.FeedTask, instance=True)
self.ftsk.configure_mock(wrapper_tasks={'vios_uuid': self.wtsk})
@mock.patch('nova.virt.powervm.vm.get_pvm_uuid')
def init_vol_adpt(mock_pvm_uuid):
con_info = {
'serial': 'id',
'data': {
'initiator_target_map': {
I_WWPN_1: ['t1'],
I_WWPN_2: ['t2', 't3']
},
'target_lun': '1',
'volume_id': 'a_volume_identifier',
},
}
mock_inst = mock.MagicMock()
mock_pvm_uuid.return_value = '1234'
return fcvscsi.FCVscsiVolumeAdapter(
self.adpt, mock_inst, con_info, stg_ftsk=self.ftsk)
self.vol_drv = init_vol_adpt()
@mock.patch('pypowervm.utils.transaction.FeedTask', autospec=True)
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS', autospec=True)
def test_reset_stg_ftsk(self, mock_vios, mock_ftsk):
self.vol_drv.reset_stg_ftsk('stg_ftsk')
self.assertEqual('stg_ftsk', self.vol_drv.stg_ftsk)
mock_vios.getter.return_value = 'getter'
mock_ftsk.return_value = 'local_feed_task'
self.vol_drv.reset_stg_ftsk()
self.assertEqual('local_feed_task', self.vol_drv.stg_ftsk)
mock_vios.getter.assert_called_once_with(
self.adpt, xag=[pvm_const.XAG.VIO_SMAP])
mock_ftsk.assert_called_once_with('local_feed_task', 'getter')
@mock.patch('pypowervm.tasks.partition.get_physical_wwpns', autospec=True)
def test_wwpns(self, mock_vio_wwpns):
mock_vio_wwpns.return_value = ['aa', 'bb']
wwpns = fcvscsi.wwpns(self.adpt)
self.assertListEqual(['aa', 'bb'], wwpns)
mock_vio_wwpns.assert_called_once_with(self.adpt, force_refresh=False)
def test_set_udid(self):
# Mock connection info
self.vol_drv.connection_info['data'][fcvscsi.UDID_KEY] = None
# Set the UDID
self.vol_drv._set_udid('udid')
# Verify
self.assertEqual('udid',
self.vol_drv.connection_info['data'][fcvscsi.UDID_KEY])
def test_get_udid(self):
# Set the value to retrieve
self.vol_drv.connection_info['data'][fcvscsi.UDID_KEY] = 'udid'
retrieved_udid = self.vol_drv._get_udid()
# Check key found
self.assertEqual('udid', retrieved_udid)
# Check key not found
self.vol_drv.connection_info['data'].pop(fcvscsi.UDID_KEY)
retrieved_udid = self.vol_drv._get_udid()
# Check key not found
self.assertIsNone(retrieved_udid)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
@mock.patch('pypowervm.utils.transaction.FeedTask', autospec=True)
def test_attach_volume(self, mock_feed_task, mock_get_wrap):
mock_lpar_wrap = mock.MagicMock()
mock_lpar_wrap.can_modify_io.return_value = True, None
mock_get_wrap.return_value = mock_lpar_wrap
mock_attach_ftsk = mock_feed_task.return_value
# Pass if all vioses modified
mock_ret = {'wrapper_task_rets': {'vios1': {'vio_modified': True},
'vios2': {'vio_modified': True}}}
mock_attach_ftsk.execute.return_value = mock_ret
self.vol_drv.attach_volume()
mock_feed_task.assert_called_once()
mock_attach_ftsk.add_functor_subtask.assert_called_once_with(
self.vol_drv._attach_volume_to_vio, provides='vio_modified',
flag_update=False)
mock_attach_ftsk.execute.assert_called_once()
self.ftsk.execute.assert_called_once()
mock_feed_task.reset_mock()
mock_attach_ftsk.reset_mock()
self.ftsk.reset_mock()
# Pass if 1 vios modified
mock_ret = {'wrapper_task_rets': {'vios1': {'vio_modified': True},
'vios2': {'vio_modified': False}}}
mock_attach_ftsk.execute.return_value = mock_ret
self.vol_drv.attach_volume()
mock_feed_task.assert_called_once()
mock_attach_ftsk.add_functor_subtask.assert_called_once_with(
self.vol_drv._attach_volume_to_vio, provides='vio_modified',
flag_update=False)
mock_attach_ftsk.execute.assert_called_once()
self.ftsk.execute.assert_called_once()
# Raise if no vios modified
mock_ret = {'wrapper_task_rets': {'vios1': {'vio_modified': False},
'vios2': {'vio_modified': False}}}
mock_attach_ftsk.execute.return_value = mock_ret
self.assertRaises(exc.VolumeAttachFailed, self.vol_drv.attach_volume)
# Raise if vm in invalid state
mock_lpar_wrap.can_modify_io.return_value = False, None
self.assertRaises(exc.VolumeAttachFailed, self.vol_drv.attach_volume)
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_set_udid')
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_add_append_mapping')
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_discover_volume_on_vios')
@mock.patch('pypowervm.tasks.hdisk.good_discovery', autospec=True)
def test_attach_volume_to_vio(self, mock_good_disc, mock_disc_vol,
mock_add_map, mock_set_udid):
# Setup mocks
mock_vios = mock.MagicMock()
mock_vios.uuid = 'uuid'
mock_disc_vol.return_value = 'status', 'devname', 'udid'
# Bad discovery
mock_good_disc.return_value = False
ret = self.vol_drv._attach_volume_to_vio(mock_vios)
self.assertFalse(ret)
mock_disc_vol.assert_called_once_with(mock_vios)
mock_good_disc.assert_called_once_with('status', 'devname')
# Good discovery
mock_good_disc.return_value = True
ret = self.vol_drv._attach_volume_to_vio(mock_vios)
self.assertTrue(ret)
mock_add_map.assert_called_once_with(
'uuid', 'devname', tag='a_volume_identifier')
mock_set_udid.assert_called_once_with('udid')
def test_extend_volume(self):
# Ensure the method is implemented
self.vol_drv.extend_volume()
@mock.patch('nova.virt.powervm.volume.fcvscsi.LOG')
@mock.patch('pypowervm.tasks.hdisk.good_discovery', autospec=True)
@mock.patch('pypowervm.tasks.hdisk.discover_hdisk', autospec=True)
@mock.patch('pypowervm.tasks.hdisk.build_itls', autospec=True)
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_get_hdisk_itls')
def test_discover_volume_on_vios(self, mock_get_itls, mock_build_itls,
mock_disc_hdisk, mock_good_disc,
mock_log):
mock_vios = mock.MagicMock()
mock_vios.uuid = 'uuid'
mock_get_itls.return_value = 'v_wwpns', 't_wwpns', 'lun'
mock_build_itls.return_value = 'itls'
mock_disc_hdisk.return_value = 'status', 'devname', 'udid'
# Good discovery
mock_good_disc.return_value = True
status, devname, udid = self.vol_drv._discover_volume_on_vios(
mock_vios)
self.assertEqual(mock_disc_hdisk.return_value[0], status)
self.assertEqual(mock_disc_hdisk.return_value[1], devname)
self.assertEqual(mock_disc_hdisk.return_value[2], udid)
mock_get_itls.assert_called_once_with(mock_vios)
mock_build_itls.assert_called_once_with('v_wwpns', 't_wwpns', 'lun')
mock_disc_hdisk.assert_called_once_with(self.adpt, 'uuid', 'itls')
mock_good_disc.assert_called_once_with('status', 'devname')
mock_log.info.assert_called_once()
mock_log.warning.assert_not_called()
mock_log.reset_mock()
# Bad discovery, not device in use status
mock_good_disc.return_value = False
self.vol_drv._discover_volume_on_vios(mock_vios)
mock_log.warning.assert_not_called()
mock_log.info.assert_not_called()
# Bad discovery, device in use status
mock_disc_hdisk.return_value = (hdisk.LUAStatus.DEVICE_IN_USE, 'dev',
'udid')
self.vol_drv._discover_volume_on_vios(mock_vios)
mock_log.warning.assert_called_once()
def test_get_hdisk_itls(self):
"""Validates the _get_hdisk_itls method."""
mock_vios = mock.MagicMock()
mock_vios.get_active_pfc_wwpns.return_value = [I_WWPN_1]
i_wwpn, t_wwpns, lun = self.vol_drv._get_hdisk_itls(mock_vios)
self.assertListEqual([I_WWPN_1], i_wwpn)
self.assertListEqual(['t1'], t_wwpns)
self.assertEqual('1', lun)
mock_vios.get_active_pfc_wwpns.return_value = [I_WWPN_2]
i_wwpn, t_wwpns, lun = self.vol_drv._get_hdisk_itls(mock_vios)
self.assertListEqual([I_WWPN_2], i_wwpn)
self.assertListEqual(['t2', 't3'], t_wwpns)
mock_vios.get_active_pfc_wwpns.return_value = ['12345']
i_wwpn, t_wwpns, lun = self.vol_drv._get_hdisk_itls(mock_vios)
self.assertListEqual([], i_wwpn)
@mock.patch('pypowervm.wrappers.storage.PV', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.build_vscsi_mapping',
autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.add_map', autospec=True)
def test_add_append_mapping(self, mock_add_map, mock_bld_map, mock_pv):
def test_afs(add_func):
mock_vios = mock.create_autospec(pvm_vios.VIOS)
self.assertEqual(mock_add_map.return_value, add_func(mock_vios))
mock_pv.bld.assert_called_once_with(self.adpt, 'devname', tag=None)
mock_bld_map.assert_called_once_with(
None, mock_vios, self.vol_drv.vm_uuid,
mock_pv.bld.return_value)
mock_add_map.assert_called_once_with(
mock_vios, mock_bld_map.return_value)
self.wtsk.add_functor_subtask.side_effect = test_afs
self.vol_drv._add_append_mapping('vios_uuid', 'devname')
self.wtsk.add_functor_subtask.assert_called_once()
@mock.patch('nova.virt.powervm.volume.fcvscsi.LOG.warning')
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
@mock.patch('pypowervm.utils.transaction.FeedTask', autospec=True)
def test_detach_volume(self, mock_feed_task, mock_get_wrap, mock_log):
mock_lpar_wrap = mock.MagicMock()
mock_lpar_wrap.can_modify_io.return_value = True, None
mock_get_wrap.return_value = mock_lpar_wrap
mock_detach_ftsk = mock_feed_task.return_value
# Multiple vioses modified
mock_ret = {'wrapper_task_rets': {'vios1': {'vio_modified': True},
'vios2': {'vio_modified': True}}}
mock_detach_ftsk.execute.return_value = mock_ret
self.vol_drv.detach_volume()
mock_feed_task.assert_called_once()
mock_detach_ftsk.add_functor_subtask.assert_called_once_with(
self.vol_drv._detach_vol_for_vio, provides='vio_modified',
flag_update=False)
mock_detach_ftsk.execute.assert_called_once_with()
self.ftsk.execute.assert_called_once_with()
mock_log.assert_not_called()
# 1 vios modified
mock_ret = {'wrapper_task_rets': {'vios1': {'vio_modified': True},
'vios2': {'vio_modified': False}}}
mock_detach_ftsk.execute.return_value = mock_ret
self.vol_drv.detach_volume()
mock_log.assert_not_called()
# No vioses modifed
mock_ret = {'wrapper_task_rets': {'vios1': {'vio_modified': False},
'vios2': {'vio_modified': False}}}
mock_detach_ftsk.execute.return_value = mock_ret
self.vol_drv.detach_volume()
mock_log.assert_called_once()
# Raise if exception during execute
mock_detach_ftsk.execute.side_effect = Exception()
self.assertRaises(exc.VolumeDetachFailed, self.vol_drv.detach_volume)
# Raise if vm in invalid state
mock_lpar_wrap.can_modify_io.return_value = False, None
self.assertRaises(exc.VolumeDetachFailed, self.vol_drv.detach_volume)
@mock.patch('pypowervm.tasks.hdisk.good_discovery', autospec=True)
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_discover_volume_on_vios')
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_add_remove_mapping')
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_add_remove_hdisk')
@mock.patch('nova.virt.powervm.vm.get_vm_qp')
def test_detach_vol_for_vio(self, mock_get_qp, mock_rm_hdisk, mock_rm_map,
mock_disc_vol, mock_good_disc):
# Good detach, bdm data is found
self.vol_drv._set_udid('udid')
mock_vios = mock.MagicMock()
mock_vios.uuid = 'vios_uuid'
mock_vios.hdisk_from_uuid.return_value = 'devname'
mock_get_qp.return_value = 'part_id'
ret = self.vol_drv._detach_vol_for_vio(mock_vios)
self.assertTrue(ret)
mock_vios.hdisk_from_uuid.assert_called_once_with('udid')
mock_rm_map.assert_called_once_with('part_id', 'vios_uuid', 'devname')
mock_rm_hdisk.assert_called_once_with(mock_vios, 'devname')
mock_vios.reset_mock()
mock_rm_map.reset_mock()
mock_rm_hdisk.reset_mock()
# Good detach, no udid
self.vol_drv._set_udid(None)
mock_disc_vol.return_value = 'status', 'devname', 'udid'
mock_good_disc.return_value = True
ret = self.vol_drv._detach_vol_for_vio(mock_vios)
self.assertTrue(ret)
mock_vios.hdisk_from_uuid.assert_not_called()
mock_disc_vol.assert_called_once_with(mock_vios)
mock_good_disc.assert_called_once_with('status', 'devname')
mock_rm_map.assert_called_once_with('part_id', 'vios_uuid', 'devname')
mock_rm_hdisk.assert_called_once_with(mock_vios, 'devname')
mock_vios.reset_mock()
mock_disc_vol.reset_mock()
mock_good_disc.reset_mock()
mock_rm_map.reset_mock()
mock_rm_hdisk.reset_mock()
# Good detach, no device name
self.vol_drv._set_udid('udid')
mock_vios.hdisk_from_uuid.return_value = None
ret = self.vol_drv._detach_vol_for_vio(mock_vios)
self.assertTrue(ret)
mock_vios.hdisk_from_uuid.assert_called_once_with('udid')
mock_disc_vol.assert_called_once_with(mock_vios)
mock_good_disc.assert_called_once_with('status', 'devname')
mock_rm_map.assert_called_once_with('part_id', 'vios_uuid', 'devname')
mock_rm_hdisk.assert_called_once_with(mock_vios, 'devname')
mock_rm_map.reset_mock()
mock_rm_hdisk.reset_mock()
# Bad detach, invalid state
mock_good_disc.return_value = False
ret = self.vol_drv._detach_vol_for_vio(mock_vios)
self.assertFalse(ret)
mock_rm_map.assert_not_called()
mock_rm_hdisk.assert_not_called()
# Bad detach, exception discovering volume on vios
mock_disc_vol.side_effect = Exception()
ret = self.vol_drv._detach_vol_for_vio(mock_vios)
self.assertFalse(ret)
mock_rm_map.assert_not_called()
mock_rm_hdisk.assert_not_called()
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.remove_maps', autospec=True)
def test_add_remove_mapping(self, mock_rm_maps, mock_gen_match):
def test_afs(rm_func):
mock_vios = mock.create_autospec(pvm_vios.VIOS)
self.assertEqual(mock_rm_maps.return_value, rm_func(mock_vios))
mock_gen_match.assert_called_once_with(
pvm_stor.PV, names=['devname'])
mock_rm_maps.assert_called_once_with(
mock_vios, 'vm_uuid', mock_gen_match.return_value)
self.wtsk.add_functor_subtask.side_effect = test_afs
self.vol_drv._add_remove_mapping('vm_uuid', 'vios_uuid', 'devname')
self.wtsk.add_functor_subtask.assert_called_once()
@mock.patch('pypowervm.tasks.hdisk.remove_hdisk', autospec=True)
@mock.patch('taskflow.task.FunctorTask', autospec=True)
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter.'
'_check_host_mappings')
def test_add_remove_hdisk(self, mock_check_maps, mock_functask,
mock_rm_hdisk):
mock_vios = mock.MagicMock()
mock_vios.uuid = 'uuid'
mock_check_maps.return_value = True
self.vol_drv._add_remove_hdisk(mock_vios, 'devname')
mock_functask.assert_not_called()
self.ftsk.add_post_execute.assert_not_called()
mock_check_maps.assert_called_once_with(mock_vios, 'devname')
self.assertEqual(0, mock_rm_hdisk.call_count)
def test_functor_task(rm_hdisk, name=None):
rm_hdisk()
return 'functor_task'
mock_check_maps.return_value = False
mock_functask.side_effect = test_functor_task
self.vol_drv._add_remove_hdisk(mock_vios, 'devname')
mock_functask.assert_called_once()
self.ftsk.add_post_execute.assert_called_once_with('functor_task')
mock_rm_hdisk.assert_called_once_with(self.adpt, CONF.host,
'devname', 'uuid')
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func', autospec=True)
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps', autospec=True)
def test_check_host_mappings(self, mock_find_maps, mock_gen_match):
mock_vios = mock.MagicMock()
mock_vios.uuid = 'uuid2'
mock_v1 = mock.MagicMock(scsi_mappings='scsi_maps_1', uuid='uuid1')
mock_v2 = mock.MagicMock(scsi_mappings='scsi_maps_2', uuid='uuid2')
mock_feed = [mock_v1, mock_v2]
self.ftsk.feed = mock_feed
# Multiple mappings found
mock_find_maps.return_value = ['map1', 'map2']
ret = self.vol_drv._check_host_mappings(mock_vios, 'devname')
self.assertTrue(ret)
mock_gen_match.assert_called_once_with(pvm_stor.PV, names=['devname'])
mock_find_maps.assert_called_once_with('scsi_maps_2', None,
mock_gen_match.return_value)
# One mapping found
mock_find_maps.return_value = ['map1']
ret = self.vol_drv._check_host_mappings(mock_vios, 'devname')
self.assertFalse(ret)
# No mappings found
mock_find_maps.return_value = []
ret = self.vol_drv._check_host_mappings(mock_vios, 'devname')
self.assertFalse(ret)
-17
View File
@@ -1,17 +0,0 @@
# Copyright 2017 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova.virt.powervm import driver
PowerVMDriver = driver.PowerVMDriver
View File
-268
View File
@@ -1,268 +0,0 @@
# Copyright 2013 OpenStack Foundation
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import oslo_log.log as logging
import pypowervm.const as pvm_const
import pypowervm.tasks.scsi_mapper as tsk_map
import pypowervm.util as pvm_u
import pypowervm.wrappers.virtual_io_server as pvm_vios
from nova import exception
from nova.virt.powervm import mgmt
from nova.virt.powervm import vm
LOG = logging.getLogger(__name__)
class DiskType(object):
BOOT = 'boot'
IMAGE = 'image'
class IterableToFileAdapter(object):
"""A degenerate file-like so that an iterable can be read like a file.
The Glance client returns an iterable, but PowerVM requires a file. This
is the adapter between the two.
"""
def __init__(self, iterable):
self.iterator = iterable.__iter__()
self.remaining_data = ''
def read(self, size):
chunk = self.remaining_data
try:
while not chunk:
chunk = next(self.iterator)
except StopIteration:
return ''
return_value = chunk[0:size]
self.remaining_data = chunk[size:]
return return_value
class DiskAdapter(metaclass=abc.ABCMeta):
capabilities = {
'shared_storage': False,
'has_imagecache': False,
'snapshot': False,
}
def __init__(self, adapter, host_uuid):
"""Initialize the DiskAdapter.
:param adapter: The pypowervm adapter.
:param host_uuid: The UUID of the PowerVM host.
"""
self._adapter = adapter
self._host_uuid = host_uuid
self.mp_uuid = mgmt.mgmt_uuid(self._adapter)
@abc.abstractproperty
def _vios_uuids(self):
"""List the UUIDs of the Virtual I/O Servers hosting the storage."""
raise NotImplementedError()
@abc.abstractmethod
def _disk_match_func(self, disk_type, instance):
"""Return a matching function to locate the disk for an instance.
:param disk_type: One of the DiskType enum values.
:param instance: The instance whose disk is to be found.
:return: Callable suitable for the match_func parameter of the
pypowervm.tasks.scsi_mapper.find_maps method, with the
following specification:
def match_func(storage_elem)
param storage_elem: A backing storage element wrapper (VOpt,
VDisk, PV, or LU) to be analyzed.
return: True if the storage_elem's mapping should be included;
False otherwise.
"""
raise NotImplementedError()
def get_bootdisk_path(self, instance, vios_uuid):
"""Find the local path for the instance's boot disk.
:param instance: nova.objects.instance.Instance object owning the
requested disk.
:param vios_uuid: PowerVM UUID of the VIOS to search for mappings.
:return: Local path for instance's boot disk.
"""
vm_uuid = vm.get_pvm_uuid(instance)
match_func = self._disk_match_func(DiskType.BOOT, instance)
vios_wrap = pvm_vios.VIOS.get(self._adapter, uuid=vios_uuid,
xag=[pvm_const.XAG.VIO_SMAP])
maps = tsk_map.find_maps(vios_wrap.scsi_mappings,
client_lpar_id=vm_uuid, match_func=match_func)
if maps:
return maps[0].server_adapter.backing_dev_name
return None
def _get_bootdisk_iter(self, instance):
"""Return an iterator of (storage_elem, VIOS) tuples for the instance.
This method returns an iterator of (storage_elem, VIOS) tuples, where
storage_element is a pypowervm storage element wrapper associated with
the instance boot disk and VIOS is the wrapper of the Virtual I/O
server owning that storage element.
:param instance: nova.objects.instance.Instance object owning the
requested disk.
:return: Iterator of tuples of (storage_elem, VIOS).
"""
lpar_wrap = vm.get_instance_wrapper(self._adapter, instance)
match_func = self._disk_match_func(DiskType.BOOT, instance)
for vios_uuid in self._vios_uuids:
vios_wrap = pvm_vios.VIOS.get(
self._adapter, uuid=vios_uuid, xag=[pvm_const.XAG.VIO_SMAP])
for scsi_map in tsk_map.find_maps(
vios_wrap.scsi_mappings, client_lpar_id=lpar_wrap.id,
match_func=match_func):
yield scsi_map.backing_storage, vios_wrap
def connect_instance_disk_to_mgmt(self, instance):
"""Connect an instance's boot disk to the management partition.
:param instance: The instance whose boot disk is to be mapped.
:return stg_elem: The storage element (LU, VDisk, etc.) that was mapped
:return vios: The EntryWrapper of the VIOS from which the mapping was
made.
:raise InstanceDiskMappingFailed: If the mapping could not be done.
"""
for stg_elem, vios in self._get_bootdisk_iter(instance):
msg_args = {'disk_name': stg_elem.name, 'vios_name': vios.name}
# Create a new mapping. NOTE: If there's an existing mapping on
# the other VIOS but not this one, we'll create a second mapping
# here. It would take an extreme sequence of events to get to that
# point, and the second mapping would be harmless anyway. The
# alternative would be always checking all VIOSes for existing
# mappings, which increases the response time of the common case by
# an entire GET of VIOS+VIO_SMAP.
LOG.debug("Mapping boot disk %(disk_name)s to the management "
"partition from Virtual I/O Server %(vios_name)s.",
msg_args, instance=instance)
try:
tsk_map.add_vscsi_mapping(self._host_uuid, vios, self.mp_uuid,
stg_elem)
# If that worked, we're done. add_vscsi_mapping logged.
return stg_elem, vios
except Exception:
LOG.exception("Failed to map boot disk %(disk_name)s to the "
"management partition from Virtual I/O Server "
"%(vios_name)s.", msg_args, instance=instance)
# Try the next hit, if available.
# We either didn't find the boot dev, or failed all attempts to map it.
raise exception.InstanceDiskMappingFailed(instance_name=instance.name)
@abc.abstractmethod
def disconnect_disk_from_mgmt(self, vios_uuid, disk_name):
"""Disconnect a disk from the management partition.
:param vios_uuid: The UUID of the Virtual I/O Server serving the
mapping.
:param disk_name: The name of the disk to unmap.
"""
raise NotImplementedError()
@abc.abstractproperty
def capacity(self):
"""Capacity of the storage in gigabytes.
Default is to make the capacity arbitrarily large.
"""
raise NotImplementedError()
@abc.abstractproperty
def capacity_used(self):
"""Capacity of the storage in gigabytes that is used.
Default is to say none of it is used.
"""
raise NotImplementedError()
@staticmethod
def _get_disk_name(disk_type, instance, short=False):
"""Generate a name for a virtual disk associated with an instance.
:param disk_type: One of the DiskType enum values.
:param instance: The instance for which the disk is to be created.
:param short: If True, the generated name will be limited to 15
characters (the limit for virtual disk). If False, it
will be limited by the API (79 characters currently).
:return: The sanitized file name for the disk.
"""
prefix = '%s_' % (disk_type[0] if short else disk_type)
base = ('%s_%s' % (instance.name[:8], instance.uuid[:4]) if short
else instance.name)
return pvm_u.sanitize_file_name_for_api(
base, prefix=prefix, max_len=pvm_const.MaxLen.VDISK_NAME if short
else pvm_const.MaxLen.FILENAME_DEFAULT)
@abc.abstractmethod
def detach_disk(self, instance):
"""Detaches the storage adapters from the image disk.
:param instance: instance to detach the image for.
:return: A list of all the backing storage elements that were
detached from the I/O Server and VM.
"""
raise NotImplementedError()
@abc.abstractmethod
def delete_disks(self, storage_elems):
"""Removes the disks specified by the mappings.
:param storage_elems: A list of the storage elements that are to be
deleted. Derived from the return value from
detach_disk.
"""
raise NotImplementedError()
@abc.abstractmethod
def create_disk_from_image(self, context, instance, image_meta):
"""Creates a disk and copies the specified image to it.
Cleans up created disk if an error occurs.
:param context: nova context used to retrieve image from glance
:param instance: instance to create the disk for.
:param image_meta: nova.objects.ImageMeta object with the metadata of
the image of the instance.
:return: The backing pypowervm storage object that was created.
"""
raise NotImplementedError()
@abc.abstractmethod
def attach_disk(self, instance, disk_info, stg_ftsk):
"""Attaches the disk image to the Virtual Machine.
:param instance: nova instance to attach the disk to.
:param disk_info: The pypowervm storage element returned from
create_disk_from_image. Ex. VOptMedia, VDisk, LU,
or PV.
:param stg_ftsk: (Optional) The pypowervm transaction FeedTask for the
I/O Operations. If provided, the Virtual I/O Server
mapping updates will be added to the FeedTask. This
defers the updates to some later point in time. If
the FeedTask is not provided, the updates will be run
immediately when this method is executed.
"""
raise NotImplementedError()
-211
View File
@@ -1,211 +0,0 @@
# Copyright 2013 OpenStack Foundation
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import oslo_log.log as logging
from pypowervm import const as pvm_const
from pypowervm.tasks import scsi_mapper as tsk_map
from pypowervm.tasks import storage as tsk_stg
from pypowervm.wrappers import storage as pvm_stg
from pypowervm.wrappers import virtual_io_server as pvm_vios
from nova import conf
from nova import exception
from nova.image import glance
from nova.virt.powervm.disk import driver as disk_dvr
from nova.virt.powervm import vm
LOG = logging.getLogger(__name__)
CONF = conf.CONF
IMAGE_API = glance.API()
class LocalStorage(disk_dvr.DiskAdapter):
def __init__(self, adapter, host_uuid):
super(LocalStorage, self).__init__(adapter, host_uuid)
self.capabilities = {
'shared_storage': False,
'has_imagecache': False,
# NOTE(efried): 'snapshot' capability set dynamically below.
}
# Query to get the Volume Group UUID
if not CONF.powervm.volume_group_name:
raise exception.OptRequiredIfOtherOptValue(
if_opt='disk_driver', if_value='localdisk',
then_opt='volume_group_name')
self.vg_name = CONF.powervm.volume_group_name
vios_w, vg_w = tsk_stg.find_vg(adapter, self.vg_name)
self._vios_uuid = vios_w.uuid
self.vg_uuid = vg_w.uuid
# Set the 'snapshot' capability dynamically. If we're hosting I/O on
# the management partition, we can snapshot. If we're hosting I/O on
# traditional VIOS, we are limited by the fact that a VSCSI device
# can't be mapped to two partitions (the VIOS and the management) at
# once.
self.capabilities['snapshot'] = self.mp_uuid == self._vios_uuid
LOG.info("Local Storage driver initialized: volume group: '%s'",
self.vg_name)
@property
def _vios_uuids(self):
"""List the UUIDs of the Virtual I/O Servers hosting the storage.
For localdisk, there's only one.
"""
return [self._vios_uuid]
@staticmethod
def _disk_match_func(disk_type, instance):
"""Return a matching function to locate the disk for an instance.
:param disk_type: One of the DiskType enum values.
:param instance: The instance whose disk is to be found.
:return: Callable suitable for the match_func parameter of the
pypowervm.tasks.scsi_mapper.find_maps method.
"""
disk_name = LocalStorage._get_disk_name(
disk_type, instance, short=True)
return tsk_map.gen_match_func(pvm_stg.VDisk, names=[disk_name])
@property
def capacity(self):
"""Capacity of the storage in gigabytes."""
vg_wrap = self._get_vg_wrap()
return float(vg_wrap.capacity)
@property
def capacity_used(self):
"""Capacity of the storage in gigabytes that is used."""
vg_wrap = self._get_vg_wrap()
# Subtract available from capacity
return float(vg_wrap.capacity) - float(vg_wrap.available_size)
def delete_disks(self, storage_elems):
"""Removes the specified disks.
:param storage_elems: A list of the storage elements that are to be
deleted. Derived from the return value from
detach_disk.
"""
# All of localdisk is done against the volume group. So reload
# that (to get new etag) and then update against it.
tsk_stg.rm_vg_storage(self._get_vg_wrap(), vdisks=storage_elems)
def detach_disk(self, instance):
"""Detaches the storage adapters from the image disk.
:param instance: Instance to disconnect the image for.
:return: A list of all the backing storage elements that were
disconnected from the I/O Server and VM.
"""
lpar_uuid = vm.get_pvm_uuid(instance)
# Build the match function
match_func = tsk_map.gen_match_func(pvm_stg.VDisk)
vios_w = pvm_vios.VIOS.get(
self._adapter, uuid=self._vios_uuid, xag=[pvm_const.XAG.VIO_SMAP])
# Remove the mappings.
mappings = tsk_map.remove_maps(
vios_w, lpar_uuid, match_func=match_func)
# Update the VIOS with the removed mappings.
vios_w.update()
return [x.backing_storage for x in mappings]
def disconnect_disk_from_mgmt(self, vios_uuid, disk_name):
"""Disconnect a disk from the management partition.
:param vios_uuid: The UUID of the Virtual I/O Server serving the
mapping.
:param disk_name: The name of the disk to unmap.
"""
tsk_map.remove_vdisk_mapping(self._adapter, vios_uuid, self.mp_uuid,
disk_names=[disk_name])
LOG.info("Unmapped boot disk %(disk_name)s from the management "
"partition from Virtual I/O Server %(vios_name)s.",
{'disk_name': disk_name, 'mp_uuid': self.mp_uuid,
'vios_name': vios_uuid})
def create_disk_from_image(self, context, instance, image_meta):
"""Creates a disk and copies the specified image to it.
Cleans up the created disk if an error occurs.
:param context: nova context used to retrieve image from glance
:param instance: instance to create the disk for.
:param image_meta: The metadata of the image of the instance.
:return: The backing pypowervm storage object that was created.
"""
LOG.info('Create disk.', instance=instance)
return self._upload_image(context, instance, image_meta)
# TODO(esberglu): Copy vdisk when implementing image cache.
def _upload_image(self, context, instance, image_meta):
"""Upload a new image.
:param context: Nova context used to retrieve image from glance.
:param image_meta: The metadata of the image of the instance.
:return: The virtual disk containing the image.
"""
img_name = self._get_disk_name(disk_dvr.DiskType.BOOT, instance,
short=True)
# TODO(esberglu) Add check for cached image when adding imagecache.
return tsk_stg.upload_new_vdisk(
self._adapter, self._vios_uuid, self.vg_uuid,
disk_dvr.IterableToFileAdapter(
IMAGE_API.download(context, image_meta.id)), img_name,
image_meta.size, d_size=image_meta.size,
upload_type=tsk_stg.UploadType.IO_STREAM,
file_format=image_meta.disk_format)[0]
def attach_disk(self, instance, disk_info, stg_ftsk):
"""Attaches the disk image to the Virtual Machine.
:param instance: nova instance to connect the disk to.
:param disk_info: The pypowervm storage element returned from
create_disk_from_image. Ex. VOptMedia, VDisk, LU,
or PV.
:param stg_ftsk: The pypowervm transaction FeedTask for the
I/O Operations. The Virtual I/O Server mapping updates
will be added to the FeedTask. This defers the updates
to some later point in time.
"""
lpar_uuid = vm.get_pvm_uuid(instance)
def add_func(vios_w):
LOG.info("Adding logical volume disk connection to VIOS %(vios)s.",
{'vios': vios_w.name}, instance=instance)
mapping = tsk_map.build_vscsi_mapping(
self._host_uuid, vios_w, lpar_uuid, disk_info)
return tsk_map.add_map(vios_w, mapping)
stg_ftsk.wrapper_tasks[self._vios_uuid].add_functor_subtask(add_func)
def _get_vg_wrap(self):
return pvm_stg.VG.get(self._adapter, uuid=self.vg_uuid,
parent_type=pvm_vios.VIOS,
parent_uuid=self._vios_uuid)
-258
View File
@@ -1,258 +0,0 @@
# Copyright 2015, 2018 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import random
import oslo_log.log as logging
from pypowervm import const as pvm_const
from pypowervm import exceptions as pvm_exc
from pypowervm.tasks import cluster_ssp as tsk_cs
from pypowervm.tasks import partition as tsk_par
from pypowervm.tasks import scsi_mapper as tsk_map
from pypowervm.tasks import storage as tsk_stg
import pypowervm.util as pvm_u
import pypowervm.wrappers.cluster as pvm_clust
import pypowervm.wrappers.storage as pvm_stg
from nova import exception
from nova.image import glance
from nova.virt.powervm.disk import driver as disk_drv
from nova.virt.powervm import vm
LOG = logging.getLogger(__name__)
IMAGE_API = glance.API()
class SSPDiskAdapter(disk_drv.DiskAdapter):
"""Provides a disk adapter for Shared Storage Pools.
Shared Storage Pools are a clustered file system technology that can link
together Virtual I/O Servers.
This adapter provides the connection for nova ephemeral storage (not
Cinder) to connect to virtual machines.
"""
capabilities = {
'shared_storage': True,
# NOTE(efried): Whereas the SSP disk driver definitely does image
# caching, it's not through the nova.virt.imagecache.ImageCacheManager
# API. Setting `has_imagecache` to True here would have the side
# effect of having a periodic task try to call this class's
# manage_image_cache method (not implemented here; and a no-op in the
# superclass) which would be harmless, but unnecessary.
'has_imagecache': False,
'snapshot': True,
}
def __init__(self, adapter, host_uuid):
"""Initialize the SSPDiskAdapter.
:param adapter: pypowervm.adapter.Adapter for the PowerVM REST API.
:param host_uuid: PowerVM UUID of the managed system.
"""
super(SSPDiskAdapter, self).__init__(adapter, host_uuid)
try:
self._clust = pvm_clust.Cluster.get(self._adapter)[0]
self._ssp = pvm_stg.SSP.get_by_href(
self._adapter, self._clust.ssp_uri)
self._tier = tsk_stg.default_tier_for_ssp(self._ssp)
except pvm_exc.Error:
LOG.exception("A unique PowerVM Cluster and Shared Storage Pool "
"is required in the default Tier.")
raise exception.NotFound()
LOG.info(
"SSP Storage driver initialized. Cluster '%(clust_name)s'; "
"SSP '%(ssp_name)s'; Tier '%(tier_name)s'",
{'clust_name': self._clust.name, 'ssp_name': self._ssp.name,
'tier_name': self._tier.name})
@property
def capacity(self):
"""Capacity of the storage in gigabytes."""
# Retrieving the Tier is faster (because don't have to refresh LUs.)
return float(self._tier.refresh().capacity)
@property
def capacity_used(self):
"""Capacity of the storage in gigabytes that is used."""
self._ssp = self._ssp.refresh()
return float(self._ssp.capacity) - float(self._ssp.free_space)
def detach_disk(self, instance):
"""Detaches the storage adapters from the disk.
:param instance: instance from which to detach the image.
:return: A list of all the backing storage elements that were detached
from the I/O Server and VM.
"""
stg_ftsk = tsk_par.build_active_vio_feed_task(
self._adapter, name='ssp', xag=[pvm_const.XAG.VIO_SMAP])
lpar_uuid = vm.get_pvm_uuid(instance)
match_func = tsk_map.gen_match_func(pvm_stg.LU)
def rm_func(vwrap):
LOG.info("Removing SSP disk connection to VIOS %s.",
vwrap.name, instance=instance)
return tsk_map.remove_maps(vwrap, lpar_uuid,
match_func=match_func)
# Remove the mapping from *each* VIOS on the LPAR's host.
# The LPAR's host has to be self._host_uuid, else the PowerVM API will
# fail.
#
# Note - this may not be all the VIOSes on the system...just the ones
# in the SSP cluster.
#
# The mappings will normally be the same on all VIOSes, unless a VIOS
# was down when a disk was added. So for the return value, we need to
# collect the union of all relevant mappings from all VIOSes.
lu_set = set()
for vios_uuid in self._vios_uuids:
# Add the remove for the VIO
stg_ftsk.wrapper_tasks[vios_uuid].add_functor_subtask(rm_func)
# Find the active LUs so that a delete op knows what to remove.
vios_w = stg_ftsk.wrapper_tasks[vios_uuid].wrapper
mappings = tsk_map.find_maps(vios_w.scsi_mappings,
client_lpar_id=lpar_uuid,
match_func=match_func)
if mappings:
lu_set.update([x.backing_storage for x in mappings])
stg_ftsk.execute()
return list(lu_set)
def delete_disks(self, storage_elems):
"""Removes the disks specified by the mappings.
:param storage_elems: A list of the storage elements (LU
ElementWrappers) that are to be deleted. Derived
from the return value from detach_disk.
"""
tsk_stg.rm_tier_storage(storage_elems, tier=self._tier)
def create_disk_from_image(self, context, instance, image_meta):
"""Creates a boot disk and links the specified image to it.
If the specified image has not already been uploaded, an Image LU is
created for it. A Disk LU is then created for the instance and linked
to the Image LU.
:param context: nova context used to retrieve image from glance
:param instance: instance to create the disk for.
:param nova.objects.ImageMeta image_meta:
The metadata of the image of the instance.
:return: The backing pypowervm LU storage object that was created.
"""
LOG.info('SSP: Create boot disk from image %s.', image_meta.id,
instance=instance)
image_lu = tsk_cs.get_or_upload_image_lu(
self._tier, pvm_u.sanitize_file_name_for_api(
image_meta.name, prefix=disk_drv.DiskType.IMAGE + '_',
suffix='_' + image_meta.checksum),
random.choice(self._vios_uuids), disk_drv.IterableToFileAdapter(
IMAGE_API.download(context, image_meta.id)), image_meta.size,
upload_type=tsk_stg.UploadType.IO_STREAM)
boot_lu_name = pvm_u.sanitize_file_name_for_api(
instance.name, prefix=disk_drv.DiskType.BOOT + '_')
LOG.info('SSP: Disk name is %s', boot_lu_name, instance=instance)
return tsk_stg.crt_lu(
self._tier, boot_lu_name, instance.flavor.root_gb,
typ=pvm_stg.LUType.DISK, clone=image_lu)[1]
def attach_disk(self, instance, disk_info, stg_ftsk):
"""Connects the disk image to the Virtual Machine.
:param instance: nova instance to which to attach the disk.
:param disk_info: The pypowervm storage element returned from
create_disk_from_image. Ex. VOptMedia, VDisk, LU,
or PV.
:param stg_ftsk: FeedTask to defer storage connectivity operations.
"""
# Create the LU structure
lu = pvm_stg.LU.bld_ref(self._adapter, disk_info.name, disk_info.udid)
lpar_uuid = vm.get_pvm_uuid(instance)
# This is the delay apply mapping
def add_func(vios_w):
LOG.info("Attaching SSP disk from VIOS %s.",
vios_w.name, instance=instance)
mapping = tsk_map.build_vscsi_mapping(
self._host_uuid, vios_w, lpar_uuid, lu)
return tsk_map.add_map(vios_w, mapping)
# Add the mapping to *each* VIOS on the LPAR's host.
# The LPAR's host has to be self._host_uuid, else the PowerVM API will
# fail.
#
# Note: this may not be all the VIOSes on the system - just the ones
# in the SSP cluster.
for vios_uuid in self._vios_uuids:
stg_ftsk.wrapper_tasks[vios_uuid].add_functor_subtask(add_func)
@property
def _vios_uuids(self):
"""List the UUIDs of our cluster's VIOSes on this host.
(If a VIOS is not on this host, we can't interact with it, even if its
URI and therefore its UUID happen to be available in the pypowervm
wrapper.)
:return: A list of VIOS UUID strings.
"""
ret = []
for n in self._clust.nodes:
# Skip any nodes that we don't have the VIOS uuid or uri
if not (n.vios_uuid and n.vios_uri):
continue
if self._host_uuid == pvm_u.get_req_path_uuid(
n.vios_uri, preserve_case=True, root=True):
ret.append(n.vios_uuid)
return ret
def disconnect_disk_from_mgmt(self, vios_uuid, disk_name):
"""Disconnect a disk from the management partition.
:param vios_uuid: The UUID of the Virtual I/O Server serving the
mapping.
:param disk_name: The name of the disk to unmap.
"""
tsk_map.remove_lu_mapping(self._adapter, vios_uuid, self.mp_uuid,
disk_names=[disk_name])
LOG.info("Unmapped boot disk %(disk_name)s from the management "
"partition from Virtual I/O Server %(vios_uuid)s.",
{'disk_name': disk_name, 'mp_uuid': self.mp_uuid,
'vios_uuid': vios_uuid})
@staticmethod
def _disk_match_func(disk_type, instance):
"""Return a matching function to locate the disk for an instance.
:param disk_type: One of the DiskType enum values.
:param instance: The instance whose disk is to be found.
:return: Callable suitable for the match_func parameter of the
pypowervm.tasks.scsi_mapper.find_maps method.
"""
disk_name = SSPDiskAdapter._get_disk_name(disk_type, instance)
return tsk_map.gen_match_func(pvm_stg.LU, names=[disk_name])
-709
View File
@@ -1,709 +0,0 @@
# Copyright 2014, 2018 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Connection to PowerVM hypervisor through NovaLink."""
import os_resource_classes as orc
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import importutils
from pypowervm import adapter as pvm_apt
from pypowervm import const as pvm_const
from pypowervm import exceptions as pvm_exc
from pypowervm.helpers import log_helper as log_hlp
from pypowervm.helpers import vios_busy as vio_hlp
from pypowervm.tasks import partition as pvm_par
from pypowervm.tasks import storage as pvm_stor
from pypowervm.tasks import vterm as pvm_vterm
from pypowervm.wrappers import managed_system as pvm_ms
from taskflow.patterns import linear_flow as tf_lf
from nova.compute import task_states
from nova import conf as cfg
from nova.console import type as console_type
from nova import exception as exc
from nova.i18n import _
from nova.image import glance
from nova.virt import configdrive
from nova.virt import driver
from nova.virt.powervm import host as pvm_host
from nova.virt.powervm.tasks import base as tf_base
from nova.virt.powervm.tasks import image as tf_img
from nova.virt.powervm.tasks import network as tf_net
from nova.virt.powervm.tasks import storage as tf_stg
from nova.virt.powervm.tasks import vm as tf_vm
from nova.virt.powervm import vm
from nova.virt.powervm import volume
from nova.virt.powervm.volume import fcvscsi
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
DISK_ADPT_NS = 'nova.virt.powervm.disk'
DISK_ADPT_MAPPINGS = {
'localdisk': 'localdisk.LocalStorage',
'ssp': 'ssp.SSPDiskAdapter'
}
class PowerVMDriver(driver.ComputeDriver):
"""PowerVM NovaLink Implementation of Compute Driver.
https://wiki.openstack.org/wiki/PowerVM
"""
def __init__(self, virtapi):
# NOTE(edmondsw) some of these will be dynamic in future, so putting
# capabilities on the instance rather than on the class.
self.capabilities = {
'has_imagecache': False,
'supports_bfv_rescue': False,
'supports_evacuate': False,
'supports_migrate_to_same_host': False,
'supports_attach_interface': True,
'supports_device_tagging': False,
'supports_tagged_attach_interface': False,
'supports_tagged_attach_volume': False,
'supports_extend_volume': True,
'supports_multiattach': False,
'supports_trusted_certs': False,
'supports_pcpus': False,
'supports_accelerators': False,
'supports_vtpm': False,
'supports_secure_boot': False,
'supports_socket_pci_numa_affinity': False,
'supports_remote_managed_ports': False,
# Supported image types
"supports_image_type_aki": False,
"supports_image_type_ami": False,
"supports_image_type_ari": False,
"supports_image_type_iso": False,
"supports_image_type_qcow2": False,
"supports_image_type_raw": True,
"supports_image_type_vdi": False,
"supports_image_type_vhd": False,
"supports_image_type_vhdx": False,
"supports_image_type_vmdk": False,
"supports_image_type_ploop": False,
}
super(PowerVMDriver, self).__init__(virtapi)
def init_host(self, host):
"""Initialize anything that is necessary for the driver to function.
Includes catching up with currently running VMs on the given host.
"""
LOG.warning(
'The powervm virt driver is deprecated and may be removed in a '
'future release. The driver is not tested by the OpenStack '
'project nor does it have clear maintainers and thus its quality'
'can not be ensured. If you are using the driver in production '
'please let us know the openstack-discuss mailing list or on IRC'
)
# Build the adapter. May need to attempt the connection multiple times
# in case the PowerVM management API service is starting.
# TODO(efried): Implement async compute service enable/disable like
# I73a34eb6e0ca32d03e54d12a5e066b2ed4f19a61
self.adapter = pvm_apt.Adapter(
pvm_apt.Session(conn_tries=60),
helpers=[log_hlp.log_helper, vio_hlp.vios_busy_retry_helper])
# Make sure the Virtual I/O Server(s) are available.
pvm_par.validate_vios_ready(self.adapter)
self.host_wrapper = pvm_ms.System.get(self.adapter)[0]
# Do a scrub of the I/O plane to make sure the system is in good shape
LOG.info("Clearing stale I/O connections on driver init.")
pvm_stor.ComprehensiveScrub(self.adapter).execute()
# Initialize the disk adapter
self.disk_dvr = importutils.import_object_ns(
DISK_ADPT_NS, DISK_ADPT_MAPPINGS[CONF.powervm.disk_driver.lower()],
self.adapter, self.host_wrapper.uuid)
self.image_api = glance.API()
LOG.info("The PowerVM compute driver has been initialized.")
@staticmethod
def _log_operation(op, instance):
"""Log entry point of driver operations."""
LOG.info('Operation: %(op)s. Virtual machine display name: '
'%(display_name)s, name: %(name)s',
{'op': op, 'display_name': instance.display_name,
'name': instance.name}, instance=instance)
def get_info(self, instance, use_cache=True):
"""Get the current status of an instance.
:param instance: nova.objects.instance.Instance object
:param use_cache: unused in this driver
:returns: An InstanceInfo object.
"""
return vm.get_vm_info(self.adapter, instance)
def list_instances(self):
"""Return the names of all the instances known to the virt host.
:return: VM Names as a list.
"""
return vm.get_lpar_names(self.adapter)
def get_available_nodes(self, refresh=False):
"""Returns nodenames of all nodes managed by the compute service.
This method is for multi compute-nodes support. If a driver supports
multi compute-nodes, this method returns a list of nodenames managed
by the service. Otherwise, this method should return
[hypervisor_hostname].
"""
return [CONF.host]
def get_available_resource(self, nodename):
"""Retrieve resource information.
This method is called when nova-compute launches, and as part of a
periodic task.
:param nodename: Node from which the caller wants to get resources.
A driver that manages only one node can safely ignore
this.
:return: Dictionary describing resources.
"""
# Do this here so it refreshes each time this method is called.
self.host_wrapper = pvm_ms.System.get(self.adapter)[0]
return self._get_available_resource()
def _get_available_resource(self):
# Get host information
data = pvm_host.build_host_resource_from_ms(self.host_wrapper)
# Add the disk information
data["local_gb"] = self.disk_dvr.capacity
data["local_gb_used"] = self.disk_dvr.capacity_used
return data
def update_provider_tree(self, provider_tree, nodename, allocations=None):
"""Update a ProviderTree with current provider and inventory data.
:param nova.compute.provider_tree.ProviderTree provider_tree:
A nova.compute.provider_tree.ProviderTree object representing all
the providers in the tree associated with the compute node, and any
sharing providers (those with the ``MISC_SHARES_VIA_AGGREGATE``
trait) associated via aggregate with any of those providers (but
not *their* tree- or aggregate-associated providers), as currently
known by placement.
:param nodename:
String name of the compute node (i.e.
ComputeNode.hypervisor_hostname) for which the caller is requesting
updated provider information.
:param allocations: Currently ignored by this driver.
"""
# Get (legacy) resource information. Same as get_available_resource,
# but we don't need to refresh self.host_wrapper as it was *just*
# refreshed by get_available_resource in the resource tracker's
# update_available_resource flow.
data = self._get_available_resource()
# NOTE(yikun): If the inv record does not exists, the allocation_ratio
# will use the CONF.xxx_allocation_ratio value if xxx_allocation_ratio
# is set, and fallback to use the initial_xxx_allocation_ratio
# otherwise.
inv = provider_tree.data(nodename).inventory
ratios = self._get_allocation_ratios(inv)
# TODO(efried): Fix these to reflect something like reality
cpu_reserved = CONF.reserved_host_cpus
mem_reserved = CONF.reserved_host_memory_mb
disk_reserved = self._get_reserved_host_disk_gb_from_config()
inventory = {
orc.VCPU: {
'total': data['vcpus'],
'max_unit': data['vcpus'],
'allocation_ratio': ratios[orc.VCPU],
'reserved': cpu_reserved,
},
orc.MEMORY_MB: {
'total': data['memory_mb'],
'max_unit': data['memory_mb'],
'allocation_ratio': ratios[orc.MEMORY_MB],
'reserved': mem_reserved,
},
orc.DISK_GB: {
# TODO(efried): Proper DISK_GB sharing when SSP driver in play
'total': int(data['local_gb']),
'max_unit': int(data['local_gb']),
'allocation_ratio': ratios[orc.DISK_GB],
'reserved': disk_reserved,
},
}
provider_tree.update_inventory(nodename, inventory)
def spawn(self, context, instance, image_meta, injected_files,
admin_password, allocations, network_info=None,
block_device_info=None, power_on=True, accel_info=None):
"""Create a new instance/VM/domain on the virtualization platform.
Once this successfully completes, the instance should be
running (power_state.RUNNING).
If this fails, any partial instance should be completely
cleaned up, and the virtualization platform should be in the state
that it was before this call began.
:param context: security context
:param instance: nova.objects.instance.Instance
This function should use the data there to guide
the creation of the new instance.
:param nova.objects.ImageMeta image_meta:
The metadata of the image of the instance.
:param injected_files: User files to inject into instance.
:param admin_password: Administrator password to set in instance.
:param allocations: Information about resources allocated to the
instance via placement, of the form returned by
SchedulerReportClient.get_allocations_for_consumer.
:param network_info: instance network information
:param block_device_info: Information about block devices to be
attached to the instance.
:param power_on: True if the instance should be powered on, False
otherwise
"""
self._log_operation('spawn', instance)
# Define the flow
flow_spawn = tf_lf.Flow("spawn")
# This FeedTask accumulates VIOS storage connection operations to be
# run in parallel. Include both SCSI and fibre channel mappings for
# the scrubber.
stg_ftsk = pvm_par.build_active_vio_feed_task(
self.adapter, xag={pvm_const.XAG.VIO_SMAP, pvm_const.XAG.VIO_FMAP})
flow_spawn.add(tf_vm.Create(
self.adapter, self.host_wrapper, instance, stg_ftsk))
# Create a flow for the IO
flow_spawn.add(tf_net.PlugVifs(
self.virtapi, self.adapter, instance, network_info))
flow_spawn.add(tf_net.PlugMgmtVif(
self.adapter, instance))
# Create the boot image.
flow_spawn.add(tf_stg.CreateDiskForImg(
self.disk_dvr, context, instance, image_meta))
# Connects up the disk to the LPAR
flow_spawn.add(tf_stg.AttachDisk(
self.disk_dvr, instance, stg_ftsk=stg_ftsk))
# Extract the block devices.
bdms = driver.block_device_info_get_mapping(block_device_info)
# Determine if there are volumes to connect. If so, add a connection
# for each type.
for bdm, vol_drv in self._vol_drv_iter(context, instance, bdms,
stg_ftsk=stg_ftsk):
# Connect the volume. This will update the connection_info.
flow_spawn.add(tf_stg.AttachVolume(vol_drv))
# If the config drive is needed, add those steps. Should be done
# after all the other I/O.
if configdrive.required_by(instance):
flow_spawn.add(tf_stg.CreateAndConnectCfgDrive(
self.adapter, instance, injected_files, network_info,
stg_ftsk, admin_pass=admin_password))
# Add the transaction manager flow at the end of the 'I/O
# connection' tasks. This will run all the connections in parallel.
flow_spawn.add(stg_ftsk)
# Last step is to power on the system.
flow_spawn.add(tf_vm.PowerOn(self.adapter, instance))