diff --git a/input/telemetry_config.yml b/input/telemetry_config.yml index bfc2980c0e..765b227786 100644 --- a/input/telemetry_config.yml +++ b/input/telemetry_config.yml @@ -424,7 +424,7 @@ powerscale_configurations: # Path to the CSM Observability (Karavi Observability) values.yaml file # Required when powerscale_configurations.powerscale_telemetry_support: true - # Reference: https://raw.githubusercontent.com/dell/helm-charts/refs/heads/release-v1.16.3/charts/karavi-observability/values.yaml + # Reference: https://raw.githubusercontent.com/dell/helm-charts/refs/heads/release-v1.17.1/charts/karavi-observability/values.yaml csm_observability_values_file_path: "" # -------------------------------------------------------------------------- diff --git a/input_validation/validate_config.yml b/input_validation/validate_config.yml index dc9dfa3913..f3d5469f8a 100644 --- a/input_validation/validate_config.yml +++ b/input_validation/validate_config.yml @@ -50,6 +50,11 @@ tags: - always tasks: + - name: Enable subscription check when validate_config.yml is run directly + ansible.builtin.set_fact: + run_subscription_check: true + when: run_subscription_check is not defined and omnia_run_tags is not defined + - name: Run subscription validation tasks when: "'local_repo' in (omnia_run_tags | default(ansible_run_tags | default([]) | list)) or 'all' in (ansible_run_tags | default([]) | list)" block: @@ -94,7 +99,7 @@ ansible.builtin.include_role: name: validate_subscription tasks_from: check_rhel_subscription.yml - when: "'local_repo' in (hostvars['localhost']['omnia_run_tags'] | default([]))" + when: "hostvars['localhost']['run_subscription_check'] | default(false) | bool" - name: Configure RHEL repository URLs hosts: localhost @@ -107,7 +112,7 @@ ansible.builtin.include_role: name: validate_subscription tasks_from: configure_rhel_os_urls.yml - when: "'local_repo' in (omnia_run_tags | default([]))" + when: "run_subscription_check | default(false) | bool" - name: Validate omnia input config hosts: localhost diff --git a/local_repo/local_repo.yml b/local_repo/local_repo.yml index d4bb1d488d..e6fea817a0 100644 --- a/local_repo/local_repo.yml +++ b/local_repo/local_repo.yml @@ -29,6 +29,10 @@ omnia_run_tags: "{{ (ansible_run_tags | default([]) | list + ['local_repo']) | unique }}" cacheable: true + - name: Enable subscription check for local_repo + ansible.builtin.set_fact: + run_subscription_check: true + - name: Include metadata vars ansible.builtin.include_vars: "/opt/omnia/.data/oim_metadata.yml" register: include_metadata diff --git a/upgrade/roles/upgrade_telemetry/tasks/apply_victoria_crs.yml b/upgrade/roles/upgrade_telemetry/tasks/apply_victoria_crs.yml index addac543ab..1e8b7aa3f0 100644 --- a/upgrade/roles/upgrade_telemetry/tasks/apply_victoria_crs.yml +++ b/upgrade/roles/upgrade_telemetry/tasks/apply_victoria_crs.yml @@ -55,38 +55,11 @@ # and the operator creates new ones. To preserve IPs, we inject loadBalancerIP # directly into the VMCluster CR's serviceSpec BEFORE applying, so the operator # creates services with the correct IPs from the start (no race condition). -- name: Create LoadBalancer IP injection script - ansible.builtin.copy: - dest: /tmp/inject_vm_lb_ips.py - mode: "0755" - content: | - #!/usr/bin/env python3 - import yaml - import sys - manifest_path = sys.argv[1] - vmselect_ip = sys.argv[2] if len(sys.argv) > 2 and sys.argv[2] else "" - vminsert_ip = sys.argv[3] if len(sys.argv) > 3 and sys.argv[3] else "" - with open(manifest_path) as f: - doc = yaml.safe_load(f) - spec = doc.get("spec", {}) - changed = False - if vmselect_ip and "vmselect" in spec: - svc = spec["vmselect"].setdefault("serviceSpec", {}).setdefault("spec", {}) - if svc.get("loadBalancerIP") != vmselect_ip: - svc["loadBalancerIP"] = vmselect_ip - changed = True - if vminsert_ip and "vminsert" in spec: - svc = spec["vminsert"].setdefault("serviceSpec", {}).setdefault("spec", {}) - if svc.get("loadBalancerIP") != vminsert_ip: - svc["loadBalancerIP"] = vminsert_ip - changed = True - if changed: - with open(manifest_path, "w") as f: - yaml.dump(doc, f, default_flow_style=False, sort_keys=False) - print("Injected vmselect=" + vmselect_ip + " vminsert=" + vminsert_ip) - else: - print("IPs already present - no change needed") - sys.exit(0 if changed else 2) +- name: Stage LoadBalancer IP injection script + ansible.builtin.template: + src: inject_vm_lb_ips.py.j2 + dest: "{{ ip_inject_script_path }}" + mode: "{{ executable_mode }}" delegate_to: "{{ kube_vip }}" connection: ssh when: @@ -95,7 +68,7 @@ - name: Inject preserved LoadBalancer IPs into VMCluster manifest ansible.builtin.command: cmd: >- - python3 /tmp/inject_vm_lb_ips.py + python3 {{ ip_inject_script_path }} "{{ telemetry_deploy_dir }}/deployments/victoria-operator-vmcluster.yaml" "{{ preserved_vmselect_ip | default('') }}" "{{ preserved_vminsert_ip | default('') }}" @@ -109,7 +82,7 @@ - name: Clean up LoadBalancer IP injection script ansible.builtin.file: - path: /tmp/inject_vm_lb_ips.py + path: "{{ ip_inject_script_path }}" state: absent delegate_to: "{{ kube_vip }}" connection: ssh @@ -148,7 +121,9 @@ kubectl -n {{ telemetry_namespace }} get svc vminsert-{{ new_vmcluster_name }} \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "" register: vminsert_lb_ip - until: vminsert_lb_ip.stdout | trim | length > 0 + until: > + (vminsert_lb_ip is defined) and + ((vminsert_lb_ip.stdout | default('') | trim | length) > 0) retries: "{{ lb_ip_wait_retries }}" delay: "{{ lb_ip_wait_delay }}" changed_when: false @@ -161,7 +136,9 @@ kubectl -n {{ telemetry_namespace }} get svc vmselect-{{ new_vmcluster_name }} \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "" register: vmselect_lb_ip - until: vmselect_lb_ip.stdout | trim | length > 0 + until: > + (vmselect_lb_ip is defined) and + ((vmselect_lb_ip.stdout | default('') | trim | length) > 0) retries: "{{ lb_ip_wait_retries }}" delay: "{{ lb_ip_wait_delay }}" changed_when: false @@ -218,7 +195,9 @@ kubectl -n {{ telemetry_namespace }} get svc vminsert-{{ new_vmcluster_name }} \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "" register: vminsert_lb_ip - until: vminsert_lb_ip.stdout | trim | length > 0 + until: > + (vminsert_lb_ip is defined) and + ((vminsert_lb_ip.stdout | default('') | trim | length) > 0) retries: "{{ lb_ip_wait_retries }}" delay: "{{ lb_ip_wait_delay }}" changed_when: false @@ -231,7 +210,9 @@ kubectl -n {{ telemetry_namespace }} get svc vmselect-{{ new_vmcluster_name }} \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "" register: vmselect_lb_ip - until: vmselect_lb_ip.stdout | trim | length > 0 + until: > + (vmselect_lb_ip is defined) and + ((vmselect_lb_ip.stdout | default('') | trim | length) > 0) retries: "{{ lb_ip_wait_retries }}" delay: "{{ lb_ip_wait_delay }}" changed_when: false @@ -247,8 +228,8 @@ ansible.builtin.debug: msg: "{{ victoria_lb_ip_reclaim_failed }}" when: >- - (vminsert_lb_ip is defined and vminsert_lb_ip.stdout is defined and vminsert_lb_ip.stdout | trim | length == 0) or - (vmselect_lb_ip is defined and vmselect_lb_ip.stdout is defined and vmselect_lb_ip.stdout | trim | length == 0) + (vminsert_lb_ip is defined and vminsert_lb_ip.stdout | default('') | trim | length == 0) or + (vmselect_lb_ip is defined and vmselect_lb_ip.stdout | default('') | trim | length == 0) # ── Apply scrape and agent CRs ── - name: Check for VMScrape manifest diff --git a/upgrade/roles/upgrade_telemetry/templates/inject_vm_lb_ips.py.j2 b/upgrade/roles/upgrade_telemetry/templates/inject_vm_lb_ips.py.j2 new file mode 100644 index 0000000000..21e390ae29 --- /dev/null +++ b/upgrade/roles/upgrade_telemetry/templates/inject_vm_lb_ips.py.j2 @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +# Copyright 2026 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Find services in the telemetry namespace that are holding LoadBalancer IPs +# which should belong to VMCluster services (vminsert/vmselect). +# This can happen when MetalLB reassigns freed IPs to other services +# before the VMCluster services are created by the operator. +# +# Usage: bash find_ip_conflict_svcs.sh +# Output: One service name per line (services holding conflicting IPs) + +import yaml +import sys +manifest_path = sys.argv[1] +vmselect_ip = sys.argv[2] if len(sys.argv) > 2 and sys.argv[2] else "" +vminsert_ip = sys.argv[3] if len(sys.argv) > 3 and sys.argv[3] else "" +with open(manifest_path) as f: + doc = yaml.safe_load(f) +spec = doc.get("spec", {}) +changed = False +if vmselect_ip and "vmselect" in spec: + svc = spec["vmselect"].setdefault("serviceSpec", {}).setdefault("spec", {}) + if svc.get("loadBalancerIP") != vmselect_ip: + svc["loadBalancerIP"] = vmselect_ip + changed = True +if vminsert_ip and "vminsert" in spec: + svc = spec["vminsert"].setdefault("serviceSpec", {}).setdefault("spec", {}) + if svc.get("loadBalancerIP") != vminsert_ip: + svc["loadBalancerIP"] = vminsert_ip + changed = True +if changed: + with open(manifest_path, "w") as f: + yaml.dump(doc, f, default_flow_style=False, sort_keys=False) + print("Injected vmselect=" + vmselect_ip + " vminsert=" + vminsert_ip) +else: + print("IPs already present - no change needed") +sys.exit(0 if changed else 2) diff --git a/upgrade/roles/upgrade_telemetry/vars/main.yml b/upgrade/roles/upgrade_telemetry/vars/main.yml index 5d51a1a057..b726a4be10 100644 --- a/upgrade/roles/upgrade_telemetry/vars/main.yml +++ b/upgrade/roles/upgrade_telemetry/vars/main.yml @@ -49,6 +49,7 @@ idrac_rollout_delay: 30 lb_ip_wait_retries: 30 lb_ip_wait_delay: 5 ip_conflict_script_path: /tmp/find_ip_conflict_svcs.sh +ip_inject_script_path: /tmp/inject_vm_lb_ips.py # Victoria operator configuration # victoria_operator_pkg is loaded dynamically from service_k8s JSON in include_required_input.yml