2 * Copyright (C) 2018 "IoT.bzh"
3 * Author Sebastien Douheret <sebastien@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
27 common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
28 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xdsconfig"
29 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xsapiv1"
30 socketio "github.com/googollee/go-socket.io"
31 "github.com/syncthing/syncthing/lib/sync"
34 // Targets Represent a XDS targets
38 tgts map[string]*ITARGET
39 terminals map[string]*Terminals
42 // Mutex to make add/delete atomic
43 var tcMutex = sync.NewMutex()
49 // TargetsConstructor Create a new instance of Model Target
50 func TargetsConstructor(ctx *Context) *Targets {
51 file, _ := xdsconfig.TargetsConfigFilenameGet()
55 tgts: make(map[string]*ITARGET),
56 terminals: make(map[string]*Terminals),
60 // LoadConfig Load targets configuration from disk
61 func (t *Targets) LoadConfig() error {
62 var tgts []xsapiv1.TargetConfig
64 if t.fileOnDisk != "" {
65 t.Log.Infof("Use target config file: %s", t.fileOnDisk)
66 err := targetsConfigRead(t.fileOnDisk, &tgts)
68 if strings.HasPrefix(err.Error(), "EOF") {
69 t.Log.Warnf("Empty target config file")
70 } else if strings.HasPrefix(err.Error(), "No target config") {
71 t.Log.Warnf(err.Error())
77 t.Log.Warnf("Targets config filename not set")
81 t.Log.Infof("Loading initial targets config: %d targets found", len(tgts))
82 for _, tc := range tgts {
83 if _, err := t.createUpdate(tc, false, true); err != nil {
91 // SaveConfig Save targets configuration to disk
92 func (t *Targets) SaveConfig() error {
93 if t.fileOnDisk == "" {
94 return fmt.Errorf("Targets config filename not set")
97 // FIXME: buffered save or avoid to write on disk each time
98 return targetsConfigWrite(t.fileOnDisk, t.getConfigArrUnsafe())
101 // ResolveID Complete a Target ID (helper for user that can use partial ID value)
102 func (t *Targets) ResolveID(id string) (string, error) {
108 for iid := range t.tgts {
109 if strings.HasPrefix(iid, id) {
110 match = append(match, iid)
116 } else if len(match) == 0 {
117 return id, fmt.Errorf("Unknown id")
119 return id, fmt.Errorf("Multiple IDs found %v", match)
122 // Get returns the target config or nil if not existing
123 func (t *Targets) Get(id string) *ITARGET {
127 tc, exist := t.tgts[id]
134 // GetConfigArr returns the config of all targets as an array
135 func (t *Targets) GetConfigArr() []xsapiv1.TargetConfig {
137 defer tcMutex.Unlock()
139 return t.getConfigArrUnsafe()
142 // getConfigArrUnsafe Same as GetConfigArr without mutex protection
143 func (t *Targets) getConfigArrUnsafe() []xsapiv1.TargetConfig {
144 conf := []xsapiv1.TargetConfig{}
145 for _, v := range t.tgts {
146 conf = append(conf, (*v).GetConfig())
151 // Add adds a new target
152 func (t *Targets) Add(newT xsapiv1.TargetConfig) (*xsapiv1.TargetConfig, error) {
153 return t.createUpdate(newT, true, false)
156 // CreateUpdate creates or update a target
157 func (t *Targets) createUpdate(newT xsapiv1.TargetConfig, create bool, initial bool) (*xsapiv1.TargetConfig, error) {
161 defer tcMutex.Unlock()
164 if _, exist := t.tgts[newT.ID]; exist {
165 return nil, fmt.Errorf("ID already exists")
170 case xsapiv1.TypeTgtStandard:
171 tgt = NewTargetStandard(t.Context)
173 return nil, fmt.Errorf("Unsupported target type")
176 // Allocate a new UUID
178 newT.ID = tgt.NewUID("")
180 if !create && newT.ID == "" {
181 return nil, fmt.Errorf("Cannot update target with null ID")
186 if len(newT.ID) > 8 {
187 newT.Name += "_" + newT.ID[0:8]
189 newT.Name += "_" + newT.ID
193 // Call terminals constructor the first time
195 if _, exist := t.terminals[newT.ID]; !exist {
196 terms = TerminalsConstructor(t.Context)
197 t.terminals[newT.ID] = terms
199 terms = t.terminals[newT.ID]
202 var newTarget *xsapiv1.TargetConfig
205 if newTarget, err = tgt.Add(newT, terms); err != nil {
206 newT.Status = xsapiv1.StatusTgtErrorConfig
207 log.Printf("ERROR Adding target: %v\n", err)
208 return newTarget, err
211 // Just update target config
212 if newTarget, err = tgt.Setup(newT, terms); err != nil {
213 newT.Status = xsapiv1.StatusTgtErrorConfig
214 log.Printf("ERROR Updating target: %v\n", err)
215 return newTarget, err
220 for _, tc := range newT.Terms {
221 _, err := t.CreateUpdateTerminal(newT.ID, tc, initial)
223 return newTarget, err
227 // Add to folders list
228 t.tgts[newT.ID] = &tgt
230 // Save config on disk
232 if err := t.SaveConfig(); err != nil {
233 return newTarget, err
237 newTgt := tgt.GetConfig()
241 // Delete deletes a specific target
242 func (t *Targets) Delete(id string) (xsapiv1.TargetConfig, error) {
246 defer tcMutex.Unlock()
248 tgc := xsapiv1.TargetConfig{}
249 tc, exist := t.tgts[id]
251 return tgc, fmt.Errorf("unknown id")
254 tgc = (*tc).GetConfig()
256 if err = (*tc).Delete(); err != nil {
262 // Save config on disk
272 // GetTerminalsArr Return list of existing terminals
273 func (t *Targets) GetTerminalsArr(targetID string) ([]xsapiv1.TerminalConfig, error) {
274 arr := []xsapiv1.TerminalConfig{}
276 tm, exist := t.terminals[targetID]
278 return arr, fmt.Errorf("unknown target id")
281 for _, tt := range (*tm).terms {
282 arr = append(arr, (*tt).GetConfig())
287 // GetTerminal Return info of a specific terminal
288 func (t *Targets) GetTerminal(targetID, termID string) (*ITERMINAL, error) {
289 tm, exist := t.terminals[targetID]
291 return nil, fmt.Errorf("unknown target id")
293 term, exist := (*tm).terms[termID]
295 return nil, fmt.Errorf("unknown terminal id")
300 // ResolveTerminalID Complete a Terminal ID (helper for user that can use partial ID value)
301 func (t *Targets) ResolveTerminalID(termID string) (string, error) {
303 return "", fmt.Errorf("unknown terminal id")
307 for _, tm := range t.terminals {
308 for tid := range tm.terms {
309 if strings.HasPrefix(tid, termID) {
310 match = append(match, tid)
317 } else if len(match) == 0 {
318 return termID, fmt.Errorf("Unknown id")
320 return termID, fmt.Errorf("Multiple IDs found %v", match)
323 // CreateUpdateTerminal Create or Update a target terminal definition
324 func (t *Targets) CreateUpdateTerminal(targetID string, tmCfg xsapiv1.TerminalConfig, initial bool) (*xsapiv1.TerminalConfig, error) {
326 var term *xsapiv1.TerminalConfig
328 iTerm, err := t.GetTerminal(targetID, tmCfg.ID)
329 if err != nil && strings.Contains(err.Error(), "unknown target") {
334 // Update terminal config
335 term = (*iTerm).UpdateConfig(tmCfg)
337 // Auto create a new terminal when needed
339 if term, err = t.terminals[targetID].New(tmCfg, targetID); err != nil {
344 term.Status = xsapiv1.StatusTermEnable
346 // Save config on disk
348 if err := t.SaveConfig(); err != nil {
356 // DeleteTerminal Delete a target terminal definition
357 func (t *Targets) DeleteTerminal(targetID, termID string) (*xsapiv1.TerminalConfig, error) {
358 terms, exist := t.terminals[targetID]
360 return nil, fmt.Errorf("unknown target id")
363 term, err := (*terms).Free(termID)
368 // Save config on disk
369 if err := t.SaveConfig(); err != nil {
376 // OpenTerminal Open a target terminal
377 func (t *Targets) OpenTerminal(targetID, termID string, sock *socketio.Socket, sessID string) (*xsapiv1.TerminalConfig, error) {
378 terms, exist := t.terminals[targetID]
380 return nil, fmt.Errorf("unknown target id")
382 return (*terms).Open(termID, sock, sessID)
385 // CloseTerminal Close a target terminal
386 func (t *Targets) CloseTerminal(targetID, termID string) (*xsapiv1.TerminalConfig, error) {
387 terms, exist := t.terminals[targetID]
389 return nil, fmt.Errorf("unknown target id")
391 return (*terms).Close(termID)
394 // ResizeTerminal Set size (row+col) of a target terminal
395 func (t *Targets) ResizeTerminal(targetID, termID string, cols, rows uint16) (*xsapiv1.TerminalConfig, error) {
396 terms, exist := t.terminals[targetID]
398 return nil, fmt.Errorf("unknown target id")
400 return (*terms).Resize(termID, cols, rows)
403 // SignalTerminal Send a signal to a target terminal
404 func (t *Targets) SignalTerminal(targetID, termID, sigNum string) error {
405 terms, exist := t.terminals[targetID]
407 return fmt.Errorf("unknown target id")
409 return (*terms).Signal(termID, sigNum)
416 // Use XML format and not json to be able to save/load all fields including
417 // ones that are masked in json (IOW defined with `json:"-"`)
418 type xmlTargets struct {
419 XMLName xml.Name `xml:"targets"`
420 Version string `xml:"version,attr"`
421 Targets []xsapiv1.TargetConfig `xml:"targets"`
424 // targetsConfigRead reads targets config from disk
425 func targetsConfigRead(file string, targets *[]xsapiv1.TargetConfig) error {
426 if !common.Exists(file) {
427 return fmt.Errorf("No target config file found (%s)", file)
431 defer ffMutex.Unlock()
433 fd, err := os.Open(file)
440 err = xml.NewDecoder(fd).Decode(&data)
442 *targets = data.Targets
447 // targetsConfigWrite writes targets config on disk
448 func targetsConfigWrite(file string, targets []xsapiv1.TargetConfig) error {
450 defer ffMutex.Unlock()
452 fd, err := os.OpenFile(file, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
463 enc := xml.NewEncoder(fd)
465 return enc.Encode(data)