diff --git a/.circleci/config.yml b/.circleci/config.yml
index 52a21a8b..2c1dcec5 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -18,7 +18,7 @@ jobs:
- checkout
- run:
name: install python/pip/libvirt packages
- command: sudo apt-get install -yq python-libvirt libvirt-dev python-dev python-pip
+ command: sudo apt-get install -yq python-dev python-pip
- run:
name: Download pip
command: curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index d451fffa..e202f496 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,7 @@
+2.15.0:
+ - Support Solaris guests (OmniOS CE)
+ - Wait IP flag for wait any ip's on connected NIC
+ - Set extra properties set to VM
2.14.0:
- Export host and datastore for external VM
2.13.1:
diff --git a/plugin.yaml b/plugin.yaml
index 5732b195..facfab1a 100755
--- a/plugin.yaml
+++ b/plugin.yaml
@@ -7,8 +7,8 @@ plugins:
vsphere:
executor: central_deployment_agent
package_name: cloudify-vsphere-plugin
- package_version: '2.14.0'
- source: https://github.com/cloudify-cosmo/cloudify-vsphere-plugin/archive/2.14.0.zip
+ package_version: '2.15.0'
+ source: https://github.com/cloudify-cosmo/cloudify-vsphere-plugin/archive/2.15.0.zip
data_types:
@@ -168,6 +168,14 @@ node_types:
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
diff --git a/setup.py b/setup.py
index aadadc17..60cc8ffc 100755
--- a/setup.py
+++ b/setup.py
@@ -17,7 +17,7 @@
setuptools.setup(
zip_safe=True,
name='cloudify-vsphere-plugin',
- version='2.14.0',
+ version='2.15.0',
packages=[
'vsphere_plugin_common',
'vsphere_server_plugin',
diff --git a/system_tests/resources/linux/content_library.yaml b/system_tests/resources/linux/content_library.yaml
index 9e33963f..d888f540 100644
--- a/system_tests/resources/linux/content_library.yaml
+++ b/system_tests/resources/linux/content_library.yaml
@@ -46,9 +46,6 @@ inputs:
type: string
default: true
- test_network_distributed:
- default: false
-
template_library:
type: string
description: >
diff --git a/system_tests/resources/linux/templates/CentOS-7-x86_64-GenericCloud-ContainerHost.ovf b/system_tests/resources/linux/templates/CentOS-7-x86_64-GenericCloud-ContainerHost.ovf
index add5b6cc..0454ecd0 100644
--- a/system_tests/resources/linux/templates/CentOS-7-x86_64-GenericCloud-ContainerHost.ovf
+++ b/system_tests/resources/linux/templates/CentOS-7-x86_64-GenericCloud-ContainerHost.ovf
@@ -5,6 +5,18 @@
List of the virtual disks
+
diff --git a/system_tests/resources/solaris/content_library.yaml b/system_tests/resources/solaris/content_library.yaml
new file mode 100644
index 00000000..86fa70fb
--- /dev/null
+++ b/system_tests/resources/solaris/content_library.yaml
@@ -0,0 +1,221 @@
+tosca_definitions_version: cloudify_dsl_1_3
+
+imports:
+ - http://www.getcloudify.org/spec/cloudify/4.5.5/types.yaml
+ - https://raw.githubusercontent.com/cloudify-incubator/cloudify-utilities-plugin/1.13.0/plugin.yaml
+ - ../../../plugin.yaml
+
+inputs:
+
+ vcenter_user:
+ type: string
+
+ vcenter_password:
+ type: string
+
+ vcenter_ip:
+ type: string
+
+ vcenter_port:
+ type: string
+ default: 443
+
+ vcenter_datacenter:
+ type: string
+ description: >
+ vcenter datacenter
+ default: Datacenter
+
+ vcenter_resource_pool:
+ description: >
+ Resource pool name
+ default: Resources
+
+ vcenter_datastore:
+ type: string
+ description: >
+ vcenter datastore
+ default: datastore1
+
+ vcenter_hypervisor_host:
+ type: string
+ description: >
+ vcenter host
+
+ vsphere_auto_placement:
+ type: string
+ default: true
+
+ template_library:
+ type: string
+ description: >
+ "Custom Solaris template library"
+ default: "Solaris"
+
+ template_name:
+ type: string
+ description: >
+ "Custom Solaris template template name"
+ default: "Solaris"
+
+ vm_ip:
+ type: string
+ description: >
+ VM ip
+ default: 172.16.168.156
+
+ vm_netmask:
+ type: string
+ description: >
+ VM netmask
+ default: 255.255.255.0
+
+ vm_gateway:
+ type: string
+ description: >
+ VM gateway
+ default: 172.16.168.1
+
+ vm_user:
+ type: string
+ description: >
+ VM user
+ default: root
+
+ vm_password:
+ type: string
+ description: >
+ VM password
+ default: root
+
+###############################################################################
+# DSL section
+###############################################################################
+dsl_definitions:
+
+ connection_config: &connection_config
+ username: { get_input: vcenter_user }
+ password: {get_input: vcenter_password }
+ host: { get_input: vcenter_ip }
+ port: { get_input: vcenter_port }
+ datacenter_name: {get_input: vcenter_datacenter }
+ resource_pool_name: { get_input: vcenter_resource_pool }
+ auto_placement: { get_input: vsphere_auto_placement }
+ allow_insecure: true
+
+node_templates:
+
+ vm_folder:
+ type: cloudify.vsphere.nodes.VMFolder
+ properties:
+ use_external_resource: true
+ name: vm
+ connection_config: *connection_config
+
+ resource_pool:
+ type: cloudify.vsphere.nodes.ResourcePool
+ properties:
+ use_external_resource: true
+ name: { get_input: vcenter_resource_pool }
+ connection_config: *connection_config
+
+ datastore:
+ type: cloudify.vsphere.nodes.Datastore
+ properties:
+ use_external_resource: true
+ name: { get_input: vcenter_datastore }
+ connection_config: *connection_config
+
+ hypervisor_host:
+ type: cloudify.vsphere.nodes.Host
+ properties:
+ use_external_resource: true
+ name: { get_input: vcenter_hypervisor_host }
+ connection_config: *connection_config
+
+ network:
+ type: cloudify.vsphere.nodes.Network
+ properties:
+ use_external_resource: true
+ network:
+ name: Internal
+ switch_distributed: false
+ connection_config: *connection_config
+
+ vm_content_library:
+ type: cloudify.vsphere.nodes.ContentLibraryDeployment
+ properties:
+ library_name: { get_input: template_library }
+ template_name: { get_input: template_name }
+ connection_config: *connection_config
+ interfaces:
+ cloudify.interfaces.lifecycle:
+ create:
+ inputs:
+ target:
+ folder_id: { get_attribute: [vm_folder, vsphere_vm_folder_id] }
+ host_id: { get_attribute: [hypervisor_host, vsphere_hypervisor_host_id] }
+ resource_pool_id: { get_attribute: [resource_pool, vsphere_resource_pool_id] }
+ deployment_spec:
+ default_datastore_id: { get_attribute: [datastore, vsphere_datastore_id] }
+ network_mappings:
+ - key: 'VM Network'
+ value: { get_attribute: [network, vsphere_network_id, 0] }
+ relationships:
+ - target: datastore
+ type: cloudify.relationships.depends_on
+ - target: vm_folder
+ type: cloudify.relationships.depends_on
+ - target: resource_pool
+ type: cloudify.relationships.depends_on
+ - target: hypervisor_host
+ type: cloudify.relationships.depends_on
+ - target: network
+ type: cloudify.relationships.depends_on
+
+ vm_instance:
+ type: cloudify.vsphere.nodes.Server
+ properties:
+ use_external_resource: true
+ connection_config: *connection_config
+ os_family: solaris
+ wait_ip: true
+ agent_config:
+ install_method: none
+ interfaces:
+ cloudify.interfaces.lifecycle:
+ start:
+ inputs:
+ server:
+ name: { get_attribute: [vm_content_library, vm_name] }
+ extra_config:
+ machine.id: { concat: [ 'ip=', { get_input: vm_ip }, '&netmask=', { get_input: vm_netmask }, '&gateway=', { get_input: vm_gateway } ] }
+ stop:
+ inputs:
+ force_stop: true
+ delete:
+ inputs:
+ force_delete: true
+ relationships:
+ - target: vm_content_library
+ type: cloudify.relationships.depends_on
+
+ state_check:
+ type: cloudify.terminal.raw
+ interfaces:
+ cloudify.interfaces.lifecycle:
+ create:
+ inputs:
+ terminal_auth:
+ user: { get_input: vm_user }
+ password: { get_input: vm_password }
+ ip: { get_attribute: [vm_instance, ip] }
+ exit_command: quit
+ promt_check:
+ - '>'
+ calls:
+ - action: show ip
+ save_to: ips
+ relationships:
+ - target: vm_instance
+ type: cloudify.relationships.depends_on
diff --git a/vsphere_plugin_common/__init__.py b/vsphere_plugin_common/__init__.py
index f5125ab3..d51e6d51 100755
--- a/vsphere_plugin_common/__init__.py
+++ b/vsphere_plugin_common/__init__.py
@@ -1474,13 +1474,22 @@ def _update_vm(self, server, cdrom_image=None, remove_networks=False):
devices.append(cdrom_device)
return devices
- def update_server(self, server, cdrom_image=None):
+ def update_server(self, server, cdrom_image=None, extra_config=None):
# Attrach cdrom image to vm without change networks list
devices_changes = self._update_vm(server, cdrom_image=cdrom_image,
remove_networks=False)
- if devices_changes:
+ if devices_changes or extra_config:
spec = vim.vm.ConfigSpec()
- spec.deviceChange = devices_changes
+ # changed devices
+ if devices_changes:
+ spec.deviceChange = devices_changes
+ # add extra config
+ if extra_config and isinstance(extra_config, dict):
+ logger().debug('Extra config: {config}'
+ .format(config=repr(extra_config)))
+ for k in extra_config:
+ spec.extraConfig.append(
+ vim.option.OptionValue(key=k, value=extra_config[k]))
task = server.obj.ReconfigVM_Task(spec=spec)
self._wait_for_task(task)
@@ -1508,6 +1517,7 @@ def create_server(
allowed_datastores=None,
cdrom_image=None,
vm_folder=None,
+ extra_config=None,
):
logger().debug(
"Entering create_server with parameters %s"
@@ -1614,6 +1624,14 @@ def create_server(
clonespec.powerOn = True
clonespec.template = False
+ # add extra config
+ if extra_config and isinstance(extra_config, dict):
+ logger().debug('Extra config: {config}'
+ .format(config=repr(extra_config)))
+ for k in extra_config:
+ clonespec.extraConfig.append(
+ vim.option.OptionValue(key=k, value=extra_config[k]))
+
if adaptermaps:
logger().debug(
'Preparing OS customization spec for {server}'.format(
@@ -1682,11 +1700,17 @@ def create_server(
options.changeSID = True
options.deleteAccounts = False
customspec.options = options
+ elif os_type == 'solaris':
+ ident = None
+ logger().info(
+ 'Customization of the Solaris OS is unsupported by '
+ ' vSphere. Guest additions are required/supported.')
else:
ident = None
logger().info(
- 'os_type {os_type} was specified, but only "windows" and '
- '"linux" are supported. Customization is unsupported.'
+ 'os_type {os_type} was specified, but only "windows", '
+ '"solaris" and "linux" are supported. Customization is '
+ 'unsupported.'
.format(os_type=os_type)
)
diff --git a/vsphere_server_plugin/server.py b/vsphere_server_plugin/server.py
index 0d2eee64..85a2a3a9 100644
--- a/vsphere_server_plugin/server.py
+++ b/vsphere_server_plugin/server.py
@@ -190,6 +190,7 @@ def create_new_server(
os_family='linux',
cdrom_image=None,
vm_folder=None,
+ extra_config=None,
):
vm_name = get_vm_name(ctx, server, os_family)
ctx.logger.info(
@@ -298,7 +299,8 @@ def create_new_server(
allowed_clusters,
allowed_datastores,
cdrom_image=cdrom_image,
- vm_folder=vm_folder)
+ vm_folder=vm_folder,
+ extra_config=extra_config)
ctx.logger.info('Successfully created server called {name}'.format(
name=vm_name))
ctx.instance.runtime_properties[VSPHERE_SERVER_ID] = server_obj._moId
@@ -327,6 +329,7 @@ def start(
enable_start_vm=True,
cdrom_image=None,
vm_folder=None,
+ extra_config=None,
):
ctx.logger.debug("Checking whether server exists...")
@@ -368,9 +371,12 @@ def start(
os_family=os_family,
cdrom_image=cdrom_image,
vm_folder=vm_folder,
+ extra_config=extra_config
)
else:
- server_client.update_server(server=server_obj, cdrom_image=cdrom_image)
+ server_client.update_server(server=server_obj,
+ cdrom_image=cdrom_image,
+ extra_config=extra_config)
if enable_start_vm:
ctx.logger.info("Server already exists, powering on.")
server_client.start_server(server=server_obj)
@@ -577,7 +583,7 @@ def delete(ctx, server_client, server, os_family, force_delete):
@op
@with_server_client
-def get_state(ctx, server_client, server, networking, os_family):
+def get_state(ctx, server_client, server, networking, os_family, wait_ip):
server_obj = get_server_by_context(ctx, server_client, server, os_family)
if server_obj is None:
raise NonRecoverableError(
@@ -599,6 +605,7 @@ def get_state(ctx, server_client, server, networking, os_family):
ctx.logger.info("Server is running, getting network details.")
ctx.logger.info("Guest info: {info}"
.format(info=repr(server_obj.guest)))
+
networks = networking.get('connect_networks', []) if networking else []
manager_network_ip = None
management_networks = \
@@ -611,10 +618,16 @@ def get_state(ctx, server_client, server, networking, os_family):
ctx.logger.info("Server management networks: {networks}"
.format(networks=repr(management_networks)))
+ # used for guest ip checks
+ default_ip = None
# We must obtain IPs at this stage, as they are not populated until
# after the VM is fully booted
for network in server_obj.guest.net:
network_name = network.network
+ # save ip as default
+ if not default_ip:
+ default_ip = get_ip_from_vsphere_nic_ips(network)
+ # check management
if management_network_name and \
(network_name == management_network_name):
manager_network_ip = get_ip_from_vsphere_nic_ips(network)
@@ -643,14 +656,13 @@ def get_state(ctx, server_client, server, networking, os_family):
)
ctx.instance.runtime_properties[NETWORKS] = nets
- try:
- ctx.instance.runtime_properties[IP] = (
- manager_network_ip or
- get_ip_from_vsphere_nic_ips(server_obj.guest.net[0])
- )
- except IndexError:
+ ctx.instance.runtime_properties[IP] = manager_network_ip or default_ip
+ if not ctx.instance.runtime_properties[IP]:
ctx.logger.warn("Server has no IP addresses.")
- ctx.instance.runtime_properties[IP] = None
+ # wait for any ip before next steps
+ if wait_ip:
+ ctx.logger.info("Waiting ip export from guest.")
+ return False
if len(server_obj.guest.net):
public_ips = [