From: Dan Mick Date: Sat, 15 Jul 2023 01:07:32 +0000 (-0700) Subject: examples/builder.yml, library/jenkins_node: support node update X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=839543920c3a1854ba238cc2ec3dad4e7a0ac184;p=ceph-build.git examples/builder.yml, library/jenkins_node: support node update Original implementation would only permit nodes to be created if they did not exist. Add support to update certain attributes of the node with a run against extant nodes. Only permit a few attribute updates. This is primarily motivated by the desire to support maintaining node labels with ansible. Signed-off-by: Dan Mick --- diff --git a/ansible/examples/builder.yml b/ansible/examples/builder.yml index 74ecc0d3..251d0375 100644 --- a/ansible/examples/builder.yml +++ b/ansible/examples/builder.yml @@ -119,6 +119,7 @@ - python3-pip - python3-venv - python3-virtualenv + - python3-xmltodict when: - ansible_os_family == "Debian" - ansible_distribution_major_version|int >= 18 @@ -209,6 +210,7 @@ - python3-pip - python3-devel - python3-virtualenv + - python3-xmltodict - mock - podman container_service_name: podman @@ -233,6 +235,7 @@ - jq - python3-pip - python3-devel + - python3-xmltodict - podman container_service_name: podman container_certs_path: "/etc/containers/certs.d/{{ container_mirror }}" @@ -288,6 +291,7 @@ - python2-virtualenv - python3-pip - python3-virtualenv + - python3-xmltodict - rpm-build - rpmdevtools - tig @@ -904,7 +908,6 @@ remoteFS: '/home/{{ jenkins_user }}/build' executors: '{{ executors|default(1) }}' exclusive: true - tags: register - name: Download agent.jar get_url: diff --git a/ansible/library/jenkins_node b/ansible/library/jenkins_node index e4d4ba4f..792a027c 100644 --- a/ansible/library/jenkins_node +++ b/ansible/library/jenkins_node @@ -100,6 +100,8 @@ EXAMPLES = """ jenkins_node: uri={{ jenkins_uri }} username={{ user }} password={{ password }} name={{ node_name }} operation=delete """ +import ast +import xmltodict HAS_JENKINS_API = True try: @@ -124,7 +126,49 @@ def translate_params(params): return sanitized -def create(uri, user, password, name, **kw): + +# +# it's not clear to me how ansible passes lists as lists, +# so convert them if necessary +# +def maybe_convert_string_to_list(v): + if isinstance(v, basestring): + try: + v = ast.literal_eval(v) + except Exception: + # no, really; ast makes a best effort, and if it fails, + # we didn't need its conversion + pass + return v + +def sanitize_update_params(kw): + + def translate_labels(labels): + return 'label', ' '.join(labels) + + # this list may be smaller than it needs to be, but these are + # the only ones I want to support for now + VALID_UPDATE_PARAMS = { + # value, if any, is function returning new key and value to use + 'name': None, + 'remoteFS': None, + 'numExecutors': None, + 'labels': translate_labels, + } + update_kws = dict() + invalid = list() + for k, v in kw.items(): + v = maybe_convert_string_to_list(v) + if k not in VALID_UPDATE_PARAMS: + invalid.append(k) + else: + if VALID_UPDATE_PARAMS[k]: + k, v = VALID_UPDATE_PARAMS[k](v) + update_kws[k] = v + return invalid, update_kws + + +def create_or_modify(uri, user, password, name, **kw): launcher_params = {} launcher_params['credentialsId'] = kw.pop('credentialsId', None) launcher_params['host'] = kw.pop('host', None) @@ -132,11 +176,24 @@ def create(uri, user, password, name, **kw): launcher_params = {} params = translate_params(kw) j = _jenkins(uri, user, password) + if j.node_exists(name): - return False, "Failed to create node '%s' - already exists." % name - j.create_node(name, launcher_params=launcher_params, **params) - if not j.node_exists(name): - return False, "Failed to create node '%s'." % name + # if it already exists, we can reconfigure it + + # select valid config keys, transform a few + invalid, params = sanitize_update_params(params) + + config = xmltodict.parse(j.get_node_config(name)) + for k, v in params.items(): + config['slave'][k] = v + new_xconfig = xmltodict.unparse(config) + + j.reconfig_node(name, new_xconfig) + else: + j.create_node(name, launcher_params=launcher_params, **params) + if not j.node_exists(name): + return False, "Failed to create node '%s'." % name + return True, None @@ -204,7 +261,7 @@ def main(): launcher = module.params.get('launcher', 'hudson.plugins.sshslaves.SSHLauncher') api_calls = { - 'create': create, + 'create': create_or_modify, 'delete': delete, 'enable': enable, 'disable': disable