def raw_triage(self, key_string, item, patterns): # process dict values if isinstance(item, AnsibleMapping): return AnsibleMapping( dict((key, self.raw_triage('.'.join([key_string, key]), value, patterns)) for key, value in iteritems(item))) # process list values elif isinstance(item, AnsibleSequence): return AnsibleSequence([ self.raw_triage('.'.join([key_string, str(i)]), value, patterns) for i, value in enumerate(item) ]) # wrap values if they match raw_vars pattern elif isinstance(item, AnsibleUnicode): match = next( (pattern for pattern in patterns if re.match(pattern, key_string)), None) return wrap_var(item) if match else item else: return item
def raw_triage(self, key_string, item, patterns): # process dict values if isinstance(item, AnsibleMapping): return AnsibleMapping( dict((key, self.raw_triage('.'.join([key_string, key]), value, patterns)) for key, value in item.iteritems())) # process list values elif isinstance(item, AnsibleSequence): return AnsibleSequence([ self.raw_triage('.'.join([key_string, str(i)]), value, patterns) for i, value in enumerate(item) ]) # wrap values if they match raw_vars pattern elif isinstance(item, AnsibleUnicode): match = next( (pattern for pattern in patterns if re.match(pattern, key_string)), None) return AnsibleUnicode(''.join( ['{% raw %}', item, '{% endraw %}'])) if not item.startswith( ('{% raw', '{%raw')) and match else item
def construct_yaml_seq(self, node): data = AnsibleSequence() yield data data.extend(self.construct_sequence(node)) data.ansible_pos = self._node_position_info(node)
def parse(self, inventory, device_id_map, published_port_map): if type(self.options) != AnsibleMapping: raise AnsibleParserError( f'value must be dict type in service {self.name}') inventory.add_host(self.name, group='services') for option_name, option_value in self.options.items(): if option_name not in SERVICE_PARAMS: raise AnsibleParserError( f'unknown parameter {option_name} is specified in service {self.name}' ) if option_name in SERVICE_PARAMS_COPY: inventory.set_variable(self.name, f'hive_{option_name}', option_value) if 'available_on' in self.options: self.available_on = self.options['available_on'] for s in self.available_on: if s not in STAGES: raise AnsibleParserError( f'"element of "available_on" of service {self.name} must be one of {STAGES}, but {s}' ) for s in self.available_on: inventory.add_host(self.name, group=s) if 'volumes' in self.options or self.options.get('standalone', False): volumes_value = self.options.get('volumes', AnsibleSequence([])) if type(volumes_value) != AnsibleSequence: raise AnsibleParserError( f'"volumes" must be list type in service {self.name}, but {type(volumes_value)}' ) volumes = [] if self.options.get('standalone', False): volumes = [{ 'source': '/sys/fs/cgroup', 'target': '/sys/fs/cgroup', 'readonly': True }, { 'source': '', 'target': '/run', 'type': 'tmpfs' }, { 'source': '', 'target': '/tmp', 'type': 'tmpfs' }] for volume in volumes_value: if type(volume) == AnsibleUnicode: raise AnsibleParserError( f'we do not support short syntax {text_type(volume)} in volume at service {self.name}' ) elif type(volume) == AnsibleMapping: if 'source' not in volume or 'target' not in volume: raise AnsibleParserError( f'both "source" and "target" must be specified in volume at service {self.name}' ) if 'nfs' in volume: if 'driver' in volume: raise AnsibleParserError( f'both "driver" and "nfs" can not be specified in volume at service {self.name}' ) if 'drbd' in volume: raise AnsibleParserError( f'both "nfs" and "drbd" can not be specified in volume at service {self.name}' ) self.add_volume( inventory, { 'name': volume['source'], 'nfs': volume['nfs'] }, 'nfs_volumes', volume['nfs'].get('available_on', self.available_on)) if 'driver' in volume: # we prepare volume at build-volume phase prepared_volume = { 'name': volume['source'], 'driver': volume['driver'] } if 'driver_opts' in volume: prepared_volume['dirver_options'] = volume[ 'driver_opts'] self.add_volume(inventory, prepared_volume, 'volumes', self.available_on) if 'drbd' in volume: if 'driver' in volume: raise AnsibleParserError( f'both "driver" and "drbd" can not be specified in volume at service {self.name}' ) if 'device_id' in volume['drbd']: if volume['drbd'][ 'device_id'] in device_id_map.values( ) and volume['drbd'][ 'device_id'] != device_id_map.get( volume['source']): raise AnsibleParserError( f'duplicate deviceid {volume["drbd"]["device_id"]} of volume {volume["source"]} in service {self.name}' ) else: if volume['source'] in device_id_map: volume['drbd']['device_id'] = device_id_map[ volume['source']] else: # assign new device id for device_id in range(1, 256): if device_id not in device_id_map.values(): volume['drbd']['device_id'] = device_id break if 'device_id' not in volume['drbd']: raise AnsibleParserError( f'too many drbd volumes or garbage device_id are remain in "device_id_map" of persistent hive variable' ) device_id_map[text_type( volume['source'])] = volume['drbd']['device_id'] self.add_volume( inventory, { 'name': volume['source'], 'drbd': volume['drbd'] }, 'drbd_volumes', volume['drbd'].get('available_on', self.available_on)) volume_value = {} for k, v in volume.items(): # TODO: support properties of mounts property of docker_swarm_service module # I do not know what driver_config option means, it does check for volume definition? or it does create volume ondemand? if k not in VOLUME_PARAMS + VOLUME_PARAMS_DEF: raise AnsibleParserError( f'unknown parameter {k} is specified in volume in service {self.name}' ) if k in VOLUME_PARAMS: volume_value[k] = v volumes.append(volume_value) else: raise AnsibleParserError( f'all element of "volumes" must be dict type or str type in service {self.name}' ) inventory.set_variable(self.name, 'hive_volumes', volumes) if 'image' in self.options: image_value = self.options['image'] if type(image_value) == AnsibleUnicode: # self.options['image'] can be {{hive_registry}}/image_some_another_service to refer build image inventory.set_variable(self.name, 'hive_image', image_value) elif type(image_value) == AnsibleMapping: # this container needs build. image_name = f'image_{self.name}' inventory.add_host(image_name, group='images') for s in self.available_on: inventory.add_host(image_name, group=s) # when hive_image_name is set, means image = {{hive_registry}}:{{hive_image_name}} inventory.set_variable(self.name, 'hive_image_name', image_name) if 'from' not in image_value: raise AnsibleParserError( f'"from" must be specified in "image" at service {self.name}' ) # just copy paste parameter for build image if self.options.get('standalone', False): inventory.set_variable(image_name, f'hive_standalone', True) inventory.set_variable(image_name, f'hive_privileged', True) if 'pull_from' in image_value and 'pull_on' not in image_value: raise AnsibleParserError( f'pull_from must be specified in "image" at service {self.name} when pull_on is specified' ) for option_name, option_value in image_value.items(): if option_name not in IMAGE_PARAMS: raise AnsibleParserError( f'unknown parameter {option_name} is specified in "image" at service {self.name}' ) inventory.set_variable(image_name, f'hive_{option_name}', option_value) else: raise AnsibleParserError( f'"image" must be dict type or str type in service {self.name}, but type is {type(image_value)}' ) if 'ports' in self.options: ports = [] for portdef in self.options['ports']: if type(portdef) == AnsibleMapping: pass elif type(portdef) == AnsibleUnicode: slash = portdef.split('/') protocol = 'tcp' if len(slash) > 1: protocol = slash[1] colon = slash[0].split(':') if len(colon) > 1: portdef = { 'published_port': int(colon[0]), 'target_port': int(colon[1]), 'protocol': protocol } else: portdef = { 'target_port': int(colon[0]), 'protocol': protocol } else: raise AnsibleParserError( f'element of "ports" must be dict type or str type in service {self.name}, but type is {type(portdef)}' ) if portdef['protocol'] not in ['tcp', 'udp', 'sctp']: raise AnsibleParserError( f'unknown protocol {portdef["protocol"]} is specified {self.name}.' ) published_port_key = self.name + text_type( portdef['target_port']) if 'published_port' in portdef: if portdef['published_port'] in published_port_map.values( ) and portdef['published_port'] != published_port_map.get( published_port_key): display.warning( f'duplicate port number {portdef["published_port"]} of port {published_port_key}' ) else: if published_port_key in published_port_map: portdef['published_port'] = published_port_map[ published_port_key] else: # assign new port number for port_number in range(61001, 65535): if port_number not in published_port_map.values(): portdef['published_port'] = port_number break if 'published_port' not in portdef: raise AnsibleParserError( f'too many published ports or garbage port number are remain in "published_port_map" of persistent hive variable' ) published_port_map[published_port_key] = portdef[ 'published_port'] ports.append(portdef) inventory.set_variable(self.name, f'hive_ports', ports)