ConfigLMM 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +70 -0
- data/CNAME +1 -0
- data/Examples/.lmm.state.yaml +159 -0
- data/Examples/ConfigLMM.mm.yaml +32 -0
- data/Examples/Implemented.mm.yaml +252 -4
- data/Examples/SmallBusiness.mm.yaml +492 -0
- data/Plugins/Apps/Answer/answer.lmm.rb +165 -0
- data/Plugins/Apps/Answer/answer@.service +40 -0
- data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.conf.erb +0 -3
- data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.lmm.rb +0 -1
- data/Plugins/Apps/Authentik/Authentik-ProxyOutpost.container +20 -0
- data/Plugins/Apps/Authentik/Authentik-Server.container +7 -1
- data/Plugins/Apps/Authentik/Authentik-Worker.container +7 -1
- data/Plugins/Apps/Authentik/Authentik.conf.erb +18 -6
- data/Plugins/Apps/Authentik/Authentik.lmm.rb +232 -45
- data/Plugins/Apps/BookStack/BookStack.conf.erb +38 -0
- data/Plugins/Apps/BookStack/BookStack.container +20 -0
- data/Plugins/Apps/BookStack/BookStack.lmm.rb +91 -0
- data/Plugins/Apps/Cassandra/Cassandra.lmm.rb +9 -19
- data/Plugins/Apps/ClickHouse/ClickHouse.container +28 -0
- data/Plugins/Apps/ClickHouse/ClickHouse.lmm.rb +113 -0
- data/Plugins/Apps/ClickHouse/Config/listen.yaml +2 -0
- data/Plugins/Apps/ClickHouse/Config/logger.yaml +8 -0
- data/Plugins/Apps/ClickHouse/Config/zookeepers.yaml +5 -0
- data/Plugins/Apps/ClickHouse/Connection.rb +96 -0
- data/Plugins/Apps/Discourse/Discourse-Sidekiq.container +22 -0
- data/Plugins/Apps/Discourse/Discourse.conf.erb +38 -0
- data/Plugins/Apps/Discourse/Discourse.container +21 -0
- data/Plugins/Apps/Discourse/Discourse.lmm.rb +156 -0
- data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +87 -52
- data/Plugins/Apps/ERPNext/ERPNext-Frontend.container +24 -0
- data/Plugins/Apps/ERPNext/ERPNext-Queue.container +22 -0
- data/Plugins/Apps/ERPNext/ERPNext-Scheduler.container +22 -0
- data/Plugins/Apps/ERPNext/ERPNext-Websocket.container +24 -0
- data/Plugins/Apps/ERPNext/ERPNext.container +23 -0
- data/Plugins/Apps/ERPNext/ERPNext.lmm.rb +204 -0
- data/Plugins/Apps/ERPNext/ERPNext.network +12 -0
- data/Plugins/Apps/ERPNext/sites/apps.json +10 -0
- data/Plugins/Apps/ERPNext/sites/apps.txt +3 -0
- data/Plugins/Apps/ERPNext/sites/common_site_config.json +11 -0
- data/Plugins/Apps/GitLab/GitLab.container +9 -2
- data/Plugins/Apps/GitLab/GitLab.lmm.rb +52 -33
- data/Plugins/Apps/Homepage/Homepage.conf.erb +86 -0
- data/Plugins/Apps/Homepage/Homepage.container +19 -0
- data/Plugins/Apps/Homepage/Homepage.lmm.rb +54 -0
- data/Plugins/Apps/IPFS/IPFS.conf.erb +0 -3
- data/Plugins/Apps/IPFS/IPFS.lmm.rb +0 -1
- data/Plugins/Apps/InfluxDB/InfluxDB.conf.erb +0 -3
- data/Plugins/Apps/InfluxDB/InfluxDB.lmm.rb +0 -1
- data/Plugins/Apps/Jackett/Jackett.conf.erb +0 -3
- data/Plugins/Apps/Jackett/Jackett.lmm.rb +0 -1
- data/Plugins/Apps/Jellyfin/Jellyfin.conf.erb +0 -3
- data/Plugins/Apps/Jellyfin/Jellyfin.lmm.rb +0 -1
- data/Plugins/Apps/LetsEncrypt/LetsEncrypt.lmm.rb +78 -0
- data/Plugins/Apps/LetsEncrypt/hooks/dovecot.sh +2 -0
- data/Plugins/Apps/LetsEncrypt/hooks/nginx.sh +2 -0
- data/Plugins/Apps/LetsEncrypt/hooks/postfix.sh +2 -0
- data/Plugins/Apps/LetsEncrypt/renew-certificates.service +7 -0
- data/Plugins/Apps/LetsEncrypt/renew-certificates.timer +12 -0
- data/Plugins/Apps/LetsEncrypt/rfc2136.ini +11 -0
- data/Plugins/Apps/LibreTranslate/LibreTranslate.container +21 -0
- data/Plugins/Apps/LibreTranslate/LibreTranslate.lmm.rb +34 -0
- data/Plugins/Apps/Lobsters/Containerfile +81 -0
- data/Plugins/Apps/Lobsters/Lobsters-Tasks.container +26 -0
- data/Plugins/Apps/Lobsters/Lobsters.conf.erb +99 -0
- data/Plugins/Apps/Lobsters/Lobsters.container +27 -0
- data/Plugins/Apps/Lobsters/Lobsters.lmm.rb +196 -0
- data/Plugins/Apps/Lobsters/crontab +3 -0
- data/Plugins/Apps/Lobsters/database.yml +26 -0
- data/Plugins/Apps/Lobsters/entrypoint.sh +30 -0
- data/Plugins/Apps/Lobsters/generateCredentials.rb +19 -0
- data/Plugins/Apps/Lobsters/lobsters-cron.sh +25 -0
- data/Plugins/Apps/Lobsters/lobsters-daily.sh +23 -0
- data/Plugins/Apps/Lobsters/puma.rb +49 -0
- data/Plugins/Apps/MariaDB/Connection.rb +55 -0
- data/Plugins/Apps/MariaDB/MariaDB.lmm.rb +122 -0
- data/Plugins/Apps/Mastodon/Mastodon-Sidekiq.container +22 -0
- data/Plugins/Apps/Mastodon/Mastodon-Streaming.container +20 -0
- data/Plugins/Apps/Mastodon/Mastodon.conf.erb +34 -45
- data/Plugins/Apps/Mastodon/Mastodon.container +28 -0
- data/Plugins/Apps/Mastodon/Mastodon.lmm.rb +240 -5
- data/Plugins/Apps/Mastodon/configlmm.rake +30 -0
- data/Plugins/Apps/Mastodon/entrypoint.sh +16 -0
- data/Plugins/Apps/Matrix/Element.container +19 -0
- data/Plugins/Apps/Matrix/Matrix.conf.erb +47 -9
- data/Plugins/Apps/Matrix/Matrix.lmm.rb +119 -5
- data/Plugins/Apps/Matrix/Synapse.container +22 -0
- data/Plugins/Apps/Matrix/config.json +50 -0
- data/Plugins/Apps/Matrix/homeserver.yaml +70 -0
- data/Plugins/Apps/Matrix/log.config +30 -0
- data/Plugins/Apps/Netdata/Netdata.conf.erb +0 -3
- data/Plugins/Apps/Netdata/Netdata.lmm.rb +0 -1
- data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +3 -4
- data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +155 -48
- data/Plugins/Apps/Nextcloud/autoconfig.php +13 -0
- data/Plugins/Apps/Nextcloud/config.php +10 -1
- data/Plugins/Apps/Nextcloud/nextcloudcron.service +8 -0
- data/Plugins/Apps/Nextcloud/nextcloudcron.timer +10 -0
- data/Plugins/Apps/Nginx/Connection.rb +93 -0
- data/Plugins/Apps/Nginx/conf.d/configlmm.conf +54 -4
- data/Plugins/Apps/Nginx/conf.d/languages.conf +21 -0
- data/Plugins/Apps/Nginx/config-lmm/errors.conf +33 -22
- data/Plugins/Apps/Nginx/config-lmm/gateway-errors.conf +20 -0
- data/Plugins/Apps/Nginx/config-lmm/proxy.conf +6 -2
- data/Plugins/Apps/Nginx/main.conf.erb +7 -3
- data/Plugins/Apps/Nginx/nginx.conf +2 -2
- data/Plugins/Apps/Nginx/nginx.lmm.rb +103 -81
- data/Plugins/Apps/Nginx/proxy.conf.erb +24 -6
- data/Plugins/Apps/Odoo/Odoo.conf.erb +0 -3
- data/Plugins/Apps/Odoo/Odoo.container +7 -1
- data/Plugins/Apps/Odoo/Odoo.lmm.rb +4 -5
- data/Plugins/Apps/Ollama/Ollama.container +26 -0
- data/Plugins/Apps/Ollama/Ollama.lmm.rb +73 -0
- data/Plugins/Apps/OpenTelemetry/Config/config.yaml +704 -0
- data/Plugins/Apps/OpenTelemetry/OpenTelemetry.lmm.rb +154 -0
- data/Plugins/Apps/OpenVidu/Ingress.container +23 -0
- data/Plugins/Apps/{GitLab/GitLab.conf.erb → OpenVidu/OpenVidu.conf.erb} +8 -3
- data/Plugins/Apps/OpenVidu/OpenVidu.container +21 -0
- data/Plugins/Apps/OpenVidu/OpenVidu.lmm.rb +94 -0
- data/Plugins/Apps/OpenVidu/OpenViduCall.conf.erb +32 -0
- data/Plugins/Apps/OpenVidu/OpenViduCall.container +20 -0
- data/Plugins/Apps/OpenVidu/ingress.yaml +10 -0
- data/Plugins/Apps/OpenVidu/livekit.yaml +13 -0
- data/Plugins/Apps/PHP-FPM/Connection.rb +91 -0
- data/Plugins/Apps/PHP-FPM/PHP-FPM.lmm.rb +31 -4
- data/Plugins/Apps/Peppermint/Peppermint.conf.erb +2 -9
- data/Plugins/Apps/Peppermint/Peppermint.container +7 -1
- data/Plugins/Apps/Peppermint/Peppermint.lmm.rb +29 -33
- data/Plugins/Apps/Perplexica/Perplexica.container +25 -0
- data/Plugins/Apps/Perplexica/Perplexica.lmm.rb +92 -0
- data/Plugins/Apps/Perplexica/config.toml +26 -0
- data/Plugins/Apps/Podman/Connection.rb +24 -0
- data/Plugins/Apps/Podman/Podman.lmm.rb +80 -0
- data/Plugins/Apps/Podman/storage.conf +6 -0
- data/Plugins/Apps/Postfix/Postfix.lmm.rb +249 -145
- data/Plugins/Apps/PostgreSQL/Connection.rb +97 -0
- data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +204 -99
- data/Plugins/Apps/Pterodactyl/Pterodactyl.conf.erb +0 -3
- data/Plugins/Apps/Pterodactyl/Pterodactyl.lmm.rb +0 -2
- data/Plugins/Apps/Pterodactyl/Wings.conf.erb +0 -3
- data/Plugins/Apps/RVM/RVM.lmm.rb +57 -0
- data/Plugins/Apps/Roundcube/Roundcube.conf.erb +72 -0
- data/Plugins/Apps/Roundcube/Roundcube.lmm.rb +141 -0
- data/Plugins/Apps/SSH/SSH.lmm.rb +9 -15
- data/Plugins/Apps/SearXNG/SearXNG.container +22 -0
- data/Plugins/Apps/SearXNG/SearXNG.lmm.rb +79 -0
- data/Plugins/Apps/SearXNG/limiter.toml +40 -0
- data/Plugins/Apps/SearXNG/settings.yml +2 -0
- data/Plugins/Apps/SigNoz/Config/alerts.yml +11 -0
- data/Plugins/Apps/SigNoz/Config/otel-collector-config.yaml +110 -0
- data/Plugins/Apps/SigNoz/Config/otel-collector-opamp-config.yaml +1 -0
- data/Plugins/Apps/SigNoz/Config/prometheus.yml +18 -0
- data/Plugins/Apps/SigNoz/SigNoz-Collector.container +23 -0
- data/Plugins/Apps/SigNoz/SigNoz-Migrator.container +17 -0
- data/Plugins/Apps/SigNoz/SigNoz.conf.erb +61 -0
- data/Plugins/Apps/SigNoz/SigNoz.container +26 -0
- data/Plugins/Apps/SigNoz/SigNoz.lmm.rb +319 -0
- data/Plugins/Apps/Solr/log4j2.xml +89 -0
- data/Plugins/Apps/Solr/solr.lmm.rb +82 -0
- data/Plugins/Apps/Sunshine/Sunshine.conf.erb +0 -3
- data/Plugins/Apps/Sunshine/Sunshine.lmm.rb +0 -1
- data/Plugins/Apps/Tunnel/tunnel.lmm.rb +59 -0
- data/Plugins/Apps/Tunnel/tunnelTCP.service +9 -0
- data/Plugins/Apps/Tunnel/tunnelTCP.socket +9 -0
- data/Plugins/Apps/Tunnel/tunnelUDP.service +9 -0
- data/Plugins/Apps/Tunnel/tunnelUDP.socket +9 -0
- data/Plugins/Apps/UVdesk/UVdesk.conf.erb +0 -3
- data/Plugins/Apps/Umami/Umami.container +19 -0
- data/Plugins/Apps/Umami/Umami.lmm.rb +108 -0
- data/Plugins/Apps/Valkey/Valkey.lmm.rb +64 -20
- data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +9 -6
- data/Plugins/Apps/Vaultwarden/Vaultwarden.container +7 -1
- data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +67 -28
- data/Plugins/Apps/Wiki.js/Wiki.js.conf.erb +39 -0
- data/Plugins/Apps/Wiki.js/Wiki.js.container +20 -0
- data/Plugins/Apps/Wiki.js/Wiki.js.lmm.rb +55 -0
- data/Plugins/Apps/YaCy/YaCy.conf.erb +93 -0
- data/Plugins/Apps/YaCy/YaCy.container +21 -0
- data/Plugins/Apps/YaCy/YaCy.lmm.rb +160 -0
- data/Plugins/Apps/ZooKeeper/ZooKeeper.container +24 -0
- data/Plugins/Apps/ZooKeeper/ZooKeeper.lmm.rb +68 -0
- data/Plugins/Apps/bitmagnet/bitmagnet.conf.erb +0 -3
- data/Plugins/Apps/bitmagnet/bitmagnet.lmm.rb +0 -1
- data/Plugins/Apps/gollum/gollum.conf.erb +40 -4
- data/Plugins/Apps/gollum/gollum.container +10 -1
- data/Plugins/Apps/gollum/gollum.lmm.rb +56 -47
- data/Plugins/Apps/llama.cpp/llama.cpp.container +28 -0
- data/Plugins/Apps/llama.cpp/llama.cpp.lmm.rb +90 -0
- data/Plugins/Apps/vLLM/vLLM.container +32 -0
- data/Plugins/Apps/vLLM/vLLM.lmm.rb +89 -0
- data/Plugins/OS/General/Utils.lmm.rb +26 -0
- data/Plugins/OS/Linux/Connection.rb +472 -0
- data/Plugins/OS/Linux/Debian/preseed.cfg.erb +81 -0
- data/Plugins/OS/Linux/Distributions.yaml +32 -0
- data/Plugins/OS/Linux/Flavours.yaml +24 -0
- data/Plugins/OS/Linux/Grub/grub.cfg +10 -0
- data/Plugins/OS/Linux/HTTP.rb +32 -0
- data/Plugins/OS/Linux/Linux.lmm.rb +708 -174
- data/Plugins/OS/Linux/Packages.yaml +67 -3
- data/Plugins/OS/Linux/Proxmox/answer.toml.erb +30 -0
- data/Plugins/OS/Linux/Services.yaml +8 -0
- data/Plugins/OS/Linux/Shell.rb +70 -0
- data/Plugins/OS/Linux/Syslinux/default +8 -0
- data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +93 -40
- data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +3 -0
- data/Plugins/OS/Linux/openSUSE/autoinst.xml.erb +29 -3
- data/Plugins/OS/Linux/systemd/systemd.lmm.rb +13 -11
- data/Plugins/OS/Routers/Aruba/ArubaInstant.lmm.rb +6 -5
- data/Plugins/Platforms/GitHub.lmm.rb +73 -28
- data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +10 -7
- data/Plugins/Platforms/Proxmox/Proxmox.lmm.rb +402 -0
- data/Plugins/Platforms/Proxmox/XTerm.rb +321 -0
- data/Plugins/Platforms/libvirt/libvirt.lmm.rb +41 -15
- data/Plugins/Platforms/porkbun.lmm.rb +12 -2
- data/Plugins/Platforms/porkbun_spec.rb +2 -2
- data/Plugins/Services/DNS/AmberBit.lmm.rb +1 -1
- data/Plugins/Services/DNS/ArubaItDNS.lmm.rb +1 -1
- data/Plugins/Services/DNS/NICLV.lmm.rb +1 -1
- data/Plugins/Services/DNS/PowerDNS.lmm.rb +130 -41
- data/Plugins/Services/DNS/tonic.lmm.rb +22 -12
- data/bootstrap.sh +41 -3
- data/lib/ConfigLMM/Framework/plugins/dns.rb +4 -3
- data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +187 -144
- data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +54 -6
- data/lib/ConfigLMM/Framework/plugins/plugin.rb +68 -140
- data/lib/ConfigLMM/Framework/plugins/store.rb +4 -4
- data/lib/ConfigLMM/Framework/variables.rb +75 -0
- data/lib/ConfigLMM/Framework.rb +1 -0
- data/lib/ConfigLMM/cli.rb +13 -5
- data/lib/ConfigLMM/commands/cleanup.rb +1 -0
- data/lib/ConfigLMM/commands/configsCommand.rb +38 -5
- data/lib/ConfigLMM/commands/diff.rb +33 -9
- data/lib/ConfigLMM/context.rb +22 -3
- data/lib/ConfigLMM/io/configList.rb +85 -7
- data/lib/ConfigLMM/io/connection.rb +143 -0
- data/lib/ConfigLMM/io/dhcp.rb +330 -0
- data/lib/ConfigLMM/io/http.rb +78 -0
- data/lib/ConfigLMM/io/local.rb +207 -0
- data/lib/ConfigLMM/io/pxe.rb +92 -0
- data/lib/ConfigLMM/io/ssh.rb +156 -0
- data/lib/ConfigLMM/io/tftp.rb +105 -0
- data/lib/ConfigLMM/io.rb +2 -0
- data/lib/ConfigLMM/secrets/envStore.rb +39 -0
- data/lib/ConfigLMM/secrets/fileStore.rb +43 -0
- data/lib/ConfigLMM/state.rb +12 -3
- data/lib/ConfigLMM/version.rb +2 -1
- data/lib/ConfigLMM.rb +1 -0
- data/{Examples → scripts}/configlmmAuth.sh +7 -5
- metadata +257 -9
@@ -5,53 +5,98 @@ module ConfigLMM
|
|
5
5
|
module LMM
|
6
6
|
class GitHub < Framework::Plugin
|
7
7
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
prompt.say('You need to create it manually - https://github.com/organizations/plan')
|
15
|
-
raise Framework::PluginPrerequisite.new('Organization must exist!')
|
8
|
+
def actionGitHubRefresh(id, target, activeState, context, options)
|
9
|
+
if !target['Organizations'].to_h.empty?
|
10
|
+
activeState['Organizations'] = {}
|
11
|
+
target['Organizations'].each do |name, organization|
|
12
|
+
organizationRefresh(name, target, activeState, context, options)
|
13
|
+
end
|
16
14
|
end
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
def actionGitHubDiff(id, target, activeState, context, options)
|
18
|
+
state = prepareState(target, activeState)
|
19
|
+
shouldMatch(id, state, 'Organizations', target, 'Organizations')
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
def actionGitHubDeploy(id, target, activeState, context, options)
|
23
|
+
actionGitHubDiff(id, target, activeState, context, options)
|
24
|
+
diff.each do |name, states|
|
25
|
+
# TODO FIXME
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
29
|
+
def prepareState(target, activeState)
|
30
|
+
state = activeState.dup
|
31
|
+
state['Organizations'] ||= {}
|
32
|
+
state['Organizations'].each do |name, data|
|
33
|
+
#state['Organizations'][name]['Name'] = state['Organizations'][name].delete('Login')
|
34
|
+
state['Organizations'][name]['Description'] = state['Organizations'][name].delete('description')
|
35
|
+
end
|
36
|
+
state
|
30
37
|
end
|
31
38
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
def organizationRefresh(name, target, activeState, context, options)
|
40
|
+
authToken = context.secrets.load(target['SecretId'], 'TOKEN')
|
41
|
+
authToken = context.secrets.load('GITHUB', 'TOKEN') if authToken.nil?
|
42
|
+
client = Octokit::Client.new(:access_token => authToken)
|
43
|
+
|
44
|
+
allOrgs = client.organizations
|
45
|
+
if allOrgs.empty?
|
46
|
+
# Fine-grained access token never returns any orgs
|
47
|
+
org = client.organization(name)
|
48
|
+
else
|
49
|
+
orgs = allOrgs.select { |org| org[:login] == name }
|
50
|
+
if orgs.empty?
|
51
|
+
prompt.say("Didn\'t find organization with name #{name}")
|
52
|
+
prompt.say('You need to create it manually - https://github.com/organizations/plan')
|
53
|
+
raise Framework::PluginPrerequisite.new('Organization must exist!')
|
39
54
|
end
|
55
|
+
org = orgs.first
|
56
|
+
end
|
57
|
+
|
58
|
+
activeState['Organizations'][org.login] ||= {}
|
59
|
+
|
60
|
+
org.each do |name, value|
|
61
|
+
data = value
|
62
|
+
data = value.to_h if value && value.respond_to?(:to_h)
|
63
|
+
data = value.to_s if value.is_a?(Time)
|
64
|
+
activeState['Organizations'][org.login][name.to_s] = data
|
40
65
|
end
|
41
|
-
# TODO FIXME
|
42
|
-
raise 'Not implemented!'
|
43
66
|
end
|
44
67
|
|
45
68
|
def authenticate(actionMethod, target, activeState, context, options)
|
46
|
-
authToken =
|
69
|
+
authToken = context.secrets.load(target['SecretId'], 'TOKEN')
|
70
|
+
authToken = context.secrets.load('GITHUB', 'TOKEN') if authToken.nil?
|
47
71
|
if authToken.to_s.empty?
|
48
72
|
prompt.say('Open https://github.com/settings/tokens and create a token!')
|
49
|
-
prompt.say(
|
50
|
-
raise Framework::PluginPrerequisite.new('Need
|
73
|
+
prompt.say("Then set it\'s value in #{target['SecretId']}_TOKEN")
|
74
|
+
raise Framework::PluginPrerequisite.new('Need GitHub token!')
|
51
75
|
end
|
52
76
|
true
|
53
77
|
end
|
54
78
|
|
79
|
+
def self.getReleases(repoId, logger, context, options)
|
80
|
+
response = HTTP.get("https://api.github.com/repos/#{repoId}/releases")
|
81
|
+
if response.status.success?
|
82
|
+
releases = response.parse
|
83
|
+
releases.reject! { |release| release['draft'] || release['prerelease'] }
|
84
|
+
return releases
|
85
|
+
end
|
86
|
+
logger.error("Failed to load GitHub release for #{repoId}")
|
87
|
+
raise response
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.getReleaseAsset(name, releases)
|
91
|
+
pattern = name.gsub('.', '\\.').gsub('*', '.*')
|
92
|
+
releases.each do |release|
|
93
|
+
release['assets'].each do |asset|
|
94
|
+
return asset if asset['name'].match?(pattern)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
raise "Couldn't find GitHub asset #{name}!"
|
98
|
+
end
|
99
|
+
|
55
100
|
end
|
56
101
|
end
|
57
102
|
end
|
@@ -21,10 +21,10 @@ module ConfigLMM
|
|
21
21
|
|
22
22
|
template = ERB.new(File.read(__dir__ + '/zone.txt.erb'))
|
23
23
|
|
24
|
-
target['DNS'].each do |domain, data|
|
24
|
+
target['DNS'].to_h.each do |domain, data|
|
25
25
|
config = { 'Domain' => domain, 'Records' => '' }
|
26
26
|
data.each do |name, data|
|
27
|
-
self.processDNS(domain, data).each do |type, records|
|
27
|
+
self.processDNS(domain, data, context).each do |type, records|
|
28
28
|
records.each do |record|
|
29
29
|
shortName = Addressable::IDNA.to_ascii(name) + '.' if type == 'CNAME' || type == 'ALIAS'
|
30
30
|
if record[:type] == 'MX'
|
@@ -42,7 +42,9 @@ module ConfigLMM
|
|
42
42
|
|
43
43
|
def actionGoDaddyDNSRefresh(id, target, activeState, context, options)
|
44
44
|
if USE_API
|
45
|
-
|
45
|
+
authSecret = context.secrets.load(target['SecretId'], 'SECRET')
|
46
|
+
authSecret = context.secrets.load('GODADDY', 'SECRET') if authSecret.nil?
|
47
|
+
http = HTTP.auth("sso-key #{authSecret}")
|
46
48
|
apiDomain = (options['dry'] || target['Test']) ? TEST_API_DOMAIN : API_DOMAIN
|
47
49
|
|
48
50
|
target['DNS'].each do |domain, records|
|
@@ -65,7 +67,7 @@ module ConfigLMM
|
|
65
67
|
if USE_API
|
66
68
|
# TODO
|
67
69
|
else
|
68
|
-
showManualDNSSteps(target, "Click on DNS tab and either import generated Zone file or add these records:") do |domain|
|
70
|
+
showManualDNSSteps(target, "Click on DNS tab and either import generated Zone file or add these records:", context) do |domain|
|
69
71
|
prompt.say("Open https://dcc.godaddy.com/control/portfolio/#{domain}/settings", :color => :magenta)
|
70
72
|
end
|
71
73
|
end
|
@@ -73,11 +75,12 @@ module ConfigLMM
|
|
73
75
|
|
74
76
|
def authenticate(actionMethod, target, activeState, context, options)
|
75
77
|
if USE_API
|
76
|
-
authSecret =
|
78
|
+
authSecret = context.secrets.load(target['SecretId'], 'SECRET')
|
79
|
+
authSecret = context.secrets.load('GODADDY', 'SECRET') if authSecret.nil?
|
77
80
|
if authSecret.to_s.empty?
|
78
81
|
prompt.say('Open https://developer.godaddy.com/keys and create API Key!')
|
79
|
-
prompt.say(
|
80
|
-
raise Framework::PluginPrerequisite.new('Need
|
82
|
+
prompt.say("Then set \"KEY:SECRET\" in #{target['SecretId']}_SECRET")
|
83
|
+
raise Framework::PluginPrerequisite.new('Need GoDaddy secret!')
|
81
84
|
end
|
82
85
|
end
|
83
86
|
true
|
@@ -0,0 +1,402 @@
|
|
1
|
+
|
2
|
+
require_relative 'XTerm'
|
3
|
+
require 'fog/proxmox'
|
4
|
+
require 'cgi'
|
5
|
+
require 'addressable/uri'
|
6
|
+
|
7
|
+
module ConfigLMM
|
8
|
+
module LMM
|
9
|
+
class Proxmox < Framework::Plugin
|
10
|
+
|
11
|
+
def self.buildURI(uri)
|
12
|
+
uri = uri.dup
|
13
|
+
uri.scheme = 'https'
|
14
|
+
uri.host = Addressable::IDNA.to_ascii(uri.host)
|
15
|
+
uri.port = 8006 if uri.port.nil?
|
16
|
+
uri.path = '/api2/json' if uri.path.to_s.empty? || uri.path == '/'
|
17
|
+
uri.query = nil
|
18
|
+
uri
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.getAuthParams(uri, context)
|
22
|
+
uri = Addressable::URI.parse(uri) if uri.is_a?(String)
|
23
|
+
raise 'Invalid Proxmox URL!' unless uri.scheme == 'proxmox'
|
24
|
+
connectionOptions = { }
|
25
|
+
parsedQuery = CGI.parse(uri.query)
|
26
|
+
if parsedQuery['insecure']
|
27
|
+
connectionOptions[:ssl_verify_peer] = false
|
28
|
+
end
|
29
|
+
secretId = parsedQuery['proxmoxSecretId'].to_a.first || 'PROXMOX'
|
30
|
+
uri = self.buildURI(uri)
|
31
|
+
|
32
|
+
proxmoxUsername = context.secrets.load(secretId, 'PROXMOX_USER')
|
33
|
+
proxmoxPassword = context.secrets.load(secretId, 'PROXMOX_PASSWORD')
|
34
|
+
if !proxmoxPassword
|
35
|
+
proxmoxUsername = 'root@pam'
|
36
|
+
proxmoxPassword = context.secrets.load(secretId, 'ROOT_PASSWORD')
|
37
|
+
end
|
38
|
+
|
39
|
+
raise 'Missing Proxmox password!' unless proxmoxPassword
|
40
|
+
|
41
|
+
authParams = {
|
42
|
+
proxmox_url: uri.to_s,
|
43
|
+
connection_options: connectionOptions,
|
44
|
+
proxmox_auth_method: 'access_ticket',
|
45
|
+
proxmox_username: proxmoxUsername,
|
46
|
+
proxmox_password: proxmoxPassword
|
47
|
+
}
|
48
|
+
authParams
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.getNode(authParams)
|
52
|
+
# For some reason Proxmox doesn't handle SSL shutdown correctly so we use this workaround
|
53
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
54
|
+
|
55
|
+
compute = Fog::Compute.new(provider: :proxmox, **authParams)
|
56
|
+
node = compute.nodes.find { |node| node.node == 'pve' }
|
57
|
+
raise 'Couldn\'t find pve node!' unless node
|
58
|
+
[node, compute]
|
59
|
+
ensure
|
60
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] &= ~OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
61
|
+
end
|
62
|
+
|
63
|
+
def actionProxmoxDeploy(id, target, activeState, context, options)
|
64
|
+
authParams = self.class.getAuthParams(target['Location'], context)
|
65
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
66
|
+
|
67
|
+
if !target['Storage'].to_h.empty?
|
68
|
+
storage = Fog::Storage.new(provider: :proxmox, **authParams)
|
69
|
+
all = storage.list
|
70
|
+
target['Storage'].each do |name, data|
|
71
|
+
if all.none? { |entry| entry['storage'] == name }
|
72
|
+
storage.create({ 'storage' => name, **data })
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
ensure
|
77
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] &= ~OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
78
|
+
end
|
79
|
+
|
80
|
+
def createVM(serverName, serverInfo, targetUri, iso, activeState, context)
|
81
|
+
authParams = self.class.getAuthParams(targetUri, context)
|
82
|
+
node, compute = self.class.getNode(authParams)
|
83
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
84
|
+
server = node.servers.find { |server| server.name == serverName }
|
85
|
+
if server
|
86
|
+
if server.status != 'running'
|
87
|
+
server.action('start')
|
88
|
+
server.wait_for { server.ready? }
|
89
|
+
end
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
|
93
|
+
isoStorages = node.storages.list_by_content_type('iso')
|
94
|
+
isoStorageName = isoStorages.first.storage
|
95
|
+
|
96
|
+
storage = Fog::Storage.new(provider: :proxmox, **authParams)
|
97
|
+
file = File.open(iso, 'rb')
|
98
|
+
filename = File.basename(iso)
|
99
|
+
storage.upload({
|
100
|
+
node: node.node,
|
101
|
+
storage: isoStorageName
|
102
|
+
},
|
103
|
+
{
|
104
|
+
content: 'iso',
|
105
|
+
file: file,
|
106
|
+
filename: filename
|
107
|
+
}
|
108
|
+
)
|
109
|
+
|
110
|
+
settings = {
|
111
|
+
vmid: node.servers.next_id,
|
112
|
+
name: serverName,
|
113
|
+
bios: 'ovmf',
|
114
|
+
boot: 'order=virtio0;scsi0;net0',
|
115
|
+
cpu: 'cputype=host',
|
116
|
+
machine: 'q35',
|
117
|
+
onboot: 1,
|
118
|
+
ostype: 'l26',
|
119
|
+
scsi0: "#{isoStorageName}:iso/#{filename},media=cdrom",
|
120
|
+
scsihw: 'virtio-scsi-pci',
|
121
|
+
serial0: 'socket',
|
122
|
+
vga: 'qxl'
|
123
|
+
}
|
124
|
+
|
125
|
+
if serverInfo['CPU']
|
126
|
+
settings[:cores] = serverInfo['CPU']
|
127
|
+
end
|
128
|
+
if serverInfo['RAM']
|
129
|
+
settings[:memory] = Filesize.from(serverInfo['RAM']).to_f('MiB').to_i
|
130
|
+
end
|
131
|
+
|
132
|
+
if serverInfo['NIC']
|
133
|
+
nics = serverInfo['NIC']
|
134
|
+
nics = [nics] unless nics.is_a?(Array)
|
135
|
+
nics.each_with_index do |nic, i|
|
136
|
+
nic.transform_keys!(&:downcase)
|
137
|
+
nic['model'] = 'virtio' unless nic['model']
|
138
|
+
if nic['mac']
|
139
|
+
nic['macaddr'] = nic['mac']
|
140
|
+
nic.delete('mac')
|
141
|
+
end
|
142
|
+
if nic['vlan']
|
143
|
+
nic['tag'] = nic['vlan']
|
144
|
+
nic.delete('vlan')
|
145
|
+
end
|
146
|
+
settings["net#{i}"] = nic.map { |name_value| name_value.join('=') }.join(',')
|
147
|
+
end
|
148
|
+
elsif serverInfo['NetworkBridge']
|
149
|
+
settings['net0'] = "virtio,bridge=#{serverInfo['NetworkBridge']}"
|
150
|
+
end
|
151
|
+
|
152
|
+
server = node.servers.create(settings)
|
153
|
+
|
154
|
+
imageStorages = node.storages.list_by_content_type('images')
|
155
|
+
imageStorageName = imageStorages.first.storage
|
156
|
+
efidisk = { id: 'efidisk0', storage: imageStorageName, size: '528' }
|
157
|
+
server.attach(efidisk, { efitype: '4m', 'pre-enrolled-keys': 1 })
|
158
|
+
|
159
|
+
if serverInfo['Storage']
|
160
|
+
disk = { id: 'virtio0', storage: imageStorageName, size: Filesize.from(serverInfo['Storage']).to_f('GiB').to_i }
|
161
|
+
server.attach(disk, { replicate: 0 })
|
162
|
+
end
|
163
|
+
|
164
|
+
if server.status != 'running'
|
165
|
+
server.action('start')
|
166
|
+
server.wait_for { server.ready? }
|
167
|
+
end
|
168
|
+
true
|
169
|
+
ensure
|
170
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] &= ~OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
171
|
+
end
|
172
|
+
|
173
|
+
def createContainer(serverInfo, targetUri, flavourInfo, activeState, context)
|
174
|
+
authParams = self.class.getAuthParams(targetUri, context)
|
175
|
+
node, compute = self.class.getNode(authParams)
|
176
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
177
|
+
|
178
|
+
serverInfo['Domain'] = serverInfo['Name'] unless serverInfo['Domain']
|
179
|
+
container = node.containers.find { |container| container.name == Addressable::IDNA.to_ascii(serverInfo['Domain']) }
|
180
|
+
if container
|
181
|
+
if container.status != 'running'
|
182
|
+
container.action('start')
|
183
|
+
container.wait_for { container.ready? }
|
184
|
+
end
|
185
|
+
return false
|
186
|
+
end
|
187
|
+
|
188
|
+
raise Framework::PluginProcessError.new("Don't have LXC template!") unless flavourInfo['LXC']
|
189
|
+
|
190
|
+
storage = Fog::Storage.new(provider: :proxmox, **authParams)
|
191
|
+
appliances = storage.list_appliances({ node: node.node })
|
192
|
+
appliance = appliances.find { |appliance| appliance['package'] == flavourInfo['LXC'] }
|
193
|
+
raise "Couldn't find LXC template #{flavourInfo['LXC']}" unless appliance
|
194
|
+
templateStorages = node.storages.list_by_content_type('vztmpl')
|
195
|
+
templateStorageName = templateStorages.first.storage
|
196
|
+
storage.download_appliance({ node: node.node }, { storage: templateStorageName, template: appliance['template'] })
|
197
|
+
|
198
|
+
settings = {
|
199
|
+
vmid: node.servers.next_id,
|
200
|
+
ostemplate: "#{templateStorageName}:vztmpl/#{appliance['template']}",
|
201
|
+
onboot: 1,
|
202
|
+
unprivileged: 1
|
203
|
+
}
|
204
|
+
|
205
|
+
if serverInfo['CPU']
|
206
|
+
settings[:cores] = serverInfo['CPU']
|
207
|
+
end
|
208
|
+
|
209
|
+
if serverInfo['RAM']
|
210
|
+
settings[:memory] = Filesize.from(serverInfo['RAM'].to_s).to_f('MiB').to_i
|
211
|
+
end
|
212
|
+
|
213
|
+
if serverInfo['Swap']
|
214
|
+
settings[:swap] = Filesize.from(serverInfo['Swap'].to_s).to_f('MiB').to_i
|
215
|
+
end
|
216
|
+
|
217
|
+
if serverInfo['Storage']
|
218
|
+
storagePool = serverInfo['StoragePool']
|
219
|
+
if !storagePool
|
220
|
+
storages = node.storages.list_by_content_type('rootdir')
|
221
|
+
storagePool = storages.first.storage
|
222
|
+
end
|
223
|
+
settings[:rootfs] = storagePool + ':' + Filesize.from(serverInfo['Storage'].to_s).to_f('GiB').to_i.to_s
|
224
|
+
end
|
225
|
+
|
226
|
+
if serverInfo['Domain']
|
227
|
+
settings[:hostname] = Addressable::IDNA.to_ascii(serverInfo['Domain'])
|
228
|
+
end
|
229
|
+
|
230
|
+
if serverInfo['Network'].is_a?(Hash) && serverInfo['Network']['DNS']
|
231
|
+
settings[:nameserver] = serverInfo['Network']['DNS']
|
232
|
+
end
|
233
|
+
|
234
|
+
if serverInfo['NIC']
|
235
|
+
nics = serverInfo['NIC']
|
236
|
+
nics = [nics] unless nics.is_a?(Array)
|
237
|
+
nics.each_with_index do |nic, i|
|
238
|
+
nic.transform_keys!(&:downcase)
|
239
|
+
nic['name'] = "eth#{i}" unless nic['name']
|
240
|
+
if nic['mac']
|
241
|
+
nic['hwaddr'] = nic['mac']
|
242
|
+
nic.delete('mac')
|
243
|
+
end
|
244
|
+
if nic['vlan']
|
245
|
+
nic['tag'] = nic['vlan']
|
246
|
+
nic.delete('vlan')
|
247
|
+
end
|
248
|
+
nic[:ip] = 'dhcp'
|
249
|
+
if serverInfo['Network'].is_a?(Hash)
|
250
|
+
if nic['name'] == 'eth0'
|
251
|
+
if serverInfo['Network'].key?('IP')
|
252
|
+
nic[:ip] = serverInfo['Network']['IP']
|
253
|
+
end
|
254
|
+
if serverInfo['Network'].key?('Gateway')
|
255
|
+
nic[:gw] = serverInfo['Network']['Gateway']
|
256
|
+
end
|
257
|
+
else
|
258
|
+
interface = serverInfo['Network']['Interfaces'][nic['name']]
|
259
|
+
nic[:ip] = interface['IP'] if interface
|
260
|
+
end
|
261
|
+
end
|
262
|
+
settings["net#{i}"] = nic.map { |name_value| name_value.join('=') }.join(',')
|
263
|
+
end
|
264
|
+
elsif serverInfo['NetworkBridge']
|
265
|
+
nic = {
|
266
|
+
name: 'eth0',
|
267
|
+
bridge: serverInfo['NetworkBridge'],
|
268
|
+
ip: 'dhcp'
|
269
|
+
}
|
270
|
+
|
271
|
+
if serverInfo['Network'].is_a?(Hash)
|
272
|
+
if serverInfo['Network'].key?('IP')
|
273
|
+
nic[:ip] = serverInfo['Network']['IP']
|
274
|
+
end
|
275
|
+
if serverInfo['Network'].key?('Gateway')
|
276
|
+
nic[:gw] = serverInfo['Network']['Gateway']
|
277
|
+
end
|
278
|
+
end
|
279
|
+
settings['net0'] = nic.map { |name_value| name_value.join('=') }.join(',')
|
280
|
+
end
|
281
|
+
|
282
|
+
if flavourInfo['Type']
|
283
|
+
settings[:ostype] = flavourInfo['Type']
|
284
|
+
end
|
285
|
+
|
286
|
+
if serverInfo['Users']['root'].key?('Password')
|
287
|
+
settings[:password] = serverInfo['Users']['root']['Password']
|
288
|
+
end
|
289
|
+
|
290
|
+
if !serverInfo['Users']['root']['AuthorizedKeys'].to_a.empty?
|
291
|
+
settings['ssh-public-keys'] = serverInfo['Users']['root']['AuthorizedKeys'].join("\n")
|
292
|
+
end
|
293
|
+
|
294
|
+
if serverInfo['Features']
|
295
|
+
settings[:features] = serverInfo['Features'].map { |feature| "#{feature}=1" }.join(',')
|
296
|
+
end
|
297
|
+
|
298
|
+
container = node.containers.create(settings)
|
299
|
+
|
300
|
+
if serverInfo['LXC'].is_a?(Array)
|
301
|
+
self.addLXCOptions(serverInfo, targetUri, compute, node.node, container.vmid, context)
|
302
|
+
end
|
303
|
+
|
304
|
+
# TODO - Need to be readable/executable by everyone. Otherwise some things will break inside container like `su`
|
305
|
+
# if storageIsSubvolume
|
306
|
+
# proxmoxServer.exec("chmod +rx #{storagePath}/images/$ID/subvol-$ID-disk-0.subvol")
|
307
|
+
#end
|
308
|
+
|
309
|
+
if container.status != 'running'
|
310
|
+
container.action('start')
|
311
|
+
container.wait_for { container.ready? }
|
312
|
+
end
|
313
|
+
true
|
314
|
+
ensure
|
315
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] &= ~OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
316
|
+
end
|
317
|
+
|
318
|
+
def addLXCOptions(serverInfo, uri, compute, node, vmid, context)
|
319
|
+
options = serverInfo['LXC'].map { |option| 'lxc.' + option.map { |name, value| "#{name}: #{value}" }.first }.join("\n")
|
320
|
+
|
321
|
+
uri = Addressable::URI.parse(uri) if uri.is_a?(String)
|
322
|
+
self.class.xtermTunnel(uri, serverInfo, compute, node, nil, nil, context, prompt, logger) do |xterm|
|
323
|
+
connection = IO::Connection.new(:Proxmox, xterm, prompt, logger)
|
324
|
+
connection.exec("echo \"#{options}\" >> /etc/pve/lxc/#{vmid}.conf")
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def self.withXTerm(targetUri, target, context, prompt, logger, &block)
|
329
|
+
targetUri.scheme = 'proxmox'
|
330
|
+
authParams = getAuthParams(targetUri, context)
|
331
|
+
node, compute = getNode(authParams)
|
332
|
+
name = nil
|
333
|
+
name = CGI.parse(targetUri.query)['name'] if targetUri.query
|
334
|
+
name = name.first if name
|
335
|
+
|
336
|
+
lxc = target['LXC']
|
337
|
+
if targetUri.query
|
338
|
+
parsedQuery = CGI.parse(targetUri.query)
|
339
|
+
name = parsedQuery['name']
|
340
|
+
name = name.first if name
|
341
|
+
lxc = true if parsedQuery['lxc'] && lxc.nil?
|
342
|
+
end
|
343
|
+
|
344
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
345
|
+
|
346
|
+
if lxc
|
347
|
+
unless name
|
348
|
+
name = target['Name']
|
349
|
+
name = Addressable::IDNA.to_ascii(target['Domain']) if target['Domain']
|
350
|
+
end
|
351
|
+
server = node.containers.find { |container| container.name == name }
|
352
|
+
type = 'lxc'
|
353
|
+
else
|
354
|
+
name = target['Name'] unless name
|
355
|
+
server = node.servers.find { |server| server.name == name }
|
356
|
+
type = 'qemu'
|
357
|
+
end
|
358
|
+
raise "Couldn't find server with name #{name}" unless server
|
359
|
+
|
360
|
+
self.xtermTunnel(targetUri, target, compute, node.node, type, server.vmid, context, prompt, logger, &block)
|
361
|
+
ensure
|
362
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] &= ~OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
363
|
+
end
|
364
|
+
|
365
|
+
def self.xtermTunnel(targetUri, target, compute, node, type, vmid, context, prompt, logger, &block)
|
366
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
367
|
+
term = compute.create_term({ node: node, type: type, vmid: vmid }, {})
|
368
|
+
|
369
|
+
parsedQuery = CGI.parse(targetUri.query)
|
370
|
+
insecure = !!parsedQuery['insecure']
|
371
|
+
|
372
|
+
if target['Type'] == :Linux
|
373
|
+
username = 'root'
|
374
|
+
password = target['Users']['root']['Password']
|
375
|
+
else
|
376
|
+
secretId = parsedQuery['secretId'].to_a.first || target['SecretId']
|
377
|
+
username = context.secrets.load(secretId, 'ROOT_USER') || 'root'
|
378
|
+
password = context.secrets.load(secretId, 'ROOT_PASSWORD')
|
379
|
+
end
|
380
|
+
|
381
|
+
uri = self.buildURI(targetUri)
|
382
|
+
uri.scheme = 'wss'
|
383
|
+
if type.nil? && vmid.nil?
|
384
|
+
uri.path += "/nodes/#{node}/vncwebsocket"
|
385
|
+
else
|
386
|
+
uri.path += "/nodes/#{node}/#{type}/#{vmid}/vncwebsocket"
|
387
|
+
end
|
388
|
+
uri.query = URI.encode_www_form({ port: term['port'], vncticket: term['ticket'] })
|
389
|
+
|
390
|
+
ProxmoxXTerm.tunnel(uri.to_s, insecure, compute.token, term, username, password, prompt, logger, &block)
|
391
|
+
ensure
|
392
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] &= ~OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
393
|
+
end
|
394
|
+
|
395
|
+
def self.getLocation(location)
|
396
|
+
uri = Addressable::URI.parse(location)
|
397
|
+
uri.hostname
|
398
|
+
end
|
399
|
+
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|