host from template ui
This commit is contained in:
		| @ -2,6 +2,7 @@ package main | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"net/netip" | ||||
|  | ||||
| 	"github.com/emicklei/go-restful" | ||||
|  | ||||
| @ -55,6 +56,10 @@ func hostOrTemplate(cfg *localconfig.Config, name string) (host *localconfig.Hos | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func wsHostsFromTemplateList(req *restful.Request, resp *restful.Response) { | ||||
| 	hostsFromTemplate.WsList(resp, "") | ||||
| } | ||||
|  | ||||
| func wsHostsFromTemplateSet(req *restful.Request, resp *restful.Response) { | ||||
| 	name := req.PathParameter("name") | ||||
|  | ||||
| @ -78,6 +83,10 @@ func wsHostsFromTemplateSet(req *restful.Request, resp *restful.Response) { | ||||
| 		wsBadRequest(resp, "ip is required") | ||||
| 		return | ||||
| 	} | ||||
| 	if _, err := netip.ParseAddr(v.IP); err != nil { | ||||
| 		wsBadRequest(resp, "bad IP: "+err.Error()) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	found := false | ||||
| 	for _, ht := range cfg.HostTemplates { | ||||
|  | ||||
| @ -76,6 +76,8 @@ func registerWS(rest *restful.Container) { | ||||
| 	ws.Route(ws.GET("/clusters").To(wsListClusters). | ||||
| 		Doc("List clusters")) | ||||
|  | ||||
| 	ws.Route(ws.GET("/hosts-from-template").To(wsHostsFromTemplateList). | ||||
| 		Doc("List host template instances")) | ||||
| 	ws.Route(ws.POST("/hosts-from-template/{name}").To(wsHostsFromTemplateSet). | ||||
| 		Reads(HostFromTemplate{}). | ||||
| 		Doc("Create or update a host template instance")) | ||||
|  | ||||
| @ -87,11 +87,13 @@ | ||||
|   </div> | ||||
|  | ||||
|   <h2>Admin actions</h2> | ||||
|  | ||||
|   <h3>Config</h3> | ||||
|   <form @submit="uploadConfig"> | ||||
|       <input type="file" ref="configUpload" required /> | ||||
|       <input type="submit" value="upload config" /> | ||||
|   </form> | ||||
|  | ||||
|   <h3>Store</h3> | ||||
|   <p><a :href="'/public/store.tar?token='+state.Store.DownloadToken" target="_blank">Download</a></p> | ||||
|   <form @submit="storeAddKey" action="/store/add-key"> | ||||
| @ -109,6 +111,27 @@ | ||||
|       <p v-if="state.Store.KeyNames">Available names: | ||||
|         <template v-for="k,i in state.Store.KeyNames">{{i?", ":""}}<code @click="forms.delKey.name=k">{{k}}</code></template>.</p> | ||||
|   </form> | ||||
|  | ||||
| <template v-if="state.HostTemplates"> | ||||
|   <h3>Hosts from template</h3> | ||||
|   <form @submit="hostFromTemplateAdd" action=""> | ||||
|       <p>Add a host from template instance:</p> | ||||
|       <input type="text" v-model="forms.hostFromTemplate.name" required placeholder="Name" /> | ||||
|       <select v-model="forms.hostFromTemplate.Template" required> | ||||
|         <option v-for="name in state.HostTemplates" :value="name">{{name}}</option> | ||||
|       </select> | ||||
|       <input type="text" v-model="forms.hostFromTemplate.IP" required placeholder="IP" /> | ||||
|       <input type="submit" value="add instance" /> | ||||
|   </form> | ||||
|   <form @submit="hostFromTemplateDel" action=""> | ||||
|       <p>Remove a host from template instance:</p> | ||||
|       <select v-model="forms.hostFromTemplateDel" required> | ||||
|         <option v-for="h in hostsFromTemplate" :value="h.Name">{{h.Name}}</option> | ||||
|       </select> | ||||
|       <input type="submit" value="delete instance" :disabled="!forms.hostFromTemplateDel" /> | ||||
|   </form> | ||||
| </template> | ||||
|  | ||||
| </template> | ||||
| </div> | ||||
|  | ||||
|  | ||||
| @ -10,7 +10,7 @@ export default { | ||||
|   <section> | ||||
|     <div><small>Cluster: {{ host.Cluster }}<template v-if="host.Template"> ({{ host.Template }})</template></small></div> | ||||
|     <template v-for="ip in host.IPs"> | ||||
|       <tt>{{ ip }}</tt> | ||||
|       <code>{{ ip }}</code> | ||||
|     </template> | ||||
|   </section> | ||||
|   <div class="section">Downloads</div> | ||||
|  | ||||
| @ -12,6 +12,8 @@ createApp({ | ||||
|                 store: {}, | ||||
|                 storeUpload: {}, | ||||
|                 delKey: {}, | ||||
|                 hostFromTemplate: {}, | ||||
|                 hostFromTemplateDel: "", | ||||
|             }, | ||||
|             session: {}, | ||||
|             error: null, | ||||
| @ -54,6 +56,12 @@ createApp({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|         hostsFromTemplate() { | ||||
|             return (this.state.Hosts||[]).filter((h) => h.Template) | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|         copyText(text) { | ||||
|             event.preventDefault() | ||||
| @ -104,9 +112,21 @@ createApp({ | ||||
|             }) | ||||
|         }, | ||||
|         uploadConfig() { | ||||
|             event.preventDefault() | ||||
|             this.apiPost('/configs', this.$refs.configUpload.files[0], (v) => {}, "text/vnd.yaml") | ||||
|         }, | ||||
|         hostFromTemplateAdd() { | ||||
|             let v = this.forms.hostFromTemplate; | ||||
|             this.apiPost('/hosts-from-template/'+v.name, v, (v) => { this.forms.hostFromTemplate = {} }); | ||||
|         }, | ||||
|         hostFromTemplateDel() { | ||||
|             event.preventDefault() | ||||
|  | ||||
|             let v = this.forms.hostFromTemplateDel; | ||||
|             if (!confirm("delete host template instance "+v+"?")) { | ||||
|                 return | ||||
|             } | ||||
|             this.apiDelete('/hosts-from-template/'+v, (v) => { this.forms.hostFromTemplateDel = "" }); | ||||
|         }, | ||||
|         apiPost(action, data, onload, contentType = 'application/json') { | ||||
|             event.preventDefault() | ||||
|  | ||||
| @ -155,6 +175,27 @@ createApp({ | ||||
|                 xhr.send(data) | ||||
|             } | ||||
|         }, | ||||
|         apiDelete(action, data, onload) { | ||||
|             event.preventDefault() | ||||
|  | ||||
|             var xhr = new XMLHttpRequest() | ||||
|             xhr.onload = (r) => { | ||||
|                 if (xhr.status != 200) { | ||||
|                     this.error = xhr.response | ||||
|                     return | ||||
|                 } | ||||
|                 this.error = null | ||||
|                 if (onload) { | ||||
|                     onload(xhr.response) | ||||
|                 } | ||||
|             } | ||||
|             xhr.open("DELETE", action) | ||||
|             xhr.setRequestHeader('Accept', 'application/json') | ||||
|             if (this.session.token) { | ||||
|                 xhr.setRequestHeader('Authorization', 'Bearer '+this.session.token) | ||||
|             } | ||||
|             xhr.send() | ||||
|         }, | ||||
|         download(url) { | ||||
|             event.target.target = '_blank' | ||||
|             event.target.href = this.downloadLink(url) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user