From 7437ae07cf02035521d9e9bc816822f32508996f Mon Sep 17 00:00:00 2001 From: Ahmad Musa <53178237+ahmadiesa-abu@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:29:59 +0300 Subject: [PATCH] add-plugin_1_5 (#260) --- CHANGELOG.txt | 2 + cloudify_vsphere/__version__.py | 2 +- cloudify_vsphere/contentlibrary/deployment.py | 14 +- .../tests/test_content_deployment.py | 15 +- cloudify_vsphere/devices/__init__.py | 103 +- .../devices/tests/test_controllers.py | 19 +- cloudify_vsphere/ovf/__init__.py | 13 +- .../resource_pool/tests/test_resource_pool.py | 15 +- plugin.yaml | 2 +- plugin_1_4.yaml | 2 +- plugin_1_5.yaml | 1393 +++++++++++++++++ v2_plugin.yaml | 2 +- vsphere_network_plugin/tests/ippool_test.py | 15 +- vsphere_plugin_common/__init__.py | 10 +- vsphere_plugin_common/utils.py | 8 + vsphere_server_plugin/power.py | 32 +- vsphere_server_plugin/tests/test_backup.py | 13 +- vsphere_storage_plugin/cidata.py | 36 +- vsphere_storage_plugin/tests/cidata_test.py | 15 +- vsphere_storage_plugin/tests/storage_test.py | 15 +- 20 files changed, 1663 insertions(+), 63 deletions(-) create mode 100644 plugin_1_5.yaml diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 071eb005..6264f32a 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,5 @@ +2.20.8: + - add plugin_1_5.yaml and handle ctx.plugin properties. 2.20.7: - Fix Extra_config when creating new server. - Make cpu/memory hot_add/remove configurable. diff --git a/cloudify_vsphere/__version__.py b/cloudify_vsphere/__version__.py index eac8ebbd..a4956112 100644 --- a/cloudify_vsphere/__version__.py +++ b/cloudify_vsphere/__version__.py @@ -1 +1 @@ -version = '2.20.7' +version = '2.20.8' diff --git a/cloudify_vsphere/contentlibrary/deployment.py b/cloudify_vsphere/contentlibrary/deployment.py index a345418a..bce25fb2 100644 --- a/cloudify_vsphere/contentlibrary/deployment.py +++ b/cloudify_vsphere/contentlibrary/deployment.py @@ -23,7 +23,11 @@ # This package imports from vsphere_plugin_common import with_server_client -from vsphere_plugin_common.utils import op, is_node_deprecated +from vsphere_plugin_common.utils import ( + op, + is_node_deprecated, + get_plugin_properties +) from vsphere_plugin_common import ( remove_runtime_properties, ) @@ -48,7 +52,13 @@ def create(ctx, connection_config, library_name, template_name, target, vm_name=repr(runtime_properties.get(CONTENT_LIBRARY_VM_NAME)))) return - content = ContentLibrary(connection_config) + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.node.properties['connection_config'] + if connection_config: + vsphere_config.update(connection_config) + + content = ContentLibrary(vsphere_config) library = content.content_library_get(library_name) content_library_id = library["id"] runtime_properties[CONTENT_LIBRARY_ID] = content_library_id diff --git a/cloudify_vsphere/contentlibrary/tests/test_content_deployment.py b/cloudify_vsphere/contentlibrary/tests/test_content_deployment.py index 25f42049..9544e22f 100644 --- a/cloudify_vsphere/contentlibrary/tests/test_content_deployment.py +++ b/cloudify_vsphere/contentlibrary/tests/test_content_deployment.py @@ -14,7 +14,7 @@ import unittest -from mock import Mock, patch +from mock import Mock, patch, MagicMock from pyVmomi import vim from cloudify.state import current_ctx @@ -24,11 +24,22 @@ import cloudify_vsphere.contentlibrary.deployment as deployment +class SpecialMockCloudifyContext(MockCloudifyContext): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._plugin = MagicMock(properties={}) + + @property + def plugin(self): + return self._plugin + + class ContentDeploymentTest(unittest.TestCase): def setUp(self): super(ContentDeploymentTest, self).setUp() - self.mock_ctx = MockCloudifyContext( + self.mock_ctx = SpecialMockCloudifyContext( 'node_name', properties={ "connection_config": { diff --git a/cloudify_vsphere/devices/__init__.py b/cloudify_vsphere/devices/__init__.py index 9e75b786..f1865194 100644 --- a/cloudify_vsphere/devices/__init__.py +++ b/cloudify_vsphere/devices/__init__.py @@ -24,7 +24,8 @@ from vsphere_plugin_common.utils import ( op, find_rels_by_type, - is_node_deprecated) + is_node_deprecated, + get_plugin_properties) from vsphere_plugin_common.clients.server import ( ServerClient, set_boot_order) @@ -115,8 +116,14 @@ def attach_scsi_controller(ctx, **kwargs): ctx.logger.debug("Source {0}".format(repr(scsi_properties))) ctx.logger.debug("Target {0}".format(repr(hostvm_properties))) + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) + cl = ControllerClient() - cl.get(config=ctx.source.node.properties.get("connection_config")) + cl.get(config=vsphere_config) run_deferred_task(cl, ctx.source.instance) @@ -142,8 +149,14 @@ def attach_ethernet_card(ctx, **kwargs): ctx.logger.info("Controller attached with {buskey} key.".format( buskey=ctx.source.instance.runtime_properties['busKey'])) return + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) + attachment = _attach_ethernet_card( - ctx.source.node.properties.get("connection_config"), + vsphere_config, ctx.target.instance.runtime_properties.get(VSPHERE_SERVER_ID), controller_without_connected_networks( ctx.source.instance.runtime_properties), @@ -162,8 +175,13 @@ def attach_server_to_ethernet_card(ctx, **kwargs): if ctx.target.instance.id not in \ ctx.source.instance.runtime_properties.get( VSPHERE_SERVER_CONNECTED_NICS, []): + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.target.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) attachment = _attach_ethernet_card( - ctx.target.node.properties.get("connection_config"), + vsphere_config, ctx.source.instance.runtime_properties.get(VSPHERE_SERVER_ID), controller_without_connected_networks( ctx.target.instance.runtime_properties), @@ -171,8 +189,13 @@ def attach_server_to_ethernet_card(ctx, **kwargs): ctx.target.instance.runtime_properties.update(attachment) ctx.target.instance.runtime_properties.dirty = True ctx.target.instance.update() + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) ip = _get_card_ip( - ctx.source.node.properties.get("connection_config"), + vsphere_config, ctx.source.instance.runtime_properties.get(VSPHERE_SERVER_ID), ctx.target.instance.runtime_properties.get('name')) ctx.source.instance.runtime_properties[IP] = ip @@ -185,8 +208,13 @@ def detach_controller(ctx, **kwargs): if 'busKey' not in ctx.source.instance.runtime_properties: ctx.logger.info("Controller was not attached, skipping.") return + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) _detach_controller( - ctx.source.node.properties.get("connection_config"), + vsphere_config, ctx.target.instance.runtime_properties.get(VSPHERE_SERVER_ID), ctx.source.instance.runtime_properties.get('busKey'), instance=ctx.source.instance) @@ -202,8 +230,13 @@ def detach_server_from_controller(ctx, **kwargs): if 'busKey' not in ctx.target.instance.runtime_properties: ctx.logger.info("Controller was not attached, skipping.") return + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.target.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) _detach_controller( - ctx.target.node.properties.get("connection_config"), + vsphere_config, ctx.source.instance.runtime_properties.get(VSPHERE_SERVER_ID), ctx.target.instance.runtime_properties.get('busKey'), instance=ctx.target.instance) @@ -300,11 +333,16 @@ def attach_usb_device(ctx, **kwargs): return vsphere_server_id = ctx.target.instance.runtime_properties.get( 'vsphere_server_id') - connection_config_props = ctx.source.node.properties.get( - 'connection_config') + + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) + device_name_from_props = ctx.source.node.properties.get('device_name') cl = ServerClient() - cl.get(config=connection_config_props) + cl.get(config=vsphere_config) vm = cl._get_obj_by_id(vim.VirtualMachine, vsphere_server_id) device_name = get_usb_physical_path(cl.si.content, @@ -357,11 +395,14 @@ def attach_usb_device(ctx, **kwargs): def detach_usb_device(ctx, **kwargs): vsphere_server_id = ctx.target.instance.runtime_properties.get( 'vsphere_server_id') - connection_config_props = ctx.source.node.properties.get( - 'connection_config') + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) device_name_from_props = ctx.source.node.properties.get('device_name') cl = ServerClient() - cl.get(config=connection_config_props) + cl.get(config=vsphere_config) vm = cl._get_obj_by_id(vim.VirtualMachine, vsphere_server_id) usb_device = None @@ -389,11 +430,14 @@ def attach_serial_port(ctx, **kwargs): return vsphere_server_id = ctx.target.instance.runtime_properties.get( 'vsphere_server_id') - connection_config_props = ctx.source.node.properties.get( - 'connection_config') + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) device_name_from_props = ctx.source.node.properties.get('device_name') cl = ServerClient() - cl.get(config=connection_config_props) + cl.get(config=vsphere_config) vm = cl._get_obj_by_id(vim.VirtualMachine, vsphere_server_id) device_changes = [] @@ -431,11 +475,14 @@ def attach_serial_port(ctx, **kwargs): def detach_serial_port(ctx, **kwargs): vsphere_server_id = ctx.target.instance.runtime_properties.get( 'vsphere_server_id') - connection_config_props = ctx.source.node.properties.get( - 'connection_config') + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) device_name_from_props = ctx.source.node.properties.get('device_name') cl = ServerClient() - cl.get(config=connection_config_props) + cl.get(config=vsphere_config) vm = cl._get_obj_by_id(vim.VirtualMachine, vsphere_server_id) serial_port = None @@ -501,11 +548,14 @@ def attach_pci_device(ctx, **kwargs): return vsphere_server_id = ctx.target.instance.runtime_properties.get( 'vsphere_server_id') - connection_config_props = ctx.source.node.properties.get( - 'connection_config') + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) device_name_from_props = ctx.source.node.properties.get('device_name') cl = ServerClient() - cl.get(config=connection_config_props) + cl.get(config=vsphere_config) vm = cl._get_obj_by_id(vim.VirtualMachine, vsphere_server_id) pci_device = get_pci_device(cl.si.content, @@ -556,11 +606,14 @@ def attach_pci_device(ctx, **kwargs): def detach_pci_device(ctx, **kwargs): vsphere_server_id = ctx.target.instance.runtime_properties.get( 'vsphere_server_id') - connection_config_props = ctx.source.node.properties.get( - 'connection_config') + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.source.node.properties.get('connection_config') + if connection_config: + vsphere_config.update(connection_config) device_name_from_props = ctx.source.node.properties.get('device_name') cl = ServerClient() - cl.get(config=connection_config_props) + cl.get(config=vsphere_config) vm = cl._get_obj_by_id(vim.VirtualMachine, vsphere_server_id) pci_details = get_pci_device(cl.si.content, diff --git a/cloudify_vsphere/devices/tests/test_controllers.py b/cloudify_vsphere/devices/tests/test_controllers.py index 69ec0942..ed7cb5a5 100644 --- a/cloudify_vsphere/devices/tests/test_controllers.py +++ b/cloudify_vsphere/devices/tests/test_controllers.py @@ -25,6 +25,17 @@ from cloudify_vsphere import devices +class SpecialMockCloudifyContext(MockCloudifyContext): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._plugin = MagicMock(properties={}) + + @property + def plugin(self): + return self._plugin + + class VsphereControllerTest(unittest.TestCase): def tearDown(self): @@ -32,7 +43,7 @@ def tearDown(self): super(VsphereControllerTest, self).tearDown() def _gen_ctx(self): - _ctx = MockCloudifyContext( + _ctx = SpecialMockCloudifyContext( 'node_name', properties={}, runtime_properties={} @@ -45,18 +56,18 @@ def _gen_ctx(self): return _ctx def _gen_relation_ctx(self): - _target = MockCloudifyContext( + _target = SpecialMockCloudifyContext( 'target', properties={}, runtime_properties={} ) - _source = MockCloudifyContext( + _source = SpecialMockCloudifyContext( 'source', properties={}, runtime_properties={} ) - _ctx = MockCloudifyContext( + _ctx = SpecialMockCloudifyContext( target=_target, source=_source ) diff --git a/cloudify_vsphere/ovf/__init__.py b/cloudify_vsphere/ovf/__init__.py index 4a90fbca..7c742395 100644 --- a/cloudify_vsphere/ovf/__init__.py +++ b/cloudify_vsphere/ovf/__init__.py @@ -32,7 +32,7 @@ from vsphere_plugin_common.clients.server import ( ServerClient, get_boot_order_obj) -from vsphere_plugin_common.utils import op +from vsphere_plugin_common.utils import op, get_plugin_properties from vsphere_plugin_common import ( remove_runtime_properties, ) @@ -262,12 +262,19 @@ def create(ctx, connection_config, target, ovf_name, ovf_source, esxi_node = target.get('host') vm_folder = target.get('folder') resource_pool = target.get('resource_pool') + + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.node.properties['connection_config'] + if connection_config: + vsphere_config.update(connection_config) + client = ServerClient(ctx_logger=ctx.logger).get( - config=connection_config) + config=vsphere_config) datacenter = client.si.content.rootFolder.childEntity[0] if not resource_pool: - resource_pool = connection_config.get("resource_pool_name") + resource_pool = vsphere_config.get("resource_pool_name") resource_pool = get_obj_in_list(resource_pool, client._get_resource_pools()) diff --git a/cloudify_vsphere/resource_pool/tests/test_resource_pool.py b/cloudify_vsphere/resource_pool/tests/test_resource_pool.py index 7ba72e68..2b322f2e 100644 --- a/cloudify_vsphere/resource_pool/tests/test_resource_pool.py +++ b/cloudify_vsphere/resource_pool/tests/test_resource_pool.py @@ -14,7 +14,7 @@ import unittest -from mock import Mock, patch +from mock import Mock, patch, MagicMock from cloudify.state import current_ctx from cloudify.mocks import MockCloudifyContext @@ -24,11 +24,22 @@ from cloudify_vsphere import resource_pool +class SpecialMockCloudifyContext(MockCloudifyContext): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._plugin = MagicMock(properties={}) + + @property + def plugin(self): + return self._plugin + + class ResourcePoolTest(unittest.TestCase): def setUp(self): super(ResourcePoolTest, self).setUp() - self.mock_ctx = MockCloudifyContext( + self.mock_ctx = SpecialMockCloudifyContext( 'node_name', properties={}, runtime_properties={} diff --git a/plugin.yaml b/plugin.yaml index 7cad3142..ad06b32c 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -6,7 +6,7 @@ plugins: vsphere: executor: central_deployment_agent package_name: cloudify-vsphere-plugin - package_version: '2.20.7' + package_version: '2.20.8' data_types: diff --git a/plugin_1_4.yaml b/plugin_1_4.yaml index ab5dbc22..462075f4 100755 --- a/plugin_1_4.yaml +++ b/plugin_1_4.yaml @@ -6,7 +6,7 @@ plugins: vsphere: executor: central_deployment_agent package_name: cloudify-vsphere-plugin - package_version: '2.20.7' + package_version: '2.20.8' data_types: diff --git a/plugin_1_5.yaml b/plugin_1_5.yaml new file mode 100644 index 00000000..0f5e982b --- /dev/null +++ b/plugin_1_5.yaml @@ -0,0 +1,1393 @@ +################################################################################## +# Cloudify vSphere built in types and plugins definitions. +################################################################################## + +plugins: + vsphere: + executor: central_deployment_agent + package_name: cloudify-vsphere-plugin + package_version: '2.20.8' + properties_description: | + Manage vSphere resources. + properties: + username: + type: string + display_label: vSphere username. + description: vSphere username. + host: + type: string + display_label: vCenter hostname or IP address. + description: vCenter hostname or IP address. + password: + type: string + display_label: vCenter password. + description: vCenter password. + datacenter_name: + type: string + display_label: datacenter name. + description: datacenter name. + auto_placement: + type: string + display_label: Auto Placement flag. + description: auto-placement logic from the plugin. + resource_pool_name: + type: string + display_label: Name of a resource pool. + description: Name of a resource pool. + port: + type: integer + display_label: vCenter port for SDK. + description: vCenter port for SDK. + allow_insecure: + type: boolean + display_label: Allow insecure connections. + description: Whether to allow insecure connections. + certificate_path: + type: string + display_label: Certificate path for the vCenter. + description: The path to the PEM encoded certificate for the vCenter. + +data_types: + + cloudify.datatypes.vsphere.Config: + properties: + username: + description: > + vSphere username. + type: string + required: false + host: + description: > + vCenter hostname or IP address. + type: string + required: false + password: + description: > + vCenter password. + type: string + required: false + datacenter_name: + description: > + datacenter name. + type: string + required: false + auto_placement: + description: > + Must be true if you are using clusters. + Disabled `auto placement` is not recommended for a cluster. + If `auto placement` set to false, plugin will provide recommendation + to vsphere to place vm to selected host, vsphere can move vm to other + host by internal logic. + type: string + required: false + resource_pool_name: + description: > + Name of a resource pool. + Defaults to Resources, which is a hidden resource pool on vSphere. + type: string + required: false + port: + description: > + vCenter port for SDK. + type: integer + required: false + allow_insecure: + description: > + Whether to allow insecure connections. Defaults to false, but this is not + yet enforced on Python versions older than 2.7.9. + Python versions pre-2.7.9 can only make insecure connections, which will + fail in the next major version of this plugin unless this is set to false. + type: boolean + required: false + certificate_path: + description: > + The path to the PEM encoded certificate for the vCenter. This will be used + on Python 2.7.9 and above to verify the SSL connection. + On older versions of python the connection will be insecure. + It is not supported to set this while allow_insecure is set to 'true'. + type: string + required: false + + cloudify.datatypes.vsphere.ServerProperties: + properties: + memory: + description: > + Amount of RAM, in MB. + type: integer + required: false + cpus: + description: > + Number of CPUs. + type: integer + required: false + template: + description: > + Virtual machine template from which server will be spawned. + See full documentation at docs.getcloudify.org for requirements. + type: string + required: false + name: + description: > + Server name. + Will use node name if this is not set. + Must consist of only ASCII letters, numbers, and hyphens. + Will automatically convert underscores to hyphens. + type: string + required: false + add_scale_suffix: + description: > + Whether or not to add a suffix to the server name. + This setting is meaningful only when the name property is specified. + It must be true - which is the default - if such a server is to be + scaled to more than one instance. + type: boolean + default: true + clone_vm: + description: > + Virtual machine from which server will be cloned. + type: string + required: false + disk_provision_type: + description: > + disk provisioning type. + it can be one of these values: + - thickLazyZeroed + - thickEagerZeroed + - thin + type: string + required: false + disk_size: + description: > + Disk size in GBs. + type: integer + required: false + cpu_hot_add: + description: > + control whether to have cpu_hot_add enabled or not. + type: boolean + default: true + cpu_hot_remove: + description: > + control whether to have cpu_hot_remove enabled or not. + type: boolean + default: true + memory_hot_add: + description: > + control whether to have memory_hot_add enabled or not. + type: boolean + default: true + + cloudify.datatypes.vsphere.NetworkingProperties: + properties: + domain: + description: > + the domain for this server. + Combined with the hostname this will produce the fully-qualified domain name + (e.g. if ``domain`` is ``example.local`` and the host name is ``test-abc123`` + then the fully-qualified domain name will be ``test-abc123.example.local``) + type: string + required: false + dns_servers: + description: > + List of DNS servers. + required: false + connect_networks: + description: | + List of network interfaces to connect. + These should be in the form of dicts with: + name: The name of the network on vsphere, or of the related node if from_relationship is true. + from_relationship: true/false- determines whether to use a relationship from a connected node (default false) + management: true/false- determines if this is a management network interface (default false) + external: true/false- determines if this is a external network interface (default false) + switch_distributed: determines if this is connected to a distributed port group (default false) + nsx_t_switch: determines if this is connected to a NSX-T Logical Switch (default false) + use_dhcp: whether to use DHCP for IP addressing (default true) + network: network CIDR to use if use_dhcp is false + gateway: default gateway to use if use_dhcp is false. You should only set this on one interface. + ip: IP address to use if use_dhcp is false + required: false + +node_types: + + + cloudify.vsphere.nodes.Server: + derived_from: cloudify.nodes.vsphere.Server + + cloudify.nodes.vsphere.Server: + derived_from: cloudify.nodes.Compute + properties: + use_external_resource: + description: > + Whether to use a vm that already exists on vSphere. + type: boolean + default: false + allowed_hosts: + description: > + Which ESX host(s) this server is allowed to be deployed on. + required: false + allowed_clusters: + description: > + Which ESX cluster(s) this server is allowed to be deployed on. + required: false + allowed_datastores: + description: > + Which ESX datastore(s) this server is allowed to be deployed on. + required: false + server: + type: cloudify.datatypes.vsphere.ServerProperties + networking: + type: cloudify.datatypes.vsphere.NetworkingProperties + custom_attributes: + description: | + Dictionary of custom attribute keys & values. + Keys which don't yet exist on the platform will + be created automatically. :: + custom_attributes: + key: value + lock: locked + keyring: empty + required: false + cdrom_image: + description: | + Cdrom image path + default: false + extra_config: + description: | + Extra config to set, key-value dictionary + default: {} + wait_ip: + description: | + Use guest exported ip as default. + default: false + vm_folder: + description: | + Folder name for place new created VM + default: false + connection_config: + type: cloudify.datatypes.vsphere.Config + force_stop: + description: | + Force stop vm for external resource + default: false + force_delete: + description: | + Force delete vm for external resource + default: false + enable_start_vm: + description: | + Enable start (power on) operation for VM. + default: true + postpone_delete_networks: + description: | + Remover networks only after create VM. + default: false + minimal_vm_version: + description: | + Set minimal version of vm + default: 13 + boot_order: + description: | + The order of boot + Example: + - cdrom + - hdd + - ethernet + In above list will be provided, vsphere try to boot from cdrom,then + from hdd (all disk_keys) and at the end from ethernet (ethernet_keys) + type: list + required: false + disk_keys: + type: list + required: false + description: | + The order of hdd boot - will be used only if boot_order provided. + If empty, the parameter will be generated based on vm properties (considering all hdd disks) + ethernet_keys: + type: list + required: false + description: | + The order of ethernet boot - will be used only if boot_order provided. + If empty, the parameter will be generated based on vm properties (considering all ethernet devices) + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.vsphere_server_plugin.server.create + inputs: &interfaces_power_inputs + max_wait_time: + type: integer + default: 300 + description: > + How long to wait for the operation to complete before retry. + start: + implementation: vsphere.vsphere_server_plugin.server.start + inputs: *interfaces_power_inputs + poststart: + implementation: vsphere.vsphere_server_plugin.server.poststart + inputs: {} + stop: + implementation: vsphere.vsphere_server_plugin.server.stop + inputs: *interfaces_power_inputs + shutdown_guest: + implementation: vsphere.vsphere_server_plugin.server.shutdown_guest + inputs: *interfaces_power_inputs + delete: + implementation: vsphere.vsphere_server_plugin.server.delete + inputs: *interfaces_power_inputs + check_drift: + implementation: vsphere.vsphere_server_plugin.server.check_drift + inputs: {} + update: + implementation: vsphere.vsphere_server_plugin.server.update + inputs: {} + # suspend/resume + cloudify.interfaces.freeze: + suspend: + implementation: vsphere.vsphere_server_plugin.server.freeze_suspend + inputs: *interfaces_power_inputs + resume: + implementation: vsphere.vsphere_server_plugin.server.freeze_resume + inputs: *interfaces_power_inputs + # backups related section + cloudify.interfaces.snapshot: + create: + implementation: vsphere.vsphere_server_plugin.server.snapshot_create + inputs: *interfaces_power_inputs + apply: + implementation: vsphere.vsphere_server_plugin.server.snapshot_apply + inputs: *interfaces_power_inputs + delete: + implementation: vsphere.vsphere_server_plugin.server.snapshot_delete + inputs: *interfaces_power_inputs + cloudify.interfaces.host: + get_state: + implementation: vsphere.vsphere_server_plugin.server.get_state + inputs: + minimum_wait_time: + type: integer + default: 0 + description: | + The time it takes for the machine to up, should be in seconds. + <<: *interfaces_power_inputs + cloudify.interfaces.modify: + resize: + implementation: vsphere.vsphere_server_plugin.server.resize_server + inputs: + <<: *interfaces_power_inputs + hot_add: + default: true + cloudify.interfaces.power: + 'on': + implementation: vsphere.vsphere_server_plugin.power.power_on + inputs: *interfaces_power_inputs + 'off': + implementation: vsphere.vsphere_server_plugin.power.power_off + inputs: *interfaces_power_inputs + reset: + implementation: vsphere.vsphere_server_plugin.power.reset + inputs: *interfaces_power_inputs + reboot: + implementation: vsphere.vsphere_server_plugin.power.reboot + inputs: *interfaces_power_inputs + shut_down: + implementation: vsphere.vsphere_server_plugin.power.shut_down + inputs: *interfaces_power_inputs + + cloudify.vsphere.nodes.WindowsServer: + derived_from: cloudify.nodes.vsphere.WindowsServer + + cloudify.nodes.vsphere.WindowsServer: + derived_from: cloudify.vsphere.nodes.Server + properties: + windows_password: + description: > + Administrator password to set when deploying Windows instances. + If this is not supplied, agent_config.password will be used (if that has been supplied). + Supplying neither of these properties will result in an error. + type: string + required: false + windows_timezone: + description: > + Timezone to set Windows instances to. + See https://msdn.microsoft.com/en-us/library/ms912391%28v=winembedded.11%29.aspx + Defaults to GMT without daylight savings. + type: integer + default: 90 + windows_organization: + description: > + The organization name to set on the Windows system. + type: string + default: Organization + custom_sysprep: + description: > + A custom System Preparation Answers file + to use for full customization of Windows. + This can be generated by the Windows System Image Manager. + Note that this file should be verified to work correctly before being applied, + as any errors will appear only on Windows and will not be visible to the plugin. + Also note that any scripts, etc., + that attempt to work on the VM after the custom sysprep must tolerate multiple retries + because the plugin cannot detect when the custom sysprep has finished, + so provides the server as soon as the IPs are assigned + (which occurs before customization is complete). + type: string + required: false + os_family: + default: windows + description: Overridden default from ``Server`` node_type. + agent_config: + type: cloudify.datatypes.AgentConfig + default: + port: 5985 + + cloudify.vsphere.nodes.Network: + derived_from: cloudify.nodes.vsphere.Network + + cloudify.nodes.vsphere.Network: + derived_from: cloudify.nodes.Network + properties: + use_external_resource: + description: > + Whether to use a vm that already exists on vSphere. + type: boolean + default: false + network: + description: | + key-value network configuration. :: + name: network name + vlan_id: vLAN identifier which will be assigned to the network + vswitch_name: name of the vSwitch the network will be connected to. + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.vsphere_network_plugin.network.create + inputs: {} + delete: + implementation: vsphere.vsphere_network_plugin.network.delete + inputs: {} + + cloudify.vsphere.nodes.Storage: + derived_from: cloudify.nodes.vsphere.Storage + + cloudify.nodes.vsphere.Storage: + derived_from: cloudify.nodes.Volume + properties: + use_external_resource: + description: > + Whether to use a vm that already exists on vSphere. + type: boolean + default: false + storage: + description: | + key-value storage disk configuration. :: + parent_key: Device key from controller, negative mean + that can be used any + storage_size: disk size in GB. + thin_provision: Flag to indicate to the underlying + filesystem, whether the virtual disk backing file should + be allocated lazily (using thin provisioning). This flag + is only used for file systems that support configuring + the provisioning policy on a per file basis, such as VMFS3. + Default: false + mode: The disk persistence mode. Valid modes are: + * persistent + * independent_persistent + * independent_nonpersistent + * nonpersistent + * undoable + * append + default: {} + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.vsphere_storage_plugin.storage.create + inputs: + <<: *interfaces_power_inputs + storage: + default: { get_property: [ SELF, storage ] } + delete: + implementation: vsphere.vsphere_storage_plugin.storage.delete + inputs: *interfaces_power_inputs + check_drift: + implementation: vsphere.vsphere_storage_plugin.storage.check_drift + inputs: {} + update: + implementation: vsphere.vsphere_storage_plugin.storage.update + inputs: {} + cloudify.interfaces.modify: + resize: + implementation: vsphere.vsphere_storage_plugin.storage.resize + inputs: + <<: *interfaces_power_inputs + storage: + default: { get_property: [ SELF, storage ] } + + cloudify.vsphere.nodes.IPPool: + derived_from: cloudify.nodes.vsphere.IPPool + + cloudify.nodes.vsphere.IPPool: + derived_from: cloudify.nodes.Subnet + properties: + datacenter_name: + description: > + datacenter name. + type: string + required: false + ippool: + description: > + Ip Pool settings: + name: ip pool name + subnet: Subnet settings: x.x.x.x + netmask: Netmask: x.x.x.x + gateway: Ip gateway: x.x.x.x + range: Ip range for allocate: x.x.x.x#x + dhcp: Use external dhcp server. Default is False + enabled: Enable ip pool. Default is True + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.vsphere_network_plugin.ippool.create + inputs: {} + poststart: + implementation: vsphere.vsphere_network_plugin.ippool.poststart + inputs: {} + delete: + implementation: vsphere.vsphere_network_plugin.ippool.delete + inputs: {} + check_drift: + implementation: vsphere.vsphere_network_plugin.ippool.check_drift + inputs: {} + + cloudify.vsphere.nodes.CloudInitISO: + derived_from: cloudify.nodes.vsphere.CloudInitISO + + cloudify.nodes.vsphere.CloudInitISO: + derived_from: cloudify.nodes.Volume + properties: + datacenter_name: + description: > + datacenter name. + type: string + required: false + allowed_datastores: + description: > + Which ESX datastore(s) this image is allowed to be stored on. + required: false + allowed_datastore_ids: + description: > + Which ESX datastore id(s) this image is allowed to be stored on. + Field has priority over allowed_datastores. + required: false + volume_prefix: + default: cloudinit + description: > + Datastorage path for save volume + vol_ident: + default: cidata + description: > + The volume identification string to use on the new ISO. + sys_ident: + default: "" + description: > + The system identification string to use on the new ISO. + files: + default: {} + description: > + List of files for save to CloudInit iso image with file content. + Example: + meta-data: "instance-id: localhost" + user-data: "password: passw0rd" + where meta-data and user-data are file names on cloud init image. + raw_files: + default: {} + description: > + Deprecated. List files for save to CloudInit iso image with file + name from blueprint. + files_raw: + default: { get_property: [ SELF, raw_files] } + description: > + List files for save to CloudInit iso image with file name from + blueprint. + Example: + meta-data: meta_data.yaml + where meta-data is file names on cloud init image, + meta_data.yaml is raw file name. + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.vsphere_storage_plugin.cidata.create + inputs: {} + delete: + implementation: vsphere.vsphere_storage_plugin.cidata.delete + inputs: {} + + cloudify.vsphere.nodes.Datacenter: + derived_from: cloudify.nodes.vsphere.Datacenter + + cloudify.nodes.vsphere.Datacenter: + derived_from: cloudify.nodes.Root + properties: + name: + description: > + The name of the datacenter. + use_external_resource: + description: > + Whether to use a datacenter that already exists on vSphere. + Currently, datacenters cannot be created or deleted, + and this node type exists only for compatibility with the NSX plugin. + type: boolean + default: true + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.datacenter.create + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.datacenter.delete + inputs: {} + + cloudify.vsphere.nodes.Datastore: + derived_from: cloudify.nodes.vsphere.Datastore + + cloudify.nodes.vsphere.Datastore: + derived_from: cloudify.nodes.Root + properties: + name: + description: > + The name of the datastore. + use_external_resource: + description: > + Whether to use a datastore that already exists on vSphere. + Currently, datastores cannot be created or deleted, + and this node type exists only for compatibility with the NSX/Content + Library plugin. + type: boolean + default: true + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.datastore.create + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.datastore.delete + inputs: {} + + cloudify.vsphere.nodes.Cluster: + derived_from: cloudify.nodes.vsphere.Cluster + + cloudify.nodes.vsphere.Cluster: + derived_from: cloudify.nodes.Root + properties: + name: + description: > + The name of the cluster. + use_external_resource: + description: > + Whether to use a cluster that already exists on vSphere. + Currently, clusters cannot be created or deleted, + and this node type exists only for compatibility with the NSX plugin. + type: boolean + default: true + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.cluster.create + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.cluster.delete + inputs: {} + + cloudify.vsphere.nodes.ResourcePool: + derived_from: cloudify.nodes.vsphere.ResourcePool + + cloudify.nodes.vsphere.ResourcePool: + derived_from: cloudify.nodes.Root + properties: + name: + description: > + The name of the resource_pool. + use_external_resource: + description: > + Whether to use a resource pool that already exists on vSphere. + type: boolean + default: false + host_name: + description: > + The name of the host to create the resource pool + default: "" + cluster_name: + description: > + The name of the cluster to create the resource pool + default: "" + pool_spec: + description: | + key-value resource pool configuration. :: + cpuAllocation: + expandableReservation: True or False + limit: max limit + reservation: Amount of resource that is guaranteed available + shares: + level: The allocation level. Valid levels are: + * custom + * high + * low + * normal + shares: The number of shares allocated. Used to determine resource allocation in case of resource contention, + used with custom level only + memoryAllocation: + expandableReservation: True or False + limit: max limit + reservation: Amount of resource that is guaranteed available + shares: + level: The allocation level. Valid levels are: + * custom + * high + * low + * normal + shares: The number of shares allocated. Used to determine resource allocation in case of resource contention, + used with custom level only + default: {} + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.resource_pool.create + inputs: {} + poststart: + implementation: vsphere.cloudify_vsphere.resource_pool.poststart + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.resource_pool.delete + inputs: {} + check_drift: + implementation: vsphere.cloudify_vsphere.resource_pool.check_drift + inputs: {} + cloudify.interfaces.operations: + update: + implementation: vsphere.cloudify_vsphere.resource_pool.update_resource_pool + inputs: {} + + cloudify.vsphere.nodes.VMFolder: + derived_from: cloudify.nodes.vsphere.VMFolder + + cloudify.nodes.vsphere.VMFolder: + derived_from: cloudify.nodes.Root + properties: + name: + description: > + The name of the VM folder. + use_external_resource: + description: > + Whether to use a VM folder that already exists on vSphere. + Currently, VM folder cannot be created or deleted, + and this node type exists only for compatibility with the NSX/Content + Library plugin. + type: boolean + default: true + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.vm_folder.create + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.vm_folder.delete + inputs: {} + + cloudify.vsphere.nodes.Host: + derived_from: cloudify.nodes.vsphere.Host + + cloudify.nodes.vsphere.Host: + derived_from: cloudify.nodes.Root + properties: + name: + description: > + The name of the hypervisor host. + use_external_resource: + description: > + Whether to use a hypervisor host that already exists on vSphere. + Currently, hypervisor hosts cannot be created or deleted, + and this node type exists only for compatibility with the NSX/Content + Library plugin. + type: boolean + default: true + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.hypervisor_host.create + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.hypervisor_host.delete + inputs: {} + + cloudify.vsphere.nodes.ContentLibraryDeployment: + derived_from: cloudify.nodes.vsphere.ContentLibraryDeployment + +# For more information look to https://vdc-repo.vmware.com/vmwb-repository/dcr-public/1cd28284-3b72-4885-9e31-d1c6d9e26686/71ef7304-a6c9-43b3-a3cd-868b2c236c81/doc/operations/com/vmware/vcenter/ovf/library_item.deploy-operation.html + cloudify.nodes.vsphere.ContentLibraryDeployment: + derived_from: cloudify.nodes.Root + properties: + library_name: + description: > + The name of the content library name. + default: "" + template_name: + description: > + The name of the content library item name. + default: "" + target: + description: > + Target settings for install: + resource_pool_id: Identifier of the resource pool to which the + virtual machine or virtual appliance should be attached. + host_id: Identifier of the target host on which the virtual + machine or virtual appliance will run. Optional. If unset, + the server will automatically select a target host from the + resource pool. + folder_id: Identifier of the vCenter folder that should contain + the virtual machine or virtual appliance. The folder must be + virtual machine folder. Optional. If unset, the server will + choose the deployment folder. + default: {} + deployment_spec: + description: > + Deployment settings for install: + name: Name assigned to the deployed target virtual machine or + virtual appliance. Optional. If unset, the server will use + the name from the instance.id. + annotation: Annotation assigned to the deployed target virtual + machine or virtual appliance. Optional. If unset, the server + will use the annotation from the OVF package. + network_mappings: Specification of the target network to use for + sections of type ovf:NetworkSection in the OVF descriptor. + storage_mappings: Specification of the target storage to use for + sections of type vmw:StorageGroupSection in the OVF descriptor. + storage_provisioning: Default storage provisioning type to use for + all sections of type vmw:StorageSection in the OVF descriptor. + storage_profile_id: Default storage profile to use for all sections + of type vmw:StorageSection in the OVF descriptor. + locale: The locale to use for parsing the OVF descriptor. + flags: Flags to be use for deployment. + additional_parameters: Additional OVF parameters that may be needed + for the deployment. + default_datastore_id: Default datastore to use for all sections of + type vmw:StorageSection in the OVF descriptor. Optional. + If unset, the server will choose the default datastore. + default: {} + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.contentlibrary.deployment.create + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.contentlibrary.deployment.delete + inputs: {} + + cloudify.nodes.vsphere.OvfDeployment: + derived_from: cloudify.nodes.Root + properties: + target: + description: > + Target settings for install: + resource_pool: name of the resource pool to which the + virtual machine or virtual appliance should be attached. If unset, + the server will automatically select the default resource pool + from connection_config. + host: name of the target host on which the virtual + machine or virtual appliance will run. Optional. If unset, + the server will automatically select a target host from the + resource pool. + folder: name of of the vCenter folder that should contain + the virtual machine or virtual appliance. The folder must be + virtual machine folder. Optional. If unset, the server will + choose the datacenter default folder. + default: {} + ovf_name: + description: > + Ovf Deployment name. + default: "" + ovf_source: + description: > + Ovf Source can be local or from URL + default: "" + datastore_name: + description: > + Datastore name to use when deploying the Ovf. + disk_provisioning: + description: > + Disk provisioning type. + could be one of these values: + - monolithicSparse + - monolithicFlat + - twoGbMaxExtentSparse + - twoGbMaxExtentFlat + - thin + - thick + - sparse + - flat + - seSparse + default: thin + network_mappings: + description: > + Specification of the target network to use for ovf:NetworkSection in the OVF descriptor. + array of networks mapping [(key: value)] where key is ovf network name and value is vCenter network + memory: + description: > + Amount of RAM, in MB. + type: integer + required: false + cpus: + description: > + Number of CPUs. + type: integer + required: false + disk_size: + description: > + Disk size in GBs. + type: integer + required: false + cdrom_image: + description: | + Cdrom image path + type: string + required: false + extra_config: + description: | + Extra config to set, key-value dictionary + type: dict + default: {} + boot_firmware: + description: | + specify which boot firmware to use it can be [bios,efi] + type: string + required: false + boot_order: + description: | + The order of boot + Example: + - cdrom + - hdd + - ethernet + In above list will be provided, vsphere try to boot from cdrom,then + from hdd (all disk_keys) and at the end from ethernet (ethernet_keys) + type: list + required: false + disk_keys: + type: list + required: false + description: | + The order of hdd boot - will be used only if boot_order provided. + If empty, the parameter will be generated based on vm properties (considering all hdd disks) + ethernet_keys: + type: list + required: false + description: | + The order of ethernet boot - will be used only if boot_order provided. + If empty, the parameter will be generated based on vm properties (considering all ethernet devices) + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.ovf.create + inputs: {} + delete: + implementation: vsphere.cloudify_vsphere.ovf.delete + inputs: {} + + cloudify.vsphere.nodes.NIC: + derived_from: cloudify.nodes.vsphere.NIC + + cloudify.nodes.vsphere.NIC: + derived_from: cloudify.nodes.Root + properties: + name: + description: > + Network name for connect + switch_distributed: + description: > + determines if this is connected to a distributed port group + default: false + adapter_type: + description: > + Possible: Vmxnet3, Vmxnet2, Sriov, E1000, E1000e + default: Vmxnet3 + start_connected: + description: > + Specifies whether or not to connect the device when the virtual + machine starts. + default: true + allow_guest_control: + description: > + Enables guest control over whether the connectable device is + connected. + default: true + network_connected: + description: > + Indicates whether the device is currently connected. + Valid only while the virtual machine is running. + default: true + wake_on_lan_enabled: + description: > + Indicates whether wake-on-LAN is enabled on this virtual network + adapter. Clients can set this property to selectively enable or + disable wake-on-LAN. + default: true + address_type: + description: > + MAC address type. Valid values for address type are: + ManualStatically assigned MAC address. + GeneratedAutomatically generated MAC address. + AssignedMAC address assigned by VirtualCenter. + default: assigned + mac_address: + description: > + MAC address assigned to the virtual network adapter. Clients can + set this property to any of the allowed address types. The server + might override the specified value for "Generated" or "Assigned" + if it does not fall in the right ranges or is determined to be + a duplicate. + default: "" + network_configuration: + default: {} + description: > + Only valid with a relationship cloudify.relationships.vsphere.nic_connected_to_network to a network. + Dictionary with following keys: + 'from_relationship': (bool, False), + 'external': (bool, False), + 'management': (bool, False), + 'use_dhcp': (bool, True), + 'network': (basestring, None), + 'gateway': (basestring, None), + 'ip': (basestring, None) + connection_config: + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.devices.create_controller + inputs: + name: + default: { get_attribute: [ SELF, name ] } + switch_distributed: + default: { get_attribute: [ SELF, switch_distributed ] } + adapter_type: + default: { get_attribute: [ SELF, adapter_type ] } + start_connected: + default: { get_attribute: [ SELF, start_connected ] } + allow_guest_control: + default: { get_attribute: [ SELF, allow_guest_control ] } + network_connected: + default: { get_attribute: [ SELF, network_connected ] } + wake_on_lan_enabled: + default: { get_attribute: [ SELF, wake_on_lan_enabled ] } + address_type: + default: { get_attribute: [ SELF, address_type ] } + mac_address: + default: { get_attribute: [ SELF, mac_address ] } + network_configuration: + default: { get_attribute: [ SELF, network_configuration ] } + delete: + implementation: vsphere.cloudify_vsphere.devices.delete_controller + + cloudify.vsphere.nodes.SCSIController: + derived_from: cloudify.nodes.vsphere.SCSIController + + cloudify.nodes.vsphere.SCSIController: + derived_from: cloudify.nodes.Root + properties: + busNumber: + description: > + Bus number associated with this controller. + default: 0 + label: + description: > + Label for SCSI controller + default: SCSIController + adapterType: + description: > + Possible: paravirtual, lsilogic_sas, lsilogic + default: paravirtual + hotAddRemove: + description: > + All SCSI controllers support hot adding and removing of devices. + This support can't be toggled in the current implementation. + Therefore, this option is ignored when reconfiguring a SCSI + controller and is always set to "true" when reading an existing + configuration. + default: true + sharedBus: + description: > + Mode for sharing the SCSI bus. The modes are physicalSharing, + virtualSharing, and noSharing. + default: noSharing + scsiCtlrUnitNumber: + description: > + The unit number of the SCSI controller. The SCSI controller + sits on its own bus, so this field defines which slot the + controller is using. + default: -1 + connection_config: + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.cloudify_vsphere.devices.create_controller + inputs: + busNumber: + default: { get_attribute: [ SELF, busNumber ] } + hotAddRemove: + default: { get_attribute: [ SELF, hotAddRemove ] } + sharedBus: + default: { get_attribute: [ SELF, sharedBus ] } + scsiCtlrUnitNumber: + default: { get_attribute: [ SELF, scsiCtlrUnitNumber ] } + adapterType: + default: { get_attribute: [ SELF, adapterType ] } + label: + default: { get_attribute: [ SELF, label ] } + delete: + implementation: vsphere.cloudify_vsphere.devices.delete_controller + + cloudify.nodes.vsphere.PCIDevice: + derived_from: cloudify.nodes.Root + properties: + device_name: + description: > + PCI Passthrough device name + type: string + turn_off_vm: + description: > + control if we turn off the machine to attach PCI device + as it can't be attached while VM is turned on + default: false + connection_config: + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + precreate: + implementation: vsphere.cloudify_vsphere.devices.copy_device_properties + postdelete: + implementation: vsphere.cloudify_vsphere.devices.clean_device_properties + + cloudify.nodes.vsphere.USBDevice: + derived_from: cloudify.nodes.Root + properties: + controller_type: + description: > + usb controller type associated with USB. + values can be : usb2, usb3 + default: 'usb3' + device_name: + description: > + host usb device name + type: string + connection_config: + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + precreate: + implementation: vsphere.cloudify_vsphere.devices.copy_device_properties + postdelete: + implementation: vsphere.cloudify_vsphere.devices.clean_device_properties + + cloudify.nodes.vsphere.SerialPort: + derived_from: cloudify.nodes.Root + properties: + device_name: + description: > + host serial port name should be absolute path + /dev/char/serial/uartX + type: string + turn_off_vm: + description: > + control if we turn off the machine to attach serial port + as it can't be attached while VM is turned on + default: false + connection_config: + type: cloudify.datatypes.vsphere.Config + interfaces: + cloudify.interfaces.lifecycle: + precreate: + implementation: vsphere.cloudify_vsphere.devices.copy_device_properties + postdelete: + implementation: vsphere.cloudify_vsphere.devices.clean_device_properties + + cloudify.vsphere.nodes.ISO: + derived_from: cloudify.nodes.vsphere.ISO + + cloudify.nodes.vsphere.ISO: + derived_from: cloudify.nodes.Volume + properties: + datacenter_name: + description: > + datacenter name. + type: string + required: false + allowed_datastores: + description: > + Which ESX datastore(s) this image is allowed to be stored on. + required: false + allowed_datastore_ids: + description: > + Which ESX datastore id(s) this image is allowed to be stored on. + Field has priority over allowed_datastores. + required: false + volume_prefix: + default: cloudinit + description: > + Datastorage path for save volume + iso_file_path: + type: string + description: > + The path for the ISO to upload. + required: true + connection_config: + default: {} + type: cloudify.datatypes.vsphere.Config + use_external_resource: + default: false + description: > + Use existing image (must be preuploaded to vsphere) + force_delete: + default: false + description: > + Delete image if external resource + interfaces: + cloudify.interfaces.lifecycle: + create: + implementation: vsphere.vsphere_storage_plugin.cidata.upload_iso + inputs: {} + delete: + implementation: vsphere.vsphere_storage_plugin.cidata.delete_iso + inputs: {} + +relationships: + + cloudify.vsphere.port_connected_to_network: + derived_from: cloudify.relationships.vsphere.port_connected_to_network + + cloudify.relationships.vsphere.port_connected_to_network: + derived_from: cloudify.relationships.connected_to + + cloudify.vsphere.port_connected_to_server: + derived_from: cloudify.relationships.vsphere.port_connected_to_server + + cloudify.relationships.vsphere.port_connected_to_server: + derived_from: cloudify.relationships.connected_to + + cloudify.vsphere.storage_connected_to_server: + derived_from: cloudify.relationships.vsphere.storage_connected_to_server + + cloudify.relationships.vsphere.storage_connected_to_server: + derived_from: cloudify.relationships.connected_to + + cloudify.vsphere.nic_connected_to_server: + derived_from: cloudify.relationships.vsphere.nic_connected_to_server + + cloudify.relationships.vsphere.nic_connected_to_server: + derived_from: cloudify.relationships.contained_in + source_interfaces: + cloudify.interfaces.relationship_lifecycle: + preconfigure: + implementation: vsphere.cloudify_vsphere.devices.attach_ethernet_card + unlink: + implementation: vsphere.cloudify_vsphere.devices.detach_controller + + cloudify.vsphere.controller_connected_to_vm: + derived_from: cloudify.relationships.vsphere.controller_connected_to_vm + + cloudify.relationships.vsphere.controller_connected_to_vm: + derived_from: cloudify.relationships.contained_in + source_interfaces: + cloudify.interfaces.relationship_lifecycle: + preconfigure: + implementation: vsphere.cloudify_vsphere.devices.attach_scsi_controller + unlink: + implementation: vsphere.cloudify_vsphere.devices.detach_controller + + cloudify.relationships.vsphere.nic_connected_to_network: + derived_from: cloudify.relationships.contained_in + + cloudify.relationships.vsphere.server_connected_to_nic: + derived_from: cloudify.relationships.contained_in + source_interfaces: + cloudify.interfaces.relationship_lifecycle: + establish: + implementation: vsphere.cloudify_vsphere.devices.attach_server_to_ethernet_card + unlink: + implementation: vsphere.cloudify_vsphere.devices.detach_server_from_controller + + cloudify.relationships.vsphere.ippool_connected_to_network: + derived_from: cloudify.relationships.depends_on + + cloudify.relationships.vsphere.resource_pool_contained_in: + derived_from: cloudify.relationships.contained_in + + cloudify.relationships.vsphere.usb_connected_to_server: + derived_from: cloudify.relationships.contained_in + source_interfaces: + cloudify.interfaces.relationship_lifecycle: + establish: + implementation: vsphere.cloudify_vsphere.devices.attach_usb_device + unlink: + implementation: vsphere.cloudify_vsphere.devices.detach_usb_device + + cloudify.relationships.vsphere.serial_connected_to_server: + derived_from: cloudify.relationships.contained_in + source_interfaces: + cloudify.interfaces.relationship_lifecycle: + establish: + implementation: vsphere.cloudify_vsphere.devices.attach_serial_port + unlink: + implementation: vsphere.cloudify_vsphere.devices.detach_serial_port + + cloudify.relationships.vsphere.pci_connected_to_server: + derived_from: cloudify.relationships.contained_in + source_interfaces: + cloudify.interfaces.relationship_lifecycle: + establish: + implementation: vsphere.cloudify_vsphere.devices.attach_pci_device + unlink: + implementation: vsphere.cloudify_vsphere.devices.detach_pci_device + +blueprint_labels: + obj-type: + values: + - vsphere + +labels: + obj-type: + values: + - vsphere diff --git a/v2_plugin.yaml b/v2_plugin.yaml index 1676947f..17e244bc 100755 --- a/v2_plugin.yaml +++ b/v2_plugin.yaml @@ -10,7 +10,7 @@ plugins: vsphere: executor: central_deployment_agent package_name: cloudify-vsphere-plugin - package_version: '2.20.7' + package_version: '2.20.8' data_types: diff --git a/vsphere_network_plugin/tests/ippool_test.py b/vsphere_network_plugin/tests/ippool_test.py index f878ff84..6f8a3c03 100644 --- a/vsphere_network_plugin/tests/ippool_test.py +++ b/vsphere_network_plugin/tests/ippool_test.py @@ -14,7 +14,7 @@ import unittest -from mock import Mock, patch +from mock import Mock, patch, MagicMock from cloudify.state import current_ctx from cloudify.mocks import MockCloudifyContext @@ -23,11 +23,22 @@ from vsphere_network_plugin import ippool +class SpecialMockCloudifyContext(MockCloudifyContext): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._plugin = MagicMock(properties={}) + + @property + def plugin(self): + return self._plugin + + class IPPoolTest(unittest.TestCase): def setUp(self): super(IPPoolTest, self).setUp() - self.mock_ctx = MockCloudifyContext( + self.mock_ctx = SpecialMockCloudifyContext( 'node_name', properties={}, runtime_properties={} diff --git a/vsphere_plugin_common/__init__.py b/vsphere_plugin_common/__init__.py index c4165ae9..32767ff9 100755 --- a/vsphere_plugin_common/__init__.py +++ b/vsphere_plugin_common/__init__.py @@ -32,6 +32,7 @@ from .clients.server import ServerClient from .clients.network import NetworkClient from .clients.storage import StorageClient, RawVolumeClient +from .utils import get_plugin_properties def remove_runtime_properties(): @@ -53,8 +54,15 @@ def _with_client(client_name, client): def decorator(f): @wraps(f) def wrapper(connection_config, *args, **kwargs): + # handle the logic of ctx.plugin.properties + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + + if connection_config: + vsphere_config.update(connection_config) + kwargs[client_name] = client( - ctx_logger=ctx.logger).get(config=connection_config) + ctx_logger=ctx.logger).get(config=vsphere_config) if not hasattr(f, '__wrapped__'): # don't pass connection_config to the real operation kwargs.pop('connection_config', None) diff --git a/vsphere_plugin_common/utils.py b/vsphere_plugin_common/utils.py index 313e8aa9..44d6e0fc 100644 --- a/vsphere_plugin_common/utils.py +++ b/vsphere_plugin_common/utils.py @@ -220,3 +220,11 @@ def is_node_deprecated(node_type): if re.match(pattern, node_type): ctx.logger.error('The node {} is deprecated,' 'please update your node'.format(node_type)) + + +def get_plugin_properties(plugin_properties): + final_props = {} + for k, v in list(plugin_properties.items()): + if 'value' in v: + final_props[k] = v.get('value') + return final_props diff --git a/vsphere_server_plugin/power.py b/vsphere_server_plugin/power.py index c030cfcf..928e5186 100644 --- a/vsphere_server_plugin/power.py +++ b/vsphere_server_plugin/power.py @@ -16,7 +16,7 @@ import cloudify.exceptions as cfy_exc from .server import get_server_by_context -from vsphere_plugin_common.utils import op +from vsphere_plugin_common.utils import op, get_plugin_properties from vsphere_plugin_common import with_server_client @@ -41,7 +41,11 @@ def _power_operation(operation_name, @op def power_on(max_wait_time, ctx, server, connection_config): - return _power_operation(connection_config, + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + if connection_config: + vsphere_config.update(connection_config) + return _power_operation(vsphere_config, 'start_server', ctx, server, @@ -50,7 +54,11 @@ def power_on(max_wait_time, ctx, server, connection_config): @op def power_off(max_wait_time, ctx, server, connection_config): - return _power_operation(connection_config, + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + if connection_config: + vsphere_config.update(connection_config) + return _power_operation(vsphere_config, 'stop_server', ctx, server, @@ -59,7 +67,11 @@ def power_off(max_wait_time, ctx, server, connection_config): @op def shut_down(max_wait_time, ctx, server, connection_config): - return _power_operation(connection_config, + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + if connection_config: + vsphere_config.update(connection_config) + return _power_operation(vsphere_config, 'shutdown_server_guest', ctx, server, @@ -68,14 +80,22 @@ def shut_down(max_wait_time, ctx, server, connection_config): @op def reboot(max_wait_time, ctx, server, connection_config): - return _power_operation(connection_config, 'reboot_server', + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + if connection_config: + vsphere_config.update(connection_config) + return _power_operation(vsphere_config, 'reboot_server', ctx, server, kwargs={'max_wait_time': max_wait_time},) @op def reset(max_wait_time, ctx, server, connection_config): - return _power_operation(connection_config, + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + if connection_config: + vsphere_config.update(connection_config) + return _power_operation(vsphere_config, 'reset_server', ctx, server, diff --git a/vsphere_server_plugin/tests/test_backup.py b/vsphere_server_plugin/tests/test_backup.py index 134c6c04..20327e09 100644 --- a/vsphere_server_plugin/tests/test_backup.py +++ b/vsphere_server_plugin/tests/test_backup.py @@ -24,6 +24,17 @@ import vsphere_server_plugin.server as server +class SpecialMockCloudifyContext(MockCloudifyContext): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._plugin = mock.MagicMock(properties={}) + + @property + def plugin(self): + return self._plugin + + class BackupServerTest(unittest.TestCase): def tearDown(self): @@ -31,7 +42,7 @@ def tearDown(self): super(BackupServerTest, self).tearDown() def _gen_ctx(self): - _ctx = MockCloudifyContext( + _ctx = SpecialMockCloudifyContext( 'node_name', properties={ "connection_config": { diff --git a/vsphere_storage_plugin/cidata.py b/vsphere_storage_plugin/cidata.py index d348eebc..8411137e 100644 --- a/vsphere_storage_plugin/cidata.py +++ b/vsphere_storage_plugin/cidata.py @@ -19,7 +19,11 @@ from cloudify import ctx # This package imports -from vsphere_plugin_common.utils import op, is_node_deprecated +from vsphere_plugin_common.utils import ( + op, + is_node_deprecated, + get_plugin_properties +) from vsphere_plugin_common import with_rawvolume_client from vsphere_plugin_common.constants import ( VSPHERE_STORAGE_IMAGE, @@ -64,14 +68,24 @@ def create(rawvolume_client, iso_disk = "{prefix}/{name}.iso".format( prefix=volume_prefix, name=ctx.instance.id) + + # handle the logic of ctx.plugin.properties + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.node.properties['connection_config'] + if connection_config: + vsphere_config.update(connection_config) + host = vsphere_config.get('host') + port = vsphere_config.get('port') + datacenter_id, storage_path = rawvolume_client.upload_file( datacenter_name=datacenter_name, allowed_datastores=allowed_datastores, allowed_datastore_ids=allowed_datastore_ids, remote_file=iso_disk, data=outiso, - host=ctx.node.properties['connection_config']['host'], - port=ctx.node.properties['connection_config']['port']) + host=host, + port=port) ctx.instance.runtime_properties[VSPHERE_STORAGE_IMAGE] = storage_path ctx.instance.runtime_properties[VSPHERE_STORAGE_FILE_NAME] = storage_path ctx.instance.runtime_properties[DATACENTER_ID] = datacenter_id @@ -133,6 +147,14 @@ def upload_iso(rawvolume_client, if ctx.instance.runtime_properties.get(VSPHERE_STORAGE_FILE_NAME): ctx.logger.info('Instance is already created.') return + # handle the logic of ctx.plugin.properties + vsphere_config = get_plugin_properties( + getattr(ctx.plugin, 'properties', {})) + connection_config = ctx.node.properties['connection_config'] + if connection_config: + vsphere_config.update(connection_config) + host = vsphere_config.get('host') + port = vsphere_config.get('port') if not use_external_resource: iso_disk = "{prefix}/{name}.iso".format( prefix=volume_prefix, name=ctx.instance.id) @@ -143,16 +165,16 @@ def upload_iso(rawvolume_client, allowed_datastore_ids=allowed_datastore_ids, remote_file=iso_disk, data=file_data, - host=ctx.node.properties['connection_config']['host'], - port=ctx.node.properties['connection_config']['port']) + host=host, + port=port) else: datacenter_id, storage_path = rawvolume_client.file_exist_in_vsphere( datacenter_name=datacenter_name, allowed_datastores=allowed_datastores, allowed_datastore_ids=allowed_datastore_ids, remote_file=iso_file_path, - host=ctx.node.properties['connection_config']['host'], - port=ctx.node.properties['connection_config']['port']) + host=host, + port=port) ctx.instance.runtime_properties[VSPHERE_STORAGE_IMAGE] = storage_path ctx.instance.runtime_properties[VSPHERE_STORAGE_FILE_NAME] = storage_path ctx.instance.runtime_properties[DATACENTER_ID] = datacenter_id diff --git a/vsphere_storage_plugin/tests/cidata_test.py b/vsphere_storage_plugin/tests/cidata_test.py index f9d95d53..9c3f87b1 100644 --- a/vsphere_storage_plugin/tests/cidata_test.py +++ b/vsphere_storage_plugin/tests/cidata_test.py @@ -14,7 +14,7 @@ import unittest -from mock import Mock, patch +from mock import Mock, patch, MagicMock from cloudify.state import current_ctx from cloudify.mocks import MockCloudifyContext @@ -23,11 +23,22 @@ from vsphere_storage_plugin import cidata +class SpecialMockCloudifyContext(MockCloudifyContext): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._plugin = MagicMock(properties={}) + + @property + def plugin(self): + return self._plugin + + class VsphereCIDataTest(unittest.TestCase): def setUp(self): super(VsphereCIDataTest, self).setUp() - self.mock_ctx = MockCloudifyContext( + self.mock_ctx = SpecialMockCloudifyContext( 'node_name', properties={}, runtime_properties={} diff --git a/vsphere_storage_plugin/tests/storage_test.py b/vsphere_storage_plugin/tests/storage_test.py index 7f848e3a..7829da74 100644 --- a/vsphere_storage_plugin/tests/storage_test.py +++ b/vsphere_storage_plugin/tests/storage_test.py @@ -14,7 +14,7 @@ import unittest -from mock import Mock, patch +from mock import Mock, patch, MagicMock from cloudify.exceptions import NonRecoverableError, OperationRetry from cloudify.state import current_ctx @@ -25,11 +25,22 @@ from vsphere_storage_plugin import storage +class SpecialMockCloudifyContext(MockCloudifyContext): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._plugin = MagicMock(properties={}) + + @property + def plugin(self): + return self._plugin + + class VsphereStorageTest(unittest.TestCase): def setUp(self): super(VsphereStorageTest, self).setUp() - self.mock_ctx = MockCloudifyContext( + self.mock_ctx = SpecialMockCloudifyContext( 'node_name', properties={}, runtime_properties={}