forked from srl-labs/containerlab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile.go
288 lines (260 loc) · 8.01 KB
/
file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package clab
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"text/template"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)
// TopoFile type is a struct which defines parameters of the topology file
type TopoFile struct {
fullName string // file name with extension
name string // file name without extension
}
// GetTopology parses the topology file into c.Conf structure
// as well as populates the TopoFile structure with the topology file related information
func (c *CLab) GetTopology(topo string) error {
log.Infof("Getting topology information from %s file...", topo)
yamlFile, err := ioutil.ReadFile(topo)
if err != nil {
return err
}
log.Debug(fmt.Sprintf("Topology file contents:\n%s\n", yamlFile))
err = yaml.UnmarshalStrict(yamlFile, c.Config)
if err != nil {
return err
}
s := strings.Split(topo, "/")
file := s[len(s)-1]
filename := strings.Split(file, ".")
c.TopoFile = &TopoFile{
fullName: file,
name: filename[0],
}
return nil
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
log.Debug(info)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// CopyFile copies a file from src to dst. If src and dst files exist, and are
// the same, then return success. Otherwise, copy the file contents from src to dst.
func copyFile(src, dst string) (err error) {
sfi, err := os.Stat(src)
if err != nil {
return err
}
if !sfi.Mode().IsRegular() {
// cannot copy non-regular files (e.g., directories,
// symlinks, devices, etc.)
return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
}
dfi, err := os.Stat(dst)
if err != nil {
if !os.IsNotExist(err) {
return err
}
} else {
if !(dfi.Mode().IsRegular()) {
return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
}
if os.SameFile(sfi, dfi) {
return
}
}
return copyFileContents(src, dst)
}
// copyFileContents copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFileContents(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return
}
defer func() {
cerr := out.Close()
if err == nil {
err = cerr
}
}()
if _, err = io.Copy(out, in); err != nil {
return
}
err = out.Sync()
return
}
func createFile(file, content string) {
var f *os.File
f, err := os.Create(file)
if err != nil {
panic(err)
}
defer f.Close()
if _, err := f.WriteString(content + "\n"); err != nil {
panic(err)
}
}
// CreateDirectory creates a directory by a path with a mode/permission specified by perm.
// If directory exists, the function does not do anything.
func CreateDirectory(path string, perm os.FileMode) {
if _, err := os.Stat(path); os.IsNotExist(err) {
os.Mkdir(path, perm)
}
}
// CreateNodeDirStructure create the directory structure and files for the lab nodes
func (c *CLab) CreateNodeDirStructure(node *Node) (err error) {
c.m.RLock()
defer c.m.RUnlock()
// create node directory in the lab directory
// skip creation of node directory for linux/bridge kinds
// since they don't keep any state normally
if node.Kind != "linux" && node.Kind != "bridge" {
CreateDirectory(node.LabDir, 0777)
}
switch node.Kind {
case "srl":
log.Debugf("Creating directory structure for SRL container: %s", node.ShortName)
var src string
var dst string
// copy license file to node specific directory in lab
src = node.License
dst = path.Join(node.LabDir, "license.key")
if err = copyFile(src, dst); err != nil {
return fmt.Errorf("CopyFile src %s -> dst %s failed %v", src, dst, err)
}
log.Debugf("CopyFile src %s -> dst %s succeeded", src, dst)
// generate SRL topology file
err = generateSRLTopologyFile(node.Topology, node.LabDir, node.Index)
if err != nil {
return err
}
// generate a config file if the destination does not exist
// if the node has a `config:` statement, the file specified in that section
// will be used as a template in nodeGenerateConfig()
CreateDirectory(path.Join(node.LabDir, "config"), 0777)
dst = path.Join(node.LabDir, "config", "config.json")
if !fileExists(dst) {
err = node.generateConfig(dst)
if err != nil {
log.Errorf("node=%s, failed to generate config: %v", node.ShortName, err)
}
} else {
log.Debugf("Config File Exists for node %s", node.ShortName)
}
// copy env config to node specific directory in lab
src = "/etc/containerlab/templates/srl/srl_env.conf"
dst = node.LabDir + "/" + "srlinux.conf"
err = copyFile(src, dst)
if err != nil {
return fmt.Errorf("CopyFile src %s -> dst %s failed %v", src, dst, err)
}
log.Debugf("CopyFile src %s -> dst %s succeeded\n", src, dst)
case "linux":
case "ceos":
// generate config directory
CreateDirectory(path.Join(node.LabDir, "flash"), 0777)
cfg := path.Join(node.LabDir, "flash", "startup-config")
node.ResConfig = cfg
if !fileExists(cfg) {
err = node.generateConfig(cfg)
if err != nil {
log.Errorf("node=%s, failed to generate config: %v", node.ShortName, err)
}
} else {
log.Debugf("Config file exists for node %s", node.ShortName)
}
case "crpd":
// create config and logs directory that will be bind mounted to crpd
CreateDirectory(path.Join(node.LabDir, "config"), 0777)
CreateDirectory(path.Join(node.LabDir, "log"), 0777)
// copy crpd config from default template or user-provided conf file
cfg := path.Join(node.LabDir, "/config/juniper.conf")
if !fileExists(cfg) {
err = node.generateConfig(cfg)
if err != nil {
log.Errorf("node=%s, failed to generate config: %v", node.ShortName, err)
}
} else {
log.Debugf("Config file exists for node %s", node.ShortName)
}
// copy crpd sshd conf file to crpd node dir
src := "/etc/containerlab/templates/crpd/sshd_config"
dst := node.LabDir + "/config/sshd_config"
err = copyFile(src, dst)
if err != nil {
return fmt.Errorf("file copy [src %s -> dst %s] failed %v", src, dst, err)
}
log.Debugf("CopyFile src %s -> dst %s succeeded\n", src, dst)
if node.License != "" {
// copy license file to node specific lab directory
src = node.License
dst = path.Join(node.LabDir, "/config/license.conf")
if err = copyFile(src, dst); err != nil {
return fmt.Errorf("file copy [src %s -> dst %s] failed %v", src, dst, err)
}
log.Debugf("CopyFile src %s -> dst %s succeeded", src, dst)
}
case "vr-sros":
// create config directory that will be bind mounted to vrnetlab container at / path
CreateDirectory(path.Join(node.LabDir, "tftpboot"), 0777)
if node.License != "" {
// copy license file to node specific lab directory
src := node.License
dst := path.Join(node.LabDir, "/tftpboot/license.txt")
if err = copyFile(src, dst); err != nil {
return fmt.Errorf("file copy [src %s -> dst %s] failed %v", src, dst, err)
}
log.Debugf("CopyFile src %s -> dst %s succeeded", src, dst)
cfg := path.Join(node.LabDir, "tftpboot", "config.txt")
if node.Config != "" {
err = node.generateConfig(cfg)
if err != nil {
log.Errorf("node=%s, failed to generate config: %v", node.ShortName, err)
}
} else {
log.Debugf("Config file exists for node %s", node.ShortName)
}
}
case "bridge":
default:
}
return nil
}
// GenerateConfig generates configuration for the nodes
func (node *Node) generateConfig(dst string) error {
log.Debugf("generating config for node %s from file %s", node.ShortName, node.Config)
tpl, err := template.New(filepath.Base(node.Config)).ParseFiles(node.Config)
if err != nil {
return err
}
dstBytes := new(bytes.Buffer)
err = tpl.Execute(dstBytes, node)
if err != nil {
return err
}
log.Debugf("node '%s' generated config: %s", node.ShortName, dstBytes.String())
f, err := os.Create(dst)
if err != nil {
return err
}
defer f.Close()
_, err = f.Write(dstBytes.Bytes())
return err
}