Exemplo n.º 1
0
    def has_different_config(self):
        """
        Return the list of differences between the current parameters and the existing volume.

        :return: list of options that differ
        """
        differences = DifferenceTracker()
        if self.parameters.driver and self.parameters.driver != self.existing_volume['Driver']:
            differences.add('driver', parameter=self.parameters.driver, active=self.existing_volume['Driver'])
        if self.parameters.driver_options:
            if not self.existing_volume.get('Options'):
                differences.add('driver_options',
                                parameter=self.parameters.driver_options,
                                active=self.existing_volume.get('Options'))
            else:
                for key, value in iteritems(self.parameters.driver_options):
                    if (not self.existing_volume['Options'].get(key) or
                            value != self.existing_volume['Options'][key]):
                        differences.add('driver_options.%s' % key,
                                        parameter=value,
                                        active=self.existing_volume['Options'].get(key))
        if self.parameters.labels:
            existing_labels = self.existing_volume.get('Labels', {})
            for label in self.parameters.labels:
                if existing_labels.get(label) != self.parameters.labels.get(label):
                    differences.add('labels.%s' % label,
                                    parameter=self.parameters.labels.get(label),
                                    active=existing_labels.get(label))

        return differences
Exemplo n.º 2
0
    def present(self):
        different = False
        differences = DifferenceTracker()
        if self.existing_network:
            different, differences = self.has_different_config(
                self.existing_network)

        self.diff_tracker.add('exists',
                              parameter=True,
                              active=self.existing_network is not None)
        if self.parameters.force or different:
            self.remove_network()
            self.existing_network = None

        self.create_network()
        self.connect_containers()
        if not self.parameters.appends:
            self.disconnect_missing()

        if self.diff or self.check_mode or self.parameters.debug:
            self.diff_result[
                'differences'] = differences.get_legacy_docker_diffs()
            self.diff_tracker.merge(differences)

        if not self.check_mode and not self.parameters.debug:
            self.results.pop('actions')

        self.results['ansible_facts'] = {
            u'docker_network': self.get_existing_network()
        }
Exemplo n.º 3
0
    def __init__(self, client):
        self.client = client
        self.parameters = TaskParameters(client)
        self.check_mode = self.client.check_mode
        self.results = {
            u'changed': False,
            u'actions': []
        }
        self.diff = self.client.module._diff
        self.diff_tracker = DifferenceTracker()
        self.diff_result = dict()

        self.existing_network = self.get_existing_network()

        if not self.parameters.connected and self.existing_network:
            self.parameters.connected = container_names_in_network(self.existing_network)

        if (self.parameters.ipam_options['subnet'] or self.parameters.ipam_options['iprange'] or
                self.parameters.ipam_options['gateway'] or self.parameters.ipam_options['aux_addresses']):
            self.parameters.ipam_config = [self.parameters.ipam_options]

        if self.parameters.driver_options:
            self.parameters.driver_options = clean_dict_booleans_for_docker_api(self.parameters.driver_options)

        state = self.parameters.state
        if state == 'present':
            self.present()
        elif state == 'absent':
            self.absent()

        if self.diff or self.check_mode or self.parameters.debug:
            if self.diff:
                self.diff_result['before'], self.diff_result['after'] = self.diff_tracker.get_before_after()
            self.results['diff'] = self.diff_result
Exemplo n.º 4
0
    def present(self):
        differences = DifferenceTracker()
        if self.existing_volume:
            differences = self.has_different_config()

        self.diff_tracker.add('exists',
                              parameter=True,
                              active=self.existing_volume is not None)
        if not differences.empty or self.parameters.force:
            self.remove_volume()
            self.existing_volume = None

        self.create_volume()

        if self.diff or self.check_mode or self.parameters.debug:
            self.diff_result[
                'differences'] = differences.get_legacy_docker_diffs()
            self.diff_tracker.merge(differences)

        if not self.check_mode and not self.parameters.debug:
            self.results.pop('actions')

        self.results['ansible_facts'] = {
            u'docker_volume': self.get_existing_volume()
        }
Exemplo n.º 5
0
    def __init__(self, client):
        self.client = client
        self.parameters = TaskParameters(client)
        self.check_mode = self.client.check_mode
        self.results = {u'changed': False, u'actions': []}
        self.diff = self.client.module._diff
        self.diff_tracker = DifferenceTracker()
        self.diff_result = dict()

        self.existing_volume = self.get_existing_volume()

        state = self.parameters.state
        if state == 'present':
            self.present()
        elif state == 'absent':
            self.absent()

        if self.diff or self.check_mode or self.parameters.debug:
            if self.diff:
                self.diff_result['before'], self.diff_result[
                    'after'] = self.diff_tracker.get_before_after()
            self.results['diff'] = self.diff_result
Exemplo n.º 6
0
    def has_different_config(self, net):
        '''
        Evaluates an existing network and returns a tuple containing a boolean
        indicating if the configuration is different and a list of differences.

        :param net: the inspection output for an existing network
        :return: (bool, list)
        '''
        differences = DifferenceTracker()
        if self.parameters.driver and self.parameters.driver != net['Driver']:
            differences.add('driver',
                            parameter=self.parameters.driver,
                            active=net['Driver'])
        if self.parameters.driver_options:
            if not net.get('Options'):
                differences.add('driver_options',
                                parameter=self.parameters.driver_options,
                                active=net.get('Options'))
            else:
                for key, value in self.parameters.driver_options.items():
                    if not (key
                            in net['Options']) or value != net['Options'][key]:
                        differences.add('driver_options.%s' % key,
                                        parameter=value,
                                        active=net['Options'].get(key))

        if self.parameters.ipam_driver:
            if not net.get('IPAM') or net['IPAM'][
                    'Driver'] != self.parameters.ipam_driver:
                differences.add('ipam_driver',
                                parameter=self.parameters.ipam_driver,
                                active=net.get('IPAM'))

        if self.parameters.ipam_config is not None and self.parameters.ipam_config:
            if not net.get('IPAM') or not net['IPAM']['Config']:
                differences.add('ipam_config',
                                parameter=self.parameters.ipam_config,
                                active=net.get('IPAM', {}).get('Config'))
            else:
                for idx, ipam_config in enumerate(self.parameters.ipam_config):
                    net_config = dict()
                    try:
                        ip_version = get_ip_version(ipam_config['subnet'])
                        for net_ipam_config in net['IPAM']['Config']:
                            if ip_version == get_ip_version(
                                    net_ipam_config['Subnet']):
                                net_config = net_ipam_config
                    except ValueError as e:
                        self.client.fail(str(e))

                    for key, value in ipam_config.items():
                        if value is None:
                            # due to recursive argument_spec, all keys are always present
                            # (but have default value None if not specified)
                            continue
                        camelkey = None
                        for net_key in net_config:
                            if key == net_key.lower():
                                camelkey = net_key
                                break
                        if not camelkey or net_config.get(camelkey) != value:
                            differences.add('ipam_config[%s].%s' % (idx, key),
                                            parameter=value,
                                            active=net_config.get(camelkey)
                                            if camelkey else None)

        if self.parameters.enable_ipv6 is not None and self.parameters.enable_ipv6 != net.get(
                'EnableIPv6', False):
            differences.add('enable_ipv6',
                            parameter=self.parameters.enable_ipv6,
                            active=net.get('EnableIPv6', False))

        if self.parameters.internal is not None and self.parameters.internal != net.get(
                'Internal', False):
            differences.add('internal',
                            parameter=self.parameters.internal,
                            active=net.get('Internal'))

        if self.parameters.scope is not None and self.parameters.scope != net.get(
                'Scope'):
            differences.add('scope',
                            parameter=self.parameters.scope,
                            active=net.get('Scope'))

        if self.parameters.attachable is not None and self.parameters.attachable != net.get(
                'Attachable', False):
            differences.add('attachable',
                            parameter=self.parameters.attachable,
                            active=net.get('Attachable'))

        return not differences.empty, differences
Exemplo n.º 7
0
class DockerNetworkManager(object):
    def _get_minimal_versions(self):
        # TODO: Move this and the same from docker_container.py to docker_common.py
        self.option_minimal_versions = dict()
        for option, data in self.client.module.argument_spec.items():
            self.option_minimal_versions[option] = dict()
        self.option_minimal_versions.update(
            dict(
                scope=dict(docker_py_version='2.6.0',
                           docker_api_version='1.30'),
                attachable=dict(docker_py_version='2.0.0',
                                docker_api_version='1.26'),
            ))

        for option, data in self.option_minimal_versions.items():
            # Test whether option is supported, and store result
            support_docker_py = True
            support_docker_api = True
            if 'docker_py_version' in data:
                support_docker_py = self.client.docker_py_version >= LooseVersion(
                    data['docker_py_version'])
            if 'docker_api_version' in data:
                support_docker_api = self.client.docker_api_version >= LooseVersion(
                    data['docker_api_version'])
            data['supported'] = support_docker_py and support_docker_api
            # Fail if option is not supported but used
            if not data['supported']:
                # Test whether option is specified
                if 'detect_usage' in data:
                    used = data['detect_usage']()
                else:
                    used = self.client.module.params.get(option) is not None
                    if used and 'default' in self.client.module.argument_spec[
                            option]:
                        used = self.client.module.params[
                            option] != self.client.module.argument_spec[
                                option]['default']
                if used:
                    # If the option is used, compose error message.
                    if 'usage_msg' in data:
                        usg = data['usage_msg']
                    else:
                        usg = 'set %s option' % (option, )
                    if not support_docker_api:
                        msg = 'docker API version is %s. Minimum version required is %s to %s.'
                        msg = msg % (self.client.docker_api_version_str,
                                     data['docker_api_version'], usg)
                    elif not support_docker_py:
                        if LooseVersion(data['docker_py_version']
                                        ) < LooseVersion('2.0.0'):
                            msg = (
                                "docker-py version is %s. Minimum version required is %s to %s. "
                                "Consider switching to the 'docker' package if you do not require Python 2.6 support."
                            )
                        elif self.client.docker_py_version < LooseVersion(
                                '2.0.0'):
                            msg = (
                                "docker-py version is %s. Minimum version required is %s to %s. "
                                "You have to switch to the Python 'docker' package. First uninstall 'docker-py' before "
                                "installing 'docker' to avoid a broken installation."
                            )
                        else:
                            msg = "docker version is %s. Minimum version required is %s to %s."
                        msg = msg % (docker_version, data['docker_py_version'],
                                     usg)
                    else:
                        # should not happen
                        msg = 'Cannot %s with your configuration.' % (usg, )
                    self.client.fail(msg)

    def __init__(self, client):
        self.client = client
        self.parameters = TaskParameters(client)
        self.check_mode = self.client.check_mode
        self.results = {u'changed': False, u'actions': []}
        self.diff = self.client.module._diff
        self.diff_tracker = DifferenceTracker()
        self.diff_result = dict()

        self._get_minimal_versions()

        self.existing_network = self.get_existing_network()

        if not self.parameters.connected and self.existing_network:
            self.parameters.connected = container_names_in_network(
                self.existing_network)

        if (self.parameters.ipam_options['subnet']
                or self.parameters.ipam_options['iprange']
                or self.parameters.ipam_options['gateway']
                or self.parameters.ipam_options['aux_addresses']):
            self.parameters.ipam_config = [self.parameters.ipam_options]

        if self.parameters.driver_options:
            self.parameters.driver_options = get_driver_options(
                self.parameters.driver_options)

        state = self.parameters.state
        if state == 'present':
            self.present()
        elif state == 'absent':
            self.absent()

        if self.diff or self.check_mode or self.parameters.debug:
            if self.diff:
                self.diff_result['before'], self.diff_result[
                    'after'] = self.diff_tracker.get_before_after()
            self.results['diff'] = self.diff_result

    def get_existing_network(self):
        try:
            return self.client.inspect_network(self.parameters.network_name)
        except NotFound:
            return None

    def has_different_config(self, net):
        '''
        Evaluates an existing network and returns a tuple containing a boolean
        indicating if the configuration is different and a list of differences.

        :param net: the inspection output for an existing network
        :return: (bool, list)
        '''
        differences = DifferenceTracker()
        if self.parameters.driver and self.parameters.driver != net['Driver']:
            differences.add('driver',
                            parameter=self.parameters.driver,
                            active=net['Driver'])
        if self.parameters.driver_options:
            if not net.get('Options'):
                differences.add('driver_options',
                                parameter=self.parameters.driver_options,
                                active=net.get('Options'))
            else:
                for key, value in self.parameters.driver_options.items():
                    if not (key
                            in net['Options']) or value != net['Options'][key]:
                        differences.add('driver_options.%s' % key,
                                        parameter=value,
                                        active=net['Options'].get(key))

        if self.parameters.ipam_driver:
            if not net.get('IPAM') or net['IPAM'][
                    'Driver'] != self.parameters.ipam_driver:
                differences.add('ipam_driver',
                                parameter=self.parameters.ipam_driver,
                                active=net.get('IPAM'))

        if self.parameters.ipam_config is not None and self.parameters.ipam_config:
            if not net.get('IPAM') or not net['IPAM']['Config']:
                differences.add('ipam_config',
                                parameter=self.parameters.ipam_config,
                                active=net.get('IPAM', {}).get('Config'))
            else:
                for idx, ipam_config in enumerate(self.parameters.ipam_config):
                    net_config = dict()
                    try:
                        ip_version = get_ip_version(ipam_config['subnet'])
                        for net_ipam_config in net['IPAM']['Config']:
                            if ip_version == get_ip_version(
                                    net_ipam_config['Subnet']):
                                net_config = net_ipam_config
                    except ValueError as e:
                        self.client.fail(str(e))

                    for key, value in ipam_config.items():
                        if value is None:
                            # due to recursive argument_spec, all keys are always present
                            # (but have default value None if not specified)
                            continue
                        camelkey = None
                        for net_key in net_config:
                            if key == net_key.lower():
                                camelkey = net_key
                                break
                        if not camelkey or net_config.get(camelkey) != value:
                            differences.add('ipam_config[%s].%s' % (idx, key),
                                            parameter=value,
                                            active=net_config.get(camelkey)
                                            if camelkey else None)

        if self.parameters.enable_ipv6 is not None and self.parameters.enable_ipv6 != net.get(
                'EnableIPv6', False):
            differences.add('enable_ipv6',
                            parameter=self.parameters.enable_ipv6,
                            active=net.get('EnableIPv6', False))

        if self.parameters.internal is not None and self.parameters.internal != net.get(
                'Internal', False):
            differences.add('internal',
                            parameter=self.parameters.internal,
                            active=net.get('Internal'))

        if self.parameters.scope is not None and self.parameters.scope != net.get(
                'Scope'):
            differences.add('scope',
                            parameter=self.parameters.scope,
                            active=net.get('Scope'))

        if self.parameters.attachable is not None and self.parameters.attachable != net.get(
                'Attachable', False):
            differences.add('attachable',
                            parameter=self.parameters.attachable,
                            active=net.get('Attachable'))

        return not differences.empty, differences

    def create_network(self):
        if not self.existing_network:
            params = dict(
                driver=self.parameters.driver,
                options=self.parameters.driver_options,
            )

            ipam_pools = []
            if self.parameters.ipam_config:
                for ipam_pool in self.parameters.ipam_config:
                    if LooseVersion(docker_version) >= LooseVersion('2.0.0'):
                        ipam_pools.append(IPAMPool(**ipam_pool))
                    else:
                        ipam_pools.append(utils.create_ipam_pool(**ipam_pool))

            if self.parameters.ipam_driver or ipam_pools:
                # Only add ipam parameter if a driver was specified or if IPAM parameters
                # were specified. Leaving this parameter away can significantly speed up
                # creation; on my machine creation with this option needs ~15 seconds,
                # and without just a few seconds.
                if LooseVersion(docker_version) >= LooseVersion('2.0.0'):
                    params['ipam'] = IPAMConfig(
                        driver=self.parameters.ipam_driver,
                        pool_configs=ipam_pools)
                else:
                    params['ipam'] = utils.create_ipam_config(
                        driver=self.parameters.ipam_driver,
                        pool_configs=ipam_pools)

            if self.parameters.enable_ipv6 is not None:
                params['enable_ipv6'] = self.parameters.enable_ipv6
            if self.parameters.internal is not None:
                params['internal'] = self.parameters.internal
            if self.parameters.scope is not None:
                params['scope'] = self.parameters.scope
            if self.parameters.attachable is not None:
                params['attachable'] = self.parameters.attachable

            if not self.check_mode:
                resp = self.client.create_network(self.parameters.network_name,
                                                  **params)

                self.existing_network = self.client.inspect_network(resp['Id'])
            self.results['actions'].append(
                "Created network %s with driver %s" %
                (self.parameters.network_name, self.parameters.driver))
            self.results['changed'] = True

    def remove_network(self):
        if self.existing_network:
            self.disconnect_all_containers()
            if not self.check_mode:
                self.client.remove_network(self.parameters.network_name)
            self.results['actions'].append("Removed network %s" %
                                           (self.parameters.network_name, ))
            self.results['changed'] = True

    def is_container_connected(self, container_name):
        return container_name in container_names_in_network(
            self.existing_network)

    def connect_containers(self):
        for name in self.parameters.connected:
            if not self.is_container_connected(name):
                if not self.check_mode:
                    self.client.connect_container_to_network(
                        name, self.parameters.network_name)
                self.results['actions'].append("Connected container %s" %
                                               (name, ))
                self.results['changed'] = True
                self.diff_tracker.add('connected.{0}'.format(name),
                                      parameter=True,
                                      active=False)

    def disconnect_missing(self):
        if not self.existing_network:
            return
        containers = self.existing_network['Containers']
        if not containers:
            return
        for c in containers.values():
            name = c['Name']
            if name not in self.parameters.connected:
                self.disconnect_container(name)

    def disconnect_all_containers(self):
        containers = self.client.inspect_network(
            self.parameters.network_name)['Containers']
        if not containers:
            return
        for cont in containers.values():
            self.disconnect_container(cont['Name'])

    def disconnect_container(self, container_name):
        if not self.check_mode:
            self.client.disconnect_container_from_network(
                container_name, self.parameters.network_name)
        self.results['actions'].append("Disconnected container %s" %
                                       (container_name, ))
        self.results['changed'] = True
        self.diff_tracker.add('connected.{0}'.format(container_name),
                              parameter=False,
                              active=True)

    def present(self):
        different = False
        differences = DifferenceTracker()
        if self.existing_network:
            different, differences = self.has_different_config(
                self.existing_network)

        self.diff_tracker.add('exists',
                              parameter=True,
                              active=self.existing_network is not None)
        if self.parameters.force or different:
            self.remove_network()
            self.existing_network = None

        self.create_network()
        self.connect_containers()
        if not self.parameters.appends:
            self.disconnect_missing()

        if self.diff or self.check_mode or self.parameters.debug:
            self.diff_result[
                'differences'] = differences.get_legacy_docker_diffs()
            self.diff_tracker.merge(differences)

        if not self.check_mode and not self.parameters.debug:
            self.results.pop('actions')

        self.results['ansible_facts'] = {
            u'docker_network': self.get_existing_network()
        }

    def absent(self):
        self.diff_tracker.add('exists',
                              parameter=False,
                              active=self.existing_network is not None)
        self.remove_network()
Exemplo n.º 8
0
class DockerVolumeManager(object):

    def __init__(self, client):
        self.client = client
        self.parameters = TaskParameters(client)
        self.check_mode = self.client.check_mode
        self.results = {
            u'changed': False,
            u'actions': []
        }
        self.diff = self.client.module._diff
        self.diff_tracker = DifferenceTracker()
        self.diff_result = dict()

        self.existing_volume = self.get_existing_volume()

        state = self.parameters.state
        if state == 'present':
            self.present()
        elif state == 'absent':
            self.absent()

        if self.diff or self.check_mode or self.parameters.debug:
            if self.diff:
                self.diff_result['before'], self.diff_result['after'] = self.diff_tracker.get_before_after()
            self.results['diff'] = self.diff_result

    def get_existing_volume(self):
        try:
            volumes = self.client.volumes()
        except APIError as e:
            self.client.fail(text_type(e))

        if volumes[u'Volumes'] is None:
            return None

        for volume in volumes[u'Volumes']:
            if volume['Name'] == self.parameters.volume_name:
                return volume

        return None

    def has_different_config(self):
        """
        Return the list of differences between the current parameters and the existing volume.

        :return: list of options that differ
        """
        differences = DifferenceTracker()
        if self.parameters.driver and self.parameters.driver != self.existing_volume['Driver']:
            differences.add('driver', parameter=self.parameters.driver, active=self.existing_volume['Driver'])
        if self.parameters.driver_options:
            if not self.existing_volume.get('Options'):
                differences.add('driver_options',
                                parameter=self.parameters.driver_options,
                                active=self.existing_volume.get('Options'))
            else:
                for key, value in iteritems(self.parameters.driver_options):
                    if (not self.existing_volume['Options'].get(key) or
                            value != self.existing_volume['Options'][key]):
                        differences.add('driver_options.%s' % key,
                                        parameter=value,
                                        active=self.existing_volume['Options'].get(key))
        if self.parameters.labels:
            existing_labels = self.existing_volume.get('Labels', {})
            for label in self.parameters.labels:
                if existing_labels.get(label) != self.parameters.labels.get(label):
                    differences.add('labels.%s' % label,
                                    parameter=self.parameters.labels.get(label),
                                    active=existing_labels.get(label))

        return differences

    def create_volume(self):
        if not self.existing_volume:
            if not self.check_mode:
                try:
                    params = dict(
                        driver=self.parameters.driver,
                        driver_opts=self.parameters.driver_options,
                    )

                    if self.parameters.labels is not None:
                        params['labels'] = self.parameters.labels

                    resp = self.client.create_volume(self.parameters.volume_name, **params)
                    self.existing_volume = self.client.inspect_volume(resp['Name'])
                except APIError as e:
                    self.client.fail(text_type(e))

            self.results['actions'].append("Created volume %s with driver %s" % (self.parameters.volume_name, self.parameters.driver))
            self.results['changed'] = True

    def remove_volume(self):
        if self.existing_volume:
            if not self.check_mode:
                try:
                    self.client.remove_volume(self.parameters.volume_name)
                except APIError as e:
                    self.client.fail(text_type(e))

            self.results['actions'].append("Removed volume %s" % self.parameters.volume_name)
            self.results['changed'] = True

    def present(self):
        differences = DifferenceTracker()
        if self.existing_volume:
            differences = self.has_different_config()

        self.diff_tracker.add('exists', parameter=True, active=self.existing_volume is not None)
        if (not differences.empty and self.parameters.recreate == 'options-changed') or self.parameters.recreate == 'always':
            self.remove_volume()
            self.existing_volume = None

        self.create_volume()

        if self.diff or self.check_mode or self.parameters.debug:
            self.diff_result['differences'] = differences.get_legacy_docker_diffs()
            self.diff_tracker.merge(differences)

        if not self.check_mode and not self.parameters.debug:
            self.results.pop('actions')

        self.results['ansible_facts'] = {u'docker_volume': self.get_existing_volume()}

    def absent(self):
        self.diff_tracker.add('exists', parameter=False, active=self.existing_volume is not None)
        self.remove_volume()
Exemplo n.º 9
0
class DockerServiceManager():
    def get_networks_names_ids(self):
        return [{
            'name': n['Name'],
            'id': n['Id']
        } for n in self.client.networks()]

    def get_service(self, name):
        raw_data = self.client.services(filters={'name': name})
        if len(raw_data) == 0:
            return None

        raw_data = raw_data[0]
        networks_names_ids = self.get_networks_names_ids()
        ds = DockerService()

        task_template_data = raw_data['Spec']['TaskTemplate']
        update_config_data = raw_data['Spec']['UpdateConfig']

        ds.image = task_template_data['ContainerSpec']['Image']
        ds.user = task_template_data['ContainerSpec'].get('User', 'root')
        ds.env = task_template_data['ContainerSpec'].get('Env', [])
        ds.args = task_template_data['ContainerSpec'].get('Args', [])
        ds.update_delay = update_config_data['Delay']
        ds.update_parallelism = update_config_data['Parallelism']
        ds.update_failure_action = update_config_data['FailureAction']
        ds.update_monitor = update_config_data['Monitor']
        ds.update_max_failure_ratio = update_config_data['MaxFailureRatio']

        if 'Order' in update_config_data:
            ds.update_order = update_config_data['Order']

        dns_config = task_template_data['ContainerSpec'].get('DNSConfig', None)
        if dns_config:
            if 'Nameservers' in dns_config.keys():
                ds.dns = dns_config['Nameservers']
            if 'Search' in dns_config.keys():
                ds.dns_search = dns_config['Search']
            if 'Options' in dns_config.keys():
                ds.dns_options = dns_config['Options']
        ds.hostname = task_template_data['ContainerSpec'].get('Hostname', '')
        ds.tty = task_template_data['ContainerSpec'].get('TTY', False)
        if 'Placement' in task_template_data.keys():
            ds.constraints = task_template_data['Placement'].get(
                'Constraints', [])

        restart_policy_data = task_template_data.get('RestartPolicy', None)
        if restart_policy_data:
            ds.restart_policy = restart_policy_data.get('Condition')
            ds.restart_policy_delay = restart_policy_data.get('Delay')
            ds.restart_policy_attempts = restart_policy_data.get('MaxAttempts')
            ds.restart_policy_window = restart_policy_data.get('Window')

        raw_data_endpoint = raw_data.get('Endpoint', None)
        if raw_data_endpoint:
            raw_data_endpoint_spec = raw_data_endpoint.get('Spec', None)
            if raw_data_endpoint_spec:
                ds.endpoint_mode = raw_data_endpoint_spec.get('Mode', 'vip')
                for port in raw_data_endpoint_spec.get('Ports', []):
                    ds.publish.append({
                        'protocol':
                        port['Protocol'],
                        'mode':
                        port.get('PublishMode', None),
                        'published_port':
                        int(port['PublishedPort']),
                        'target_port':
                        int(port['TargetPort'])
                    })

        if 'Resources' in task_template_data.keys():
            if 'Limits' in task_template_data['Resources'].keys():
                if 'NanoCPUs' in task_template_data['Resources'][
                        'Limits'].keys():
                    ds.limit_cpu = float(task_template_data['Resources']
                                         ['Limits']['NanoCPUs']) / 1000000000
                if 'MemoryBytes' in task_template_data['Resources'][
                        'Limits'].keys():
                    ds.limit_memory = int(task_template_data['Resources']
                                          ['Limits']['MemoryBytes'])
            if 'Reservations' in task_template_data['Resources'].keys():
                if 'NanoCPUs' in task_template_data['Resources'][
                        'Reservations'].keys():
                    ds.reserve_cpu = float(
                        task_template_data['Resources']['Reservations']
                        ['NanoCPUs']) / 1000000000
                if 'MemoryBytes' in task_template_data['Resources'][
                        'Reservations'].keys():
                    ds.reserve_memory = int(task_template_data['Resources']
                                            ['Reservations']['MemoryBytes'])

        ds.labels = raw_data['Spec'].get('Labels', {})
        if 'LogDriver' in task_template_data.keys():
            ds.log_driver = task_template_data['LogDriver'].get(
                'Name', 'json-file')
            ds.log_driver_options = task_template_data['LogDriver'].get(
                'Options', {})
        ds.container_labels = task_template_data['ContainerSpec'].get(
            'Labels', {})
        mode = raw_data['Spec']['Mode']
        if 'Replicated' in mode.keys():
            ds.mode = to_text('replicated', encoding='utf-8')
            ds.replicas = mode['Replicated']['Replicas']
        elif 'Global' in mode.keys():
            ds.mode = 'global'
        else:
            raise Exception("Unknown service mode: %s" % mode)
        for mount_data in raw_data['Spec']['TaskTemplate'][
                'ContainerSpec'].get('Mounts', []):
            ds.mounts.append({
                'source': mount_data['Source'],
                'type': mount_data['Type'],
                'target': mount_data['Target'],
                'readonly': mount_data.get('ReadOnly', False)
            })
        for config_data in raw_data['Spec']['TaskTemplate'][
                'ContainerSpec'].get('Configs', []):
            ds.configs.append({
                'config_id': config_data['ConfigID'],
                'config_name': config_data['ConfigName'],
                'filename': config_data['File'].get('Name'),
                'uid': int(config_data['File'].get('UID')),
                'gid': int(config_data['File'].get('GID')),
                'mode': config_data['File'].get('Mode')
            })
        for secret_data in raw_data['Spec']['TaskTemplate'][
                'ContainerSpec'].get('Secrets', []):
            ds.secrets.append({
                'secret_id': secret_data['SecretID'],
                'secret_name': secret_data['SecretName'],
                'filename': secret_data['File'].get('Name'),
                'uid': int(secret_data['File'].get('UID')),
                'gid': int(secret_data['File'].get('GID')),
                'mode': secret_data['File'].get('Mode')
            })
        networks_names_ids = self.get_networks_names_ids()
        for raw_network_data in raw_data['Spec']['TaskTemplate'].get(
                'Networks', raw_data['Spec'].get('Networks', [])):
            network_name = [
                network_name_id['name']
                for network_name_id in networks_names_ids
                if network_name_id['id'] == raw_network_data['Target']
            ]
            if len(network_name) == 0:
                ds.networks.append(raw_network_data['Target'])
            else:
                ds.networks.append(network_name[0])
        ds.service_version = raw_data['Version']['Index']
        ds.service_id = raw_data['ID']
        return ds

    def update_service(self, name, old_service, new_service):
        update_policy, task_template, networks, endpoint_spec, mode, labels = new_service.generate_docker_py_service_description(
            name, self.get_networks_names_ids())
        self.client.update_service(old_service.service_id,
                                   old_service.service_version,
                                   name=name,
                                   endpoint_spec=endpoint_spec,
                                   networks=networks,
                                   mode=mode,
                                   update_config=update_policy,
                                   task_template=task_template,
                                   labels=labels)

    def create_service(self, name, service):
        update_policy, task_template, networks, endpoint_spec, mode, labels = service.generate_docker_py_service_description(
            name, self.get_networks_names_ids())
        self.client.create_service(name=name,
                                   endpoint_spec=endpoint_spec,
                                   mode=mode,
                                   networks=networks,
                                   update_config=update_policy,
                                   task_template=task_template,
                                   labels=labels)

    def remove_service(self, name):
        self.client.remove_service(name)

    def __init__(self, client):
        self.client = client
        self.diff_tracker = DifferenceTracker()

    def test_parameter_versions(self):
        parameters_versions = [{
            'param': 'dns',
            'attribute': 'dns',
            'min_version': '1.25'
        }, {
            'param': 'dns_options',
            'attribute': 'dns_options',
            'min_version': '1.25'
        }, {
            'param': 'dns_search',
            'attribute': 'dns_search',
            'min_version': '1.25'
        }, {
            'param': 'hostname',
            'attribute': 'hostname',
            'min_version': '1.25'
        }, {
            'param': 'tty',
            'attribute': 'tty',
            'min_version': '1.25'
        }, {
            'param': 'secrets',
            'attribute': 'secrets',
            'min_version': '1.25'
        }, {
            'param': 'configs',
            'attribute': 'configs',
            'min_version': '1.30'
        }, {
            'param': 'update_order',
            'attribute': 'update_order',
            'min_version': '1.29'
        }]
        params = self.client.module.params
        empty_service = DockerService()
        for pv in parameters_versions:
            if (params[pv['param']] != getattr(empty_service, pv['attribute'])
                    and (LooseVersion(self.client.version()['ApiVersion']) <
                         LooseVersion(pv['min_version']))):
                self.client.module.fail_json(
                    msg=('%s parameter supported only with api_version>=%s' %
                         (pv['param'], pv['min_version'])))

        for publish_def in self.client.module.params.get('publish', []):
            if 'mode' in publish_def.keys():
                if LooseVersion(self.client.version()
                                ['ApiVersion']) < LooseVersion('1.25'):
                    self.client.module.fail_json(
                        msg=
                        'publish.mode parameter supported only with api_version>=1.25'
                    )
                if LooseVersion(docker_version) < LooseVersion('3.0.0'):
                    self.client.module.fail_json(
                        msg=
                        'publish.mode parameter requires docker python library>=3.0.0'
                    )

    def run(self):
        self.test_parameter_versions()

        module = self.client.module
        try:
            current_service = self.get_service(module.params['name'])
        except Exception as e:
            return module.fail_json(
                msg="Error looking for service named %s: %s" %
                (module.params['name'], e))
        try:
            new_service = DockerService.from_ansible_params(
                module.params, current_service)
        except Exception as e:
            return module.fail_json(msg="Error parsing module parameters: %s" %
                                    e)

        changed = False
        msg = 'noop'
        rebuilt = False
        differences = DifferenceTracker()
        facts = {}

        if current_service:
            if module.params['state'] == 'absent':
                if not module.check_mode:
                    self.remove_service(module.params['name'])
                msg = 'Service removed'
                changed = True
            else:
                changed, differences, need_rebuild, force_update = new_service.compare(
                    current_service)
                if changed:
                    self.diff_tracker.merge(differences)
                    if need_rebuild:
                        if not module.check_mode:
                            self.remove_service(module.params['name'])
                            self.create_service(module.params['name'],
                                                new_service)
                        msg = 'Service rebuilt'
                        rebuilt = True
                    else:
                        if not module.check_mode:
                            self.update_service(module.params['name'],
                                                current_service, new_service)
                        msg = 'Service updated'
                        rebuilt = False
                else:
                    if force_update:
                        if not module.check_mode:
                            self.update_service(module.params['name'],
                                                current_service, new_service)
                        msg = 'Service forcefully updated'
                        rebuilt = False
                        changed = True
                    else:
                        msg = 'Service unchanged'
                facts = new_service.get_facts()
        else:
            if module.params['state'] == 'absent':
                msg = 'Service absent'
            else:
                if not module.check_mode:
                    service_id = self.create_service(module.params['name'],
                                                     new_service)
                msg = 'Service created'
                changed = True
                facts = new_service.get_facts()

        return msg, changed, rebuilt, differences.get_legacy_docker_diffs(
        ), facts
Exemplo n.º 10
0
 def compare(self, os):
     differences = DifferenceTracker()
     needs_rebuild = False
     force_update = False
     if self.endpoint_mode != os.endpoint_mode:
         differences.add('endpoint_mode',
                         parameter=self.endpoint_mode,
                         active=os.endpoint_mode)
     if self.env != os.env:
         differences.add('env', parameter=self.env, active=os.env)
     if self.log_driver != os.log_driver:
         differences.add('log_driver',
                         parameter=self.log_driver,
                         active=os.log_driver)
     if self.log_driver_options != os.log_driver_options:
         differences.add('log_opt',
                         parameter=self.log_driver_options,
                         active=os.log_driver_options)
     if self.mode != os.mode:
         needs_rebuild = True
         differences.add('mode', parameter=self.mode, active=os.mode)
     if self.mounts != os.mounts:
         differences.add('mounts', parameter=self.mounts, active=os.mounts)
     if self.configs != os.configs:
         differences.add('configs',
                         parameter=self.configs,
                         active=os.configs)
     if self.secrets != os.secrets:
         differences.add('secrets',
                         parameter=self.secrets,
                         active=os.secrets)
     if self.networks != os.networks:
         differences.add('networks',
                         parameter=self.networks,
                         active=os.networks)
         needs_rebuild = True
     if self.replicas != os.replicas:
         differences.add('replicas',
                         parameter=self.replicas,
                         active=os.replicas)
     if self.args != os.args:
         differences.add('args', parameter=self.args, active=os.args)
     if self.constraints != os.constraints:
         differences.add('constraints',
                         parameter=self.constraints,
                         active=os.constraints)
     if self.labels != os.labels:
         differences.add('labels', parameter=self.labels, active=os.labels)
     if self.limit_cpu != os.limit_cpu:
         differences.add('limit_cpu',
                         parameter=self.limit_cpu,
                         active=os.limit_cpu)
     if self.limit_memory != os.limit_memory:
         differences.add('limit_memory',
                         parameter=self.limit_memory,
                         active=os.limit_memory)
     if self.reserve_cpu != os.reserve_cpu:
         differences.add('reserve_cpu',
                         parameter=self.reserve_cpu,
                         active=os.reserve_cpu)
     if self.reserve_memory != os.reserve_memory:
         differences.add('reserve_memory',
                         parameter=self.reserve_memory,
                         active=os.reserve_memory)
     if self.container_labels != os.container_labels:
         differences.add('container_labels',
                         parameter=self.container_labels,
                         active=os.container_labels)
     if self.publish != os.publish:
         differences.add('publish',
                         parameter=self.publish,
                         active=os.publish)
     if self.restart_policy != os.restart_policy:
         differences.add('restart_policy',
                         parameter=self.restart_policy,
                         active=os.restart_policy)
     if self.restart_policy_attempts != os.restart_policy_attempts:
         differences.add('restart_policy_attempts',
                         parameter=self.restart_policy_attempts,
                         active=os.restart_policy_attempts)
     if self.restart_policy_delay != os.restart_policy_delay:
         differences.add('restart_policy_delay',
                         parameter=self.restart_policy_delay,
                         active=os.restart_policy_delay)
     if self.restart_policy_window != os.restart_policy_window:
         differences.add('restart_policy_window',
                         parameter=self.restart_policy_window,
                         active=os.restart_policy_window)
     if self.update_delay != os.update_delay:
         differences.add('update_delay',
                         parameter=self.update_delay,
                         active=os.update_delay)
     if self.update_parallelism != os.update_parallelism:
         differences.add('update_parallelism',
                         parameter=self.update_parallelism,
                         active=os.update_parallelism)
     if self.update_failure_action != os.update_failure_action:
         differences.add('update_failure_action',
                         parameter=self.update_failure_action,
                         active=os.update_failure_action)
     if self.update_monitor != os.update_monitor:
         differences.add('update_monitor',
                         parameter=self.update_monitor,
                         active=os.update_monitor)
     if self.update_max_failure_ratio != os.update_max_failure_ratio:
         differences.add('update_max_failure_ratio',
                         parameter=self.update_max_failure_ratio,
                         active=os.update_max_failure_ratio)
     if self.update_order != os.update_order:
         differences.add('update_order',
                         parameter=self.update_order,
                         active=os.update_order)
     if self.image != os.image.split('@')[0]:
         differences.add('image',
                         parameter=self.image,
                         active=os.image.split('@')[0])
     if self.user and self.user != os.user:
         differences.add('user', parameter=self.user, active=os.user)
     if self.dns != os.dns:
         differences.add('dns', parameter=self.dns, active=os.dns)
     if self.dns_search != os.dns_search:
         differences.add('dns_search',
                         parameter=self.dns_search,
                         active=os.dns_search)
     if self.dns_options != os.dns_options:
         differences.add('dns_options',
                         parameter=self.dns_options,
                         active=os.dns_options)
     if self.hostname != os.hostname:
         differences.add('hostname',
                         parameter=self.hostname,
                         active=os.hostname)
     if self.tty != os.tty:
         differences.add('tty', parameter=self.tty, active=os.tty)
     if self.force_update:
         force_update = True
     return not differences.empty or force_update, differences, needs_rebuild, force_update
Exemplo n.º 11
0
    def run(self):
        self.test_parameter_versions()

        module = self.client.module
        try:
            current_service = self.get_service(module.params['name'])
        except Exception as e:
            return module.fail_json(
                msg="Error looking for service named %s: %s" %
                (module.params['name'], e))
        try:
            new_service = DockerService.from_ansible_params(
                module.params, current_service)
        except Exception as e:
            return module.fail_json(msg="Error parsing module parameters: %s" %
                                    e)

        changed = False
        msg = 'noop'
        rebuilt = False
        differences = DifferenceTracker()
        facts = {}

        if current_service:
            if module.params['state'] == 'absent':
                if not module.check_mode:
                    self.remove_service(module.params['name'])
                msg = 'Service removed'
                changed = True
            else:
                changed, differences, need_rebuild, force_update = new_service.compare(
                    current_service)
                if changed:
                    self.diff_tracker.merge(differences)
                    if need_rebuild:
                        if not module.check_mode:
                            self.remove_service(module.params['name'])
                            self.create_service(module.params['name'],
                                                new_service)
                        msg = 'Service rebuilt'
                        rebuilt = True
                    else:
                        if not module.check_mode:
                            self.update_service(module.params['name'],
                                                current_service, new_service)
                        msg = 'Service updated'
                        rebuilt = False
                else:
                    if force_update:
                        if not module.check_mode:
                            self.update_service(module.params['name'],
                                                current_service, new_service)
                        msg = 'Service forcefully updated'
                        rebuilt = False
                        changed = True
                    else:
                        msg = 'Service unchanged'
                facts = new_service.get_facts()
        else:
            if module.params['state'] == 'absent':
                msg = 'Service absent'
            else:
                if not module.check_mode:
                    service_id = self.create_service(module.params['name'],
                                                     new_service)
                msg = 'Service created'
                changed = True
                facts = new_service.get_facts()

        return msg, changed, rebuilt, differences.get_legacy_docker_diffs(
        ), facts
Exemplo n.º 12
0
 def __init__(self, client):
     self.client = client
     self.diff_tracker = DifferenceTracker()
class DockerNetworkManager(object):
    def __init__(self, client):
        self.client = client
        self.parameters = TaskParameters(client)
        self.check_mode = self.client.check_mode
        self.results = {u'changed': False, u'actions': []}
        self.diff = self.client.module._diff
        self.diff_tracker = DifferenceTracker()
        self.diff_result = dict()

        self.existing_network = self.get_existing_network()

        if not self.parameters.connected and self.existing_network:
            self.parameters.connected = container_names_in_network(
                self.existing_network)

        if (self.parameters.ipam_options['subnet']
                or self.parameters.ipam_options['iprange']
                or self.parameters.ipam_options['gateway']
                or self.parameters.ipam_options['aux_addresses']):
            self.parameters.ipam_config = [self.parameters.ipam_options]

        if self.parameters.driver_options:
            self.parameters.driver_options = get_driver_options(
                self.parameters.driver_options)

        state = self.parameters.state
        if state == 'present':
            self.present()
        elif state == 'absent':
            self.absent()

        if self.diff or self.check_mode or self.parameters.debug:
            if self.diff:
                self.diff_result['before'], self.diff_result[
                    'after'] = self.diff_tracker.get_before_after()
            self.results['diff'] = self.diff_result

    def get_existing_network(self):
        try:
            return self.client.inspect_network(self.parameters.network_name)
        except NotFound:
            return None

    def has_different_config(self, net):
        '''
        Evaluates an existing network and returns a tuple containing a boolean
        indicating if the configuration is different and a list of differences.

        :param net: the inspection output for an existing network
        :return: (bool, list)
        '''
        differences = DifferenceTracker()
        if self.parameters.driver and self.parameters.driver != net['Driver']:
            differences.add('driver',
                            parameter=self.parameters.driver,
                            active=net['Driver'])
        if self.parameters.driver_options:
            if not net.get('Options'):
                differences.add('driver_options',
                                parameter=self.parameters.driver_options,
                                active=net.get('Options'))
            else:
                for key, value in self.parameters.driver_options.items():
                    if not (key
                            in net['Options']) or value != net['Options'][key]:
                        differences.add('driver_options.%s' % key,
                                        parameter=value,
                                        active=net['Options'].get(key))

        if self.parameters.ipam_driver:
            if not net.get('IPAM') or net['IPAM'][
                    'Driver'] != self.parameters.ipam_driver:
                differences.add('ipam_driver',
                                parameter=self.parameters.ipam_driver,
                                active=net.get('IPAM'))

        if self.parameters.ipam_config is not None and self.parameters.ipam_config:
            if not net.get('IPAM') or not net['IPAM']['Config']:
                differences.add('ipam_config',
                                parameter=self.parameters.ipam_config,
                                active=net.get('IPAM', {}).get('Config'))
            else:
                for idx, ipam_config in enumerate(self.parameters.ipam_config):
                    net_config = dict()
                    try:
                        ip_version = get_ip_version(ipam_config['subnet'])
                        for net_ipam_config in net['IPAM']['Config']:
                            if ip_version == get_ip_version(
                                    net_ipam_config['Subnet']):
                                net_config = net_ipam_config
                    except ValueError as e:
                        self.client.fail(str(e))

                    for key, value in ipam_config.items():
                        if value is None:
                            # due to recursive argument_spec, all keys are always present
                            # (but have default value None if not specified)
                            continue
                        camelkey = None
                        for net_key in net_config:
                            if key == net_key.lower():
                                camelkey = net_key
                                break
                        if not camelkey or net_config.get(camelkey) != value:
                            differences.add('ipam_config[%s].%s' % (idx, key),
                                            parameter=value,
                                            active=net_config.get(camelkey)
                                            if camelkey else None)

        if self.parameters.enable_ipv6 is not None and self.parameters.enable_ipv6 != net.get(
                'EnableIPv6', False):
            differences.add('enable_ipv6',
                            parameter=self.parameters.enable_ipv6,
                            active=net.get('EnableIPv6', False))

        if self.parameters.internal is not None:
            if self.parameters.internal:
                if not net.get('Internal'):
                    differences.add('internal',
                                    parameter=self.parameters.internal,
                                    active=net.get('Internal'))
            else:
                if net.get('Internal'):
                    differences.add('internal',
                                    parameter=self.parameters.internal,
                                    active=net.get('Internal'))
        return not differences.empty, differences

    def create_network(self):
        if not self.existing_network:
            params = dict(
                driver=self.parameters.driver,
                options=self.parameters.driver_options,
            )

            ipam_pools = []
            if self.parameters.ipam_config:
                for ipam_pool in self.parameters.ipam_config:
                    if LooseVersion(docker_version) >= LooseVersion('2.0.0'):
                        ipam_pools.append(IPAMPool(**ipam_pool))
                    else:
                        ipam_pools.append(utils.create_ipam_pool(**ipam_pool))

            if self.parameters.ipam_driver or ipam_pools:
                # Only add ipam parameter if a driver was specified or if IPAM parameters
                # were specified. Leaving this parameter away can significantly speed up
                # creation; on my machine creation with this option needs ~15 seconds,
                # and without just a few seconds.
                if LooseVersion(docker_version) >= LooseVersion('2.0.0'):
                    params['ipam'] = IPAMConfig(
                        driver=self.parameters.ipam_driver,
                        pool_configs=ipam_pools)
                else:
                    params['ipam'] = utils.create_ipam_config(
                        driver=self.parameters.ipam_driver,
                        pool_configs=ipam_pools)

            if self.parameters.enable_ipv6 is not None:
                params['enable_ipv6'] = self.parameters.enable_ipv6
            if self.parameters.internal is not None:
                params['internal'] = self.parameters.internal

            if not self.check_mode:
                resp = self.client.create_network(self.parameters.network_name,
                                                  **params)

                self.existing_network = self.client.inspect_network(resp['Id'])
            self.results['actions'].append(
                "Created network %s with driver %s" %
                (self.parameters.network_name, self.parameters.driver))
            self.results['changed'] = True

    def remove_network(self):
        if self.existing_network:
            self.disconnect_all_containers()
            if not self.check_mode:
                self.client.remove_network(self.parameters.network_name)
            self.results['actions'].append("Removed network %s" %
                                           (self.parameters.network_name, ))
            self.results['changed'] = True

    def is_container_connected(self, container_name):
        return container_name in container_names_in_network(
            self.existing_network)

    def connect_containers(self):
        for name in self.parameters.connected:
            if not self.is_container_connected(name):
                if not self.check_mode:
                    self.client.connect_container_to_network(
                        name, self.parameters.network_name)
                self.results['actions'].append("Connected container %s" %
                                               (name, ))
                self.results['changed'] = True
                self.diff_tracker.add('connected.{0}'.format(name),
                                      parameter=True,
                                      active=False)

    def disconnect_missing(self):
        if not self.existing_network:
            return
        containers = self.existing_network['Containers']
        if not containers:
            return
        for c in containers.values():
            name = c['Name']
            if name not in self.parameters.connected:
                self.disconnect_container(name)

    def disconnect_all_containers(self):
        containers = self.client.inspect_network(
            self.parameters.network_name)['Containers']
        if not containers:
            return
        for cont in containers.values():
            self.disconnect_container(cont['Name'])

    def disconnect_container(self, container_name):
        if not self.check_mode:
            self.client.disconnect_container_from_network(
                container_name, self.parameters.network_name)
        self.results['actions'].append("Disconnected container %s" %
                                       (container_name, ))
        self.results['changed'] = True
        self.diff_tracker.add('connected.{0}'.format(container_name),
                              parameter=False,
                              active=True)

    def present(self):
        different = False
        differences = DifferenceTracker()
        if self.existing_network:
            different, differences = self.has_different_config(
                self.existing_network)

        self.diff_tracker.add('exists',
                              parameter=True,
                              active=self.existing_network is not None)
        if self.parameters.force or different:
            self.remove_network()
            self.existing_network = None

        self.create_network()
        self.connect_containers()
        if not self.parameters.appends:
            self.disconnect_missing()

        if self.diff or self.check_mode or self.parameters.debug:
            self.diff_result[
                'differences'] = differences.get_legacy_docker_diffs()
            self.diff_tracker.merge(differences)

        if not self.check_mode and not self.parameters.debug:
            self.results.pop('actions')

        self.results['ansible_facts'] = {
            u'docker_network': self.get_existing_network()
        }

    def absent(self):
        self.diff_tracker.add('exists',
                              parameter=False,
                              active=self.existing_network is not None)
        self.remove_network()