%PDF- %PDF-
Direktori : /lib/python2.7/site-packages/salt/states/ |
Current File : //lib/python2.7/site-packages/salt/states/virt.py |
# -*- coding: utf-8 -*- ''' Manage virt =========== For the key certificate this state uses the external pillar in the master to call for the generation and signing of certificates for systems running libvirt: .. code-block:: yaml libvirt_keys: virt.keys ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import fnmatch import os try: import libvirt # pylint: disable=import-error HAS_LIBVIRT = True except ImportError: HAS_LIBVIRT = False # Import Salt libs import salt.utils.args import salt.utils.files import salt.utils.stringutils import salt.utils.versions from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six __virtualname__ = 'virt' def __virtual__(): ''' Only if virt module is available. :return: ''' if 'virt.node_info' in __salt__: return __virtualname__ return False def keys(name, basepath='/etc/pki', **kwargs): ''' Manage libvirt keys. name The name variable used to track the execution basepath Defaults to ``/etc/pki``, this is the root location used for libvirt keys on the hypervisor The following parameters are optional: country The country that the certificate should use. Defaults to US. .. versionadded:: 2018.3.0 state The state that the certificate should use. Defaults to Utah. .. versionadded:: 2018.3.0 locality The locality that the certificate should use. Defaults to Salt Lake City. .. versionadded:: 2018.3.0 organization The organization that the certificate should use. Defaults to Salted. .. versionadded:: 2018.3.0 expiration_days The number of days that the certificate should be valid for. Defaults to 365 days (1 year) .. versionadded:: 2018.3.0 ''' ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} # Grab all kwargs to make them available as pillar values # rename them to something hopefully unique to avoid # overriding anything existing pillar_kwargs = {} for key, value in six.iteritems(kwargs): pillar_kwargs['ext_pillar_virt.{0}'.format(key)] = value pillar = __salt__['pillar.ext']({'libvirt': '_'}, pillar_kwargs) paths = { 'serverkey': os.path.join(basepath, 'libvirt', 'private', 'serverkey.pem'), 'servercert': os.path.join(basepath, 'libvirt', 'servercert.pem'), 'clientkey': os.path.join(basepath, 'libvirt', 'private', 'clientkey.pem'), 'clientcert': os.path.join(basepath, 'libvirt', 'clientcert.pem'), 'cacert': os.path.join(basepath, 'CA', 'cacert.pem') } for key in paths: p_key = 'libvirt.{0}.pem'.format(key) if p_key not in pillar: continue if not os.path.exists(os.path.dirname(paths[key])): os.makedirs(os.path.dirname(paths[key])) if os.path.isfile(paths[key]): with salt.utils.files.fopen(paths[key], 'r') as fp_: if salt.utils.stringutils.to_unicode(fp_.read()) != pillar[p_key]: ret['changes'][key] = 'update' else: ret['changes'][key] = 'new' if not ret['changes']: ret['comment'] = 'All keys are correct' elif __opts__['test']: ret['result'] = None ret['comment'] = 'Libvirt keys are set to be updated' ret['changes'] = {} else: for key in ret['changes']: with salt.utils.files.fopen(paths[key], 'w+') as fp_: fp_.write( salt.utils.stringutils.to_str( pillar['libvirt.{0}.pem'.format(key)] ) ) ret['comment'] = 'Updated libvirt certs and keys' return ret def _virt_call(domain, function, section, comment, connection=None, username=None, password=None, **kwargs): ''' Helper to call the virt functions. Wildcards supported. :param domain: :param function: :param section: :param comment: :return: ''' ret = {'name': domain, 'changes': {}, 'result': True, 'comment': ''} targeted_domains = fnmatch.filter(__salt__['virt.list_domains'](), domain) changed_domains = list() ignored_domains = list() for targeted_domain in targeted_domains: try: response = __salt__['virt.{0}'.format(function)](targeted_domain, connection=connection, username=username, password=password, **kwargs) if isinstance(response, dict): response = response['name'] changed_domains.append({'domain': targeted_domain, function: response}) except libvirt.libvirtError as err: ignored_domains.append({'domain': targeted_domain, 'issue': six.text_type(err)}) if not changed_domains: ret['result'] = False ret['comment'] = 'No changes had happened' if ignored_domains: ret['changes'] = {'ignored': ignored_domains} else: ret['changes'] = {section: changed_domains} ret['comment'] = comment return ret def stopped(name, connection=None, username=None, password=None): ''' Stops a VM by shutting it down nicely. .. versionadded:: 2016.3.0 :param connection: libvirt connection URI, overriding defaults .. versionadded:: 2019.2.0 :param username: username to connect with, overriding defaults .. versionadded:: 2019.2.0 :param password: password to connect with, overriding defaults .. versionadded:: 2019.2.0 .. code-block:: yaml domain_name: virt.stopped ''' return _virt_call(name, 'shutdown', 'stopped', "Machine has been shut down", connection=connection, username=username, password=password) def powered_off(name, connection=None, username=None, password=None): ''' Stops a VM by power off. .. versionadded:: 2016.3.0 :param connection: libvirt connection URI, overriding defaults .. versionadded:: 2019.2.0 :param username: username to connect with, overriding defaults .. versionadded:: 2019.2.0 :param password: password to connect with, overriding defaults .. versionadded:: 2019.2.0 .. code-block:: yaml domain_name: virt.stopped ''' return _virt_call(name, 'stop', 'unpowered', 'Machine has been powered off', connection=connection, username=username, password=password) def running(name, cpu=None, mem=None, image=None, vm_type=None, disk_profile=None, disks=None, nic_profile=None, interfaces=None, graphics=None, seed=True, install=True, pub_key=None, priv_key=None, update=False, connection=None, username=None, password=None, os_type=None, arch=None): ''' Starts an existing guest, or defines and starts a new VM with specified arguments. .. versionadded:: 2016.3.0 :param name: name of the virtual machine to run :param cpu: number of CPUs for the virtual machine to create :param mem: amount of memory in MiB for the new virtual machine :param image: disk image to use for the first disk of the new VM .. deprecated:: 2019.2.0 :param vm_type: force virtual machine type for the new VM. The default value is taken from the host capabilities. This could be useful for example to use ``'qemu'`` type instead of the ``'kvm'`` one. .. versionadded:: 2019.2.0 :param disk_profile: Name of the disk profile to use for the new virtual machine .. versionadded:: 2019.2.0 :param disks: List of disk to create for the new virtual machine. See :ref:`init-disk-def` for more details on the items on this list. .. versionadded:: 2019.2.0 :param nic_profile: Name of the network interfaces profile to use for the new virtual machine .. versionadded:: 2019.2.0 :param interfaces: List of network interfaces to create for the new virtual machine. See :ref:`init-nic-def` for more details on the items on this list. .. versionadded:: 2019.2.0 :param graphics: Graphics device to create for the new virtual machine. See :ref:`init-graphics-def` for more details on this dictionary .. versionadded:: 2019.2.0 :param saltenv: Fileserver environment (Default: ``'base'``). See :mod:`cp module for more details <salt.modules.cp>` .. versionadded:: 2019.2.0 :param seed: ``True`` to seed the disk image. Only used when the ``image`` parameter is provided. (Default: ``True``) .. versionadded:: 2019.2.0 :param install: install salt minion if absent (Default: ``True``) .. versionadded:: 2019.2.0 :param pub_key: public key to seed with (Default: ``None``) .. versionadded:: 2019.2.0 :param priv_key: public key to seed with (Default: ``None``) .. versionadded:: 2019.2.0 :param seed_cmd: Salt command to execute to seed the image. (Default: ``'seed.apply'``) .. versionadded:: 2019.2.0 :param update: set to ``True`` to update a defined module. (Default: ``False``) .. versionadded:: 2019.2.0 :param connection: libvirt connection URI, overriding defaults .. versionadded:: 2019.2.0 :param username: username to connect with, overriding defaults .. versionadded:: 2019.2.0 :param password: password to connect with, overriding defaults .. versionadded:: 2019.2.0 :param os_type: type of virtualization as found in the ``//os/type`` element of the libvirt definition. The default value is taken from the host capabilities, with a preference for ``hvm``. Only used when creating a new virtual machine. .. versionadded:: Neon :param arch: architecture of the virtual machine. The default value is taken from the host capabilities, but ``x86_64`` is prefed over ``i686``. Only used when creating a new virtual machine. .. versionadded:: Neon .. rubric:: Example States Make sure an already-defined virtual machine called ``domain_name`` is running: .. code-block:: yaml domain_name: virt.running Do the same, but define the virtual machine if needed: .. code-block:: yaml domain_name: virt.running: - cpu: 2 - mem: 2048 - disk_profile: prod - disks: - name: system size: 8192 overlay_image: True pool: default image: /path/to/image.qcow2 - name: data size: 16834 - nic_profile: prod - interfaces: - name: eth0 mac: 01:23:45:67:89:AB - name: eth1 type: network source: admin - graphics: - type: spice listen: - type: address address: 192.168.0.125 ''' ret = {'name': name, 'changes': {}, 'result': True, 'comment': '{0} is running'.format(name) } try: try: __salt__['virt.vm_state'](name) if __salt__['virt.vm_state'](name) != 'running': action_msg = 'started' if update: status = __salt__['virt.update'](name, cpu=cpu, mem=mem, disk_profile=disk_profile, disks=disks, nic_profile=nic_profile, interfaces=interfaces, graphics=graphics, live=False, connection=connection, username=username, password=password) if status['definition']: action_msg = 'updated and started' __salt__['virt.start'](name) ret['changes'][name] = 'Domain {0}'.format(action_msg) ret['comment'] = 'Domain {0} {1}'.format(name, action_msg) else: if update: status = __salt__['virt.update'](name, cpu=cpu, mem=mem, disk_profile=disk_profile, disks=disks, nic_profile=nic_profile, interfaces=interfaces, graphics=graphics, connection=connection, username=username, password=password) ret['changes'][name] = status if status.get('errors', None): ret['comment'] = 'Domain {0} updated, but some live update(s) failed'.format(name) elif not status['definition']: ret['comment'] = 'Domain {0} exists and is running'.format(name) else: ret['comment'] = 'Domain {0} updated, restart to fully apply the changes'.format(name) else: ret['comment'] = 'Domain {0} exists and is running'.format(name) except CommandExecutionError: if image: salt.utils.versions.warn_until( 'Sodium', '\'image\' parameter has been deprecated. Rather use the \'disks\' parameter ' 'to override or define the image. \'image\' will be removed in {version}.' ) __salt__['virt.init'](name, cpu=cpu, mem=mem, os_type=os_type, arch=arch, image=image, hypervisor=vm_type, disk=disk_profile, disks=disks, nic=nic_profile, interfaces=interfaces, graphics=graphics, seed=seed, install=install, pub_key=pub_key, priv_key=priv_key, connection=connection, username=username, password=password) ret['changes'][name] = 'Domain defined and started' ret['comment'] = 'Domain {0} defined and started'.format(name) except libvirt.libvirtError as err: # Something bad happened when starting / updating the VM, report it ret['comment'] = six.text_type(err) ret['result'] = False return ret def snapshot(name, suffix=None, connection=None, username=None, password=None): ''' Takes a snapshot of a particular VM or by a UNIX-style wildcard. .. versionadded:: 2016.3.0 :param connection: libvirt connection URI, overriding defaults .. versionadded:: 2019.2.0 :param username: username to connect with, overriding defaults .. versionadded:: 2019.2.0 :param password: password to connect with, overriding defaults .. versionadded:: 2019.2.0 .. code-block:: yaml domain_name: virt.snapshot: - suffix: periodic domain*: virt.snapshot: - suffix: periodic ''' return _virt_call(name, 'snapshot', 'saved', 'Snapshot has been taken', suffix=suffix, connection=connection, username=username, password=password) # Deprecated states def rebooted(name, connection=None, username=None, password=None): ''' Reboots VMs .. versionadded:: 2016.3.0 :param name: :param connection: libvirt connection URI, overriding defaults .. versionadded:: 2019.2.0 :param username: username to connect with, overriding defaults .. versionadded:: 2019.2.0 :param password: password to connect with, overriding defaults .. versionadded:: 2019.2.0 ''' return _virt_call(name, 'reboot', 'rebooted', "Machine has been rebooted", connection=connection, username=username, password=password) def unpowered(name): ''' .. deprecated:: 2016.3.0 Use :py:func:`~salt.modules.virt.powered_off` instead. Stops a VM by power off. .. versionadded:: 2016.3.0 .. code-block:: yaml domain_name: virt.stopped ''' return _virt_call(name, 'stop', 'unpowered', 'Machine has been powered off') def saved(name, suffix=None): ''' .. deprecated:: 2016.3.0 Use :py:func:`~salt.modules.virt.snapshot` instead. Takes a snapshot of a particular VM or by a UNIX-style wildcard. .. versionadded:: 2016.3.0 .. code-block:: yaml domain_name: virt.saved: - suffix: periodic domain*: virt.saved: - suffix: periodic ''' return _virt_call(name, 'snapshot', 'saved', 'Snapshots has been taken', suffix=suffix) def reverted(name, snapshot=None, cleanup=False): # pylint: disable=redefined-outer-name ''' .. deprecated:: 2016.3.0 Reverts to the particular snapshot. .. versionadded:: 2016.3.0 .. code-block:: yaml domain_name: virt.reverted: - cleanup: True domain_name_1: virt.reverted: - snapshot: snapshot_name - cleanup: False ''' ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} try: domains = fnmatch.filter(__salt__['virt.list_domains'](), name) if not domains: ret['comment'] = 'No domains found for criteria "{0}"'.format(name) else: ignored_domains = list() if len(domains) > 1: ret['changes'] = {'reverted': list()} for domain in domains: result = {} try: result = __salt__['virt.revert_snapshot'](domain, snapshot=snapshot, cleanup=cleanup) result = {'domain': domain, 'current': result['reverted'], 'deleted': result['deleted']} except CommandExecutionError as err: if len(domains) > 1: ignored_domains.append({'domain': domain, 'issue': six.text_type(err)}) if len(domains) > 1: if result: ret['changes']['reverted'].append(result) else: ret['changes'] = result break ret['result'] = len(domains) != len(ignored_domains) if ret['result']: ret['comment'] = 'Domain{0} has been reverted'.format(len(domains) > 1 and "s" or "") if ignored_domains: ret['changes']['ignored'] = ignored_domains if not ret['changes']['reverted']: ret['changes'].pop('reverted') except libvirt.libvirtError as err: ret['comment'] = six.text_type(err) except CommandExecutionError as err: ret['comment'] = six.text_type(err) return ret def network_running(name, bridge, forward, vport=None, tag=None, autostart=True, connection=None, username=None, password=None): ''' Defines and starts a new network with specified arguments. :param connection: libvirt connection URI, overriding defaults .. versionadded:: 2019.2.0 :param username: username to connect with, overriding defaults .. versionadded:: 2019.2.0 :param password: password to connect with, overriding defaults .. versionadded:: 2019.2.0 .. code-block:: yaml domain_name: virt.network_define .. code-block:: yaml network_name: virt.network_define: - bridge: main - forward: bridge - vport: openvswitch - tag: 180 - autostart: True ''' ret = {'name': name, 'changes': {}, 'result': True, 'comment': '' } try: info = __salt__['virt.network_info'](name, connection=connection, username=username, password=password) if info: if info['active']: ret['comment'] = 'Network {0} exists and is running'.format(name) else: __salt__['virt.network_start'](name, connection=connection, username=username, password=password) ret['changes'][name] = 'Network started' ret['comment'] = 'Network {0} started'.format(name) else: __salt__['virt.network_define'](name, bridge, forward, vport, tag=tag, autostart=autostart, start=True, connection=connection, username=username, password=password) ret['changes'][name] = 'Network defined and started' ret['comment'] = 'Network {0} defined and started'.format(name) except libvirt.libvirtError as err: ret['result'] = False ret['comment'] = err.get_error_message() return ret def pool_running(name, ptype=None, target=None, permissions=None, source=None, transient=False, autostart=True, connection=None, username=None, password=None): ''' Defines and starts a new pool with specified arguments. .. versionadded:: 2019.2.0 :param ptype: libvirt pool type :param target: full path to the target device or folder. (Default: ``None``) :param permissions: target permissions. See :ref:`pool-define-permissions` for more details on this structure. :param source: dictionary containing keys matching the ``source_*`` parameters in function :func:`salt.modules.virt.pool_define`. :param transient: when set to ``True``, the pool will be automatically undefined after being stopped. (Default: ``False``) :param autostart: Whether to start the pool when booting the host. (Default: ``True``) :param start: When ``True``, define and start the pool, otherwise the pool will be left stopped. :param connection: libvirt connection URI, overriding defaults :param username: username to connect with, overriding defaults :param password: password to connect with, overriding defaults .. code-block:: yaml pool_name: virt.pool_define .. code-block:: yaml pool_name: virt.pool_define: - ptype: netfs - target: /mnt/cifs - permissions: - mode: 0770 - owner: 1000 - group: 100 - source: - dir: samba_share - hosts: one.example.com two.example.com - format: cifs - autostart: True ''' ret = {'name': name, 'changes': {}, 'result': True, 'comment': '' } try: info = __salt__['virt.pool_info'](name, connection=connection, username=username, password=password) if info: if info['state'] == 'running': ret['comment'] = 'Pool {0} exists and is running'.format(name) else: __salt__['virt.pool_start'](name, connection=connection, username=username, password=password) ret['changes'][name] = 'Pool started' ret['comment'] = 'Pool {0} started'.format(name) else: __salt__['virt.pool_define'](name, ptype=ptype, target=target, permissions=permissions, source_devices=(source or {}).get('devices', None), source_dir=(source or {}).get('dir', None), source_adapter=(source or {}).get('adapter', None), source_hosts=(source or {}).get('hosts', None), source_auth=(source or {}).get('auth', None), source_name=(source or {}).get('name', None), source_format=(source or {}).get('format', None), transient=transient, start=False, connection=connection, username=username, password=password) if autostart: __salt__['virt.pool_set_autostart'](name, state='on' if autostart else 'off', connection=connection, username=username, password=password) __salt__['virt.pool_build'](name, connection=connection, username=username, password=password) ret['changes'][name] = 'Pool defined and started' ret['comment'] = 'Pool {0} defined and started'.format(name) except libvirt.libvirtError as err: ret['comment'] = err.get_error_message() ret['result'] = False return ret