From 678fd437301c715b662aaf3035c87f940f8501b5 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 20 Mar 2026 12:37:59 +0200 Subject: task 300: persist effective service mode --- snippets/hyperstack/hyperstack.rb | 69 ++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 9 deletions(-) (limited to 'snippets/hyperstack/hyperstack.rb') diff --git a/snippets/hyperstack/hyperstack.rb b/snippets/hyperstack/hyperstack.rb index bcbae69..c164f6e 100755 --- a/snippets/hyperstack/hyperstack.rb +++ b/snippets/hyperstack/hyperstack.rb @@ -518,7 +518,7 @@ module HyperstackVM expand_path(fetch('wireguard', 'setup_script')) end - def desired_security_rules + def desired_security_rules(include_ollama: ollama_install_enabled?, include_vllm: vllm_install_enabled?) rules = [] allowed_ssh_cidrs.each do |cidr| @@ -529,8 +529,8 @@ module HyperstackVM rules << firewall_rule('udp', wireguard_udp_port, cidr) end - rules << firewall_rule('tcp', ollama_port, wireguard_subnet) - rules << firewall_rule('tcp', litellm_port, wireguard_subnet) + rules << firewall_rule('tcp', ollama_port, wireguard_subnet) if include_ollama || include_vllm + rules << firewall_rule('tcp', litellm_port, wireguard_subnet) if include_vllm rules.uniq end @@ -1127,6 +1127,7 @@ module HyperstackVM 'public_ip' => instance['floating_ip'], 'created_at' => Time.now.utc.iso8601 } + sync_service_mode_state(state) @state_store.save(state) continue_create(state) end @@ -1167,14 +1168,17 @@ module HyperstackVM else begin vm = @client.get_vm(state['vm_id']) - desired = @config.desired_security_rules.map { |rule| normalize_rule(rule) } + desired = desired_security_rules_for_state(state).map { |rule| normalize_rule(rule) } current = Array(vm['security_rules']).map { |rule| normalize_rule(rule) } missing_rules = desired - current + vllm_enabled = state_vllm_enabled?(state) + ollama_enabled = state_ollama_enabled?(state) info "Tracked VM: #{state['vm_id']} #{vm['name']}" info "Status: #{vm['status']} / #{vm['vm_state']}" info "Public IP: #{connect_host_for(vm) || 'none'}" - info "Active model: #{state['vllm_model'] || @config.vllm_model}" + info "Service mode: #{service_mode_summary(vllm_enabled: vllm_enabled, ollama_enabled: ollama_enabled)}" + info "Active model: #{state['vllm_model'] || @config.vllm_model}" if vllm_enabled info "Missing firewall rules: #{missing_rules.empty? ? 'none' : missing_rules.size}" rescue Error => e warn "Unable to load VM #{state['vm_id']}: #{e.message}" @@ -1260,6 +1264,9 @@ module HyperstackVM state['vllm_container_name'] = new_container state['vllm_preset'] = preset_name state['vllm_setup_at'] = Time.now.utc.iso8601 + state['services'] ||= {} + state['services']['vllm_enabled'] = true + state['services']['ollama_enabled'] = state_ollama_enabled?(state) @state_store.save(state) info "Model switched to '#{preset_name}' (#{preset['model']})." @@ -1273,14 +1280,16 @@ module HyperstackVM raise Error, "No tracked VM state file found at #{@state_store.path}." if state.nil? wg_ip = @config.wireguard_gateway_hostname + vllm_enabled = state_vllm_enabled?(state) + ollama_enabled = state_ollama_enabled?(state) info "Running end-to-end inference tests via WireGuard (#{wg_ip})..." - if @config.vllm_install_enabled? + if vllm_enabled test_vllm(wg_ip) test_litellm(wg_ip) end - info " Ollama test: connect via SSH and run 'ollama list' to verify models." if @config.ollama_install_enabled? + info " Ollama test: connect via SSH and run 'ollama list' to verify models." if ollama_enabled info 'All inference tests passed.' end @@ -1298,6 +1307,7 @@ module HyperstackVM def continue_create(state) vm_id = state['vm_id'] + sync_service_mode_state(state) vm = wait_for_vm_ready(vm_id) ensure_security_rules(vm) @@ -1384,7 +1394,7 @@ module HyperstackVM 'assign_floating_ip' => @config.assign_floating_ip?, 'create_bootable_volume' => @config.create_bootable_volume?, 'enable_port_randomization' => @config.enable_port_randomization?, - 'security_rules' => @config.desired_security_rules + 'security_rules' => desired_security_rules } payload['labels'] = @config.labels unless @config.labels.empty? payload['user_data'] = @config.user_data if @config.user_data @@ -1465,7 +1475,7 @@ module HyperstackVM def ensure_security_rules(vm) existing = Array(vm['security_rules']).map { |rule| normalize_rule(rule) } - desired = @config.desired_security_rules.map { |rule| normalize_rule(rule) } + desired = desired_security_rules.map { |rule| normalize_rule(rule) } (desired - existing).each do |rule| info "Adding Hyperstack firewall rule #{rule['protocol']} #{rule['remote_ip_prefix']} #{rule['port_range_min']}..." @@ -1751,6 +1761,47 @@ module HyperstackVM } end + def sync_service_mode_state(state) + state['services'] = { + 'vllm_enabled' => effective_vllm?, + 'ollama_enabled' => effective_ollama? + } + end + + def desired_security_rules(include_vllm: effective_vllm?, include_ollama: effective_ollama?) + @config.desired_security_rules(include_vllm: include_vllm, include_ollama: include_ollama) + end + + def desired_security_rules_for_state(state) + desired_security_rules(include_vllm: state_vllm_enabled?(state), include_ollama: state_ollama_enabled?(state)) + end + + def state_vllm_enabled?(state) + recorded = state&.dig('services', 'vllm_enabled') + return recorded unless recorded.nil? + + return true if state&.key?('vllm_setup_at') + + @config.vllm_install_enabled? + end + + def state_ollama_enabled?(state) + recorded = state&.dig('services', 'ollama_enabled') + return recorded unless recorded.nil? + + return true if state&.key?('ollama_installed_at') || state&.key?('ollama_setup_at') + + @config.ollama_install_enabled? + end + + def service_mode_summary(vllm_enabled:, ollama_enabled:) + return 'vLLM+LiteLLM enabled, Ollama enabled' if vllm_enabled && ollama_enabled + return 'vLLM+LiteLLM enabled, Ollama disabled' if vllm_enabled + return 'Ollama enabled, vLLM+LiteLLM disabled' if ollama_enabled + + 'All inference services disabled' + end + def cleanup_local_access(dry_run:, hostnames:, allowed_ips:) { peers: @local_wireguard.remove_peers_by_allowed_ips(allowed_ips, dry_run: dry_run), -- cgit v1.2.3