diff --git a/config.yaml b/config.yaml index 597ba52f..d6c415dc 100644 --- a/config.yaml +++ b/config.yaml @@ -406,3 +406,13 @@ options: description: | A comma-separated list of nagios servicegroups. If left empty, the nagios_context will be used as the servicegroup. + use-multipath: + type: boolean + default: False + description: | + Use a multipath connection for iSCSI volumes. Enabling this feature + causes libvirt to discover and login to available iscsi targets before + presenting the disk via device mapper (/dev/mapper/XX) to the VM instead + of a single path (/dev/disk/by-path/XX). If changed after deployment, + each VM will require a full stop/start for changes to take affect. + diff --git a/hooks/nova_compute_context.py b/hooks/nova_compute_context.py index f5684124..51de92ad 100644 --- a/hooks/nova_compute_context.py +++ b/hooks/nova_compute_context.py @@ -197,6 +197,9 @@ class NovaComputeLibvirtContext(context.OSContextGenerator): if config('disk-cachemodes'): ctxt['disk_cachemodes'] = config('disk-cachemodes') + if config('use-multipath'): + ctxt['use_multipath'] = config('use-multipath') + if config('cpu-mode'): ctxt['cpu_mode'] = config('cpu-mode') elif ctxt['arch'] in ('ppc64el', 'ppc64le', 'aarch64'): diff --git a/templates/icehouse/nova.conf b/templates/icehouse/nova.conf index 81a20f54..53581604 100644 --- a/templates/icehouse/nova.conf +++ b/templates/icehouse/nova.conf @@ -175,5 +175,8 @@ rbd_secret_uuid = {{ rbd_secret_uuid }} {% if disk_cachemodes -%} disk_cachemodes = {{ disk_cachemodes }} {% endif -%} +{% if use_multipath -%} +iscsi_use_multipath = {{ use_multipath }} +{% endif %} {% include "parts/section-ephemeral" %} diff --git a/templates/juno/nova.conf b/templates/juno/nova.conf index 52a0adc4..35cc8176 100644 --- a/templates/juno/nova.conf +++ b/templates/juno/nova.conf @@ -170,6 +170,9 @@ live_migration_uri = {{ live_migration_uri }} {% if disk_cachemodes -%} disk_cachemodes = {{ disk_cachemodes }} {% endif %} +{% if use_multipath -%} +iscsi_use_multipath = {{ use_multipath }} +{% endif %} {% include "parts/section-cinder" %} diff --git a/templates/kilo/nova.conf b/templates/kilo/nova.conf index d634675a..c91f3e7a 100644 --- a/templates/kilo/nova.conf +++ b/templates/kilo/nova.conf @@ -188,6 +188,9 @@ live_migration_uri = {{ live_migration_uri }} {% if disk_cachemodes -%} disk_cachemodes = {{ disk_cachemodes }} {% endif %} +{% if use_multipath -%} +iscsi_use_multipath = {{ use_multipath }} +{% endif %} {% include "parts/section-database" %} diff --git a/templates/liberty/nova.conf b/templates/liberty/nova.conf index ad6324b8..0035c7ea 100644 --- a/templates/liberty/nova.conf +++ b/templates/liberty/nova.conf @@ -198,6 +198,9 @@ live_migration_uri = {{ live_migration_uri }} {% if disk_cachemodes -%} disk_cachemodes = {{ disk_cachemodes }} {% endif %} +{% if use_multipath -%} +iscsi_use_multipath = {{ use_multipath }} +{% endif %} {% include "parts/section-database" %} diff --git a/templates/mitaka/nova.conf b/templates/mitaka/nova.conf index f2e78ce4..9db4dd96 100644 --- a/templates/mitaka/nova.conf +++ b/templates/mitaka/nova.conf @@ -208,6 +208,9 @@ disk_cachemodes = {{ disk_cachemodes }} # Disable tunnelled migration so that selective # live block migration can be supported. live_migration_tunnelled = False +{% if use_multipath -%} +iscsi_use_multipath = {{ use_multipath }} +{% endif %} {% if virt_type == 'lxd' -%} [lxd] diff --git a/templates/newton/nova.conf b/templates/newton/nova.conf new file mode 100644 index 00000000..d419125e --- /dev/null +++ b/templates/newton/nova.conf @@ -0,0 +1,241 @@ +# mitaka +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +{% if restart_trigger -%} +# restart trigger: {{ restart_trigger }} +{% endif -%} +############################################################################### +[DEFAULT] +verbose={{ verbose }} +debug={{ debug }} +dhcpbridge_flagfile=/etc/nova/nova.conf +dhcpbridge=/usr/bin/nova-dhcpbridge +logdir=/var/log/nova +state_path=/var/lib/nova +force_dhcp_release=True +use_syslog = {{ use_syslog }} +ec2_private_dns_show_ip=True +api_paste_config=/etc/nova/api-paste.ini +enabled_apis=osapi_compute,metadata +auth_strategy=keystone +my_ip = {{ host_ip }} + +{% if arch == 'aarch64' -%} +libvirt_use_virtio_for_bridges=False +libvirt_disk_prefix=vd +{% endif -%} + +{% if console_vnc_type -%} +vnc_enabled = True +novnc_enabled = True +vnc_keymap = {{ console_keymap }} +vncserver_listen = 0.0.0.0 +vncserver_proxyclient_address = {{ console_listen_addr }} +{% if console_access_protocol == 'novnc' or console_access_protocol == 'vnc' -%} +novncproxy_base_url = {{ novnc_proxy_address }} +{% endif -%} +{% if console_access_protocol == 'xvpvnc' or console_access_protocol == 'vnc' -%} +xvpvncproxy_port = {{ xvpvnc_proxy_port }} +xvpvncproxy_host = {{ xvpvnc_proxy_host }} +xvpvncproxy_base_url = {{ xvpvnc_proxy_address }} +{% endif -%} +{% else -%} +vnc_enabled = False +novnc_enabled = False +{% endif -%} + +{% if neutron_plugin and neutron_plugin in ('ovs', 'midonet') -%} +libvirt_vif_driver = nova.virt.libvirt.vif.LibvirtGenericVIFDriver +{% if neutron_security_groups -%} +security_group_api = neutron +firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} +{% endif -%} + +{% if neutron_plugin and neutron_plugin == 'vsp' -%} +network_api_class=nova.network.neutronv2.api.API +libvirt_vif_driver=nova.virt.libvirt.vif.LibvirtGenericVIFDriver +neutron_ovs_bridge=alubr0 +security_group_api=neutron +firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} + +{% if neutron_plugin and (neutron_plugin == 'nvp' or neutron_plugin == 'nsx') -%} +libvirt_vif_driver = nova.virt.libvirt.vif.LibvirtOpenVswitchVirtualPortDriver +security_group_api = neutron +firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} + +{% if neutron_plugin and neutron_plugin == 'Calico' -%} +security_group_api = neutron +firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} + +{% if neutron_plugin and neutron_plugin == 'plumgrid' -%} +security_group_api=neutron +firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} + +{% if network_manager != 'neutron' and network_manager_config -%} +{% for key, value in network_manager_config.items() -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif -%} + +{% if network_manager == 'neutron' -%} +network_api_class = nova.network.neutronv2.api.API +use_neutron = True +{% else -%} +network_manager = nova.network.manager.FlatDHCPManager +{% endif -%} + +{% if network_device_mtu -%} +network_device_mtu = {{ network_device_mtu }} +{% endif -%} + +{% if volume_service -%} +volume_api_class = nova.volume.cinder.API +{% endif -%} + +{% if user_config_flags -%} +{% for key, value in user_config_flags.items() -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif -%} + +{% if instances_path -%} +instances_path = {{ instances_path }} +{% endif -%} + +{% if enable_designate -%} +notify_on_state_change = {{ notify_on_state_change }} +{% endif -%} + +{% if sections and 'DEFAULT' in sections -%} +{% for key, value in sections['DEFAULT'] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif -%} + +{% if vcpu_pin_set -%} +vcpu_pin_set = {{ vcpu_pin_set }} +{% endif -%} +reserved_host_memory_mb = {{ reserved_host_memory }} + +{% if pci_passthrough_whitelist -%} +pci_passthrough_whitelist = {{ pci_passthrough_whitelist }} +{% endif -%} + +{% include "section-zeromq" %} + +{% if default_availability_zone -%} +default_availability_zone = {{ default_availability_zone }} +{% endif -%} + + +{% if resume_guests_state_on_host_boot -%} +resume_guests_state_on_host_boot = {{ resume_guests_state_on_host_boot }} +{% endif -%} + +{% if network_manager == 'neutron' and network_manager_config -%} +[neutron] +url = {{ network_manager_config.neutron_url }} +{% if network_manager_config.keystone_host -%} +{% if neutron_plugin and neutron_plugin == 'vsp' -%} +ovs_bridge = alubr0 +{% endif -%} +{% if auth_host -%} +auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} +auth_type = password +project_domain_name = default +user_domain_name = default +project_name = {{ admin_tenant_name }} +username = {{ admin_user }} +password = {{ admin_password }} +signing_dir = {{ signing_dir }} +{% endif -%} +{% if metadata_shared_secret -%} +metadata_proxy_shared_secret = {{ metadata_shared_secret }} +service_metadata_proxy=True +{% endif -%} +{% endif -%} +metadata_workers = {{ workers }} +{% endif -%} + +{% include "section-keystone-authtoken-mitaka" %} + +{% if glance_api_servers -%} +[glance] +api_servers = {{ glance_api_servers }} +{% endif -%} + +{% if console_access_protocol == 'spice' -%} +[spice] +agent_enabled = True +enabled = True +html5proxy_base_url = {{ spice_proxy_address }} +keymap = {{ console_keymap }} +server_listen = 0.0.0.0 +server_proxyclient_address = {{ console_listen_addr }} +{% endif -%} + +[libvirt] +{% if cpu_mode -%} +cpu_mode = {{ cpu_mode }} +{% endif -%} +{% if cpu_model -%} +cpu_model = {{ cpu_model }} +{% endif -%} +{% if libvirt_images_type -%} +images_type = {{ libvirt_images_type }} +{% endif -%} +{% if libvirt_images_type and rbd_pool -%} +images_rbd_pool = {{ rbd_pool }} +images_rbd_ceph_conf = {{ libvirt_rbd_images_ceph_conf }} +inject_password = false +inject_key = false +inject_partition = -2 +{% endif -%} +rbd_user = {{ rbd_user }} +rbd_secret_uuid = {{ rbd_secret_uuid }} +{% if live_migration_uri -%} +live_migration_uri = {{ live_migration_uri }} +{% endif -%} +{% if disk_cachemodes -%} +disk_cachemodes = {{ disk_cachemodes }} +{% endif %} +# Disable tunnelled migration so that selective +# live block migration can be supported. +live_migration_tunnelled = False +{% if use_multipath -%} +volume_use_multipath = {{ use_multipath }} +{% endif %} + +{% if virt_type == 'lxd' -%} +[lxd] +{% if enable_live_migration -%} +allow_live_migration = True +{% endif -%} +{% if storage_pool -%} +pool = {{ storage_pool }} +{% endif -%} +{% endif -%} + +{% include "parts/section-database" %} + +{% include "section-rabbitmq-oslo" %} + +{% include "section-oslo-notifications" %} + +{% include "parts/section-cinder" %} + +[oslo_concurrency] +lock_path=/var/lock/nova + +[workarounds] +disable_libvirt_livesnapshot = False + +{% include "parts/section-ephemeral" %} + +{% include "parts/section-serial-console" %} diff --git a/templates/ocata/nova.conf b/templates/ocata/nova.conf index 044fd82f..476012a1 100644 --- a/templates/ocata/nova.conf +++ b/templates/ocata/nova.conf @@ -209,6 +209,9 @@ disk_cachemodes = {{ disk_cachemodes }} # Disable tunnelled migration so that selective # live block migration can be supported. live_migration_tunnelled = False +{% if use_multipath -%} +volume_use_multipath = {{ use_multipath }} +{% endif %} {% if virt_type == 'lxd' -%} [lxd] diff --git a/unit_tests/test_nova_compute_contexts.py b/unit_tests/test_nova_compute_contexts.py index 8be6828c..a634cfae 100644 --- a/unit_tests/test_nova_compute_contexts.py +++ b/unit_tests/test_nova_compute_contexts.py @@ -576,3 +576,22 @@ class SerialConsoleContextTests(CharmTestCase): {'enable_serial_console': 'true', 'serial_console_base_url': 'ws://10.10.10.1:6083/'} ) + + def test_libvirt_use_multipath(self): + self.kv.return_value = FakeUnitdata(**{'host_uuid': self.host_uuid}) + self.lsb_release.return_value = {'DISTRIB_CODENAME': 'yakkety'} + self.os_release.return_value = 'ocata' + self.test_config.set('use-multipath', True) + libvirt = context.NovaComputeLibvirtContext() + + self.assertEqual( + {'libvirtd_opts': '', + 'libvirt_user': 'libvirt', + 'use_multipath': True, + 'arch': platform.machine(), + 'ksm': 'AUTO', + 'kvm_hugepages': 0, + 'listen_tls': 0, + 'host_uuid': self.host_uuid, + 'force_raw_images': True, + 'reserved_host_memory': 512}, libvirt())