gopkg.in/dedis/onet.v2@v2.0.0-20181115163211-c8f3724038a7/simul/platform/platform.go (about) 1 // Package platform contains interface and implementation to run onet code 2 // amongst multiple platforms. Such implementations include Localhost (run your 3 // test locally) and Deterlab (similar to emulab). 4 package platform 5 6 import ( 7 "bufio" 8 "bytes" 9 "errors" 10 "fmt" 11 "go/build" 12 "os" 13 "strconv" 14 "strings" 15 "time" 16 17 "sync" 18 19 "os/exec" 20 21 "io/ioutil" 22 23 "github.com/BurntSushi/toml" 24 "gopkg.in/dedis/onet.v2/app" 25 "gopkg.in/dedis/onet.v2/log" 26 ) 27 28 // The Life of a simulation: 29 // 30 // 1. Configure 31 // * read configuration 32 // * compile eventual files 33 // 2. Build 34 // * builds all files 35 // * eventually for different platforms 36 // 3. Cleanup 37 // * send killall to applications 38 // 4. Deploy 39 // * make sure the environment is up and running 40 // * copy files 41 // 5. Start 42 // * start all logservers 43 // * start all nodes 44 // * start all clients 45 // 6. Wait 46 // * wait for the applications to finish 47 48 // Platform interface that has to be implemented to add another simulation- 49 // platform. 50 type Platform interface { 51 // Does the initial configuration of all structures needed for the platform 52 Configure(*Config) 53 // Build builds all necessary binaries 54 Build(build string, arg ...string) error 55 // Makes sure that there is no part of the application still running 56 Cleanup() error 57 // Copies the binaries to the appropriate directory/machines, together with 58 // the necessary configuration. RunConfig is a simple string that should 59 // be copied as 'app.toml' to the directory where the app resides 60 Deploy(*RunConfig) error 61 // Starts the application and returns - non-blocking! 62 Start(args ...string) error 63 // Waits for the application to quit 64 Wait() error 65 } 66 67 // Config is passed to Platform.Config and prepares the platform for 68 // specific system-wide configurations 69 type Config struct { 70 // string denoting the group used for simulations 71 // XXX find ways to remove that "one suite" assumption 72 Suite string 73 MonitorPort int 74 Debug int 75 } 76 77 var deterlab = "deterlab" 78 var localhost = "localhost" 79 var mininet = "mininet" 80 81 // NewPlatform returns the appropriate platform 82 // [deterlab,localhost] 83 func NewPlatform(t string) Platform { 84 var p Platform 85 switch t { 86 case deterlab: 87 p = &Deterlab{} 88 case localhost: 89 p = &Localhost{} 90 case mininet: 91 p = &MiniNet{} 92 _, err := os.Stat("server_list") 93 if os.IsNotExist(err) { 94 path := build.Default.GOPATH + "/src/gopkg.in/dedis/onet.v2/simul/platform/mininet/" 95 var command string 96 if app.InputYN(true, "Do you want to run mininet on ICCluster?") { 97 command = path + "setup_iccluster.sh" 98 } else { 99 command = path + "setup_servers.sh" 100 } 101 numbers := app.Input("server1 server2 server3", "Please enter the space separated numbers of the servers") 102 split := strings.Split(numbers, " ") 103 cmd := exec.Command(command, split...) 104 out, err := cmd.CombinedOutput() 105 if err != nil { 106 log.Error(err) 107 } 108 log.Lvl1(string(out)) 109 } else { 110 log.Lvl1("Using existing 'server_list'-file") 111 if log.DebugVisible() > 1 { 112 sl, err := ioutil.ReadFile("server_list") 113 log.ErrFatal(err) 114 servers := strings.Replace(string(sl), "\n", " ", -1) 115 log.Lvl2("Server_list is: ", servers) 116 } 117 } 118 } 119 return p 120 } 121 122 // ReadRunFile reads from a configuration-file for a run. The configuration-file has the 123 // following syntax: 124 // Name1 = value1 125 // Name2 = value2 126 // [empty line] 127 // n1, n2, n3, n4 128 // v11, v12, v13, v14 129 // v21, v22, v23, v24 130 // 131 // The Name1...Namen are global configuration-options. 132 // n1..nn are configuration-options for one run 133 // Both the global and the run-configuration are copied to both 134 // the platform and the app-configuration. 135 func ReadRunFile(p Platform, filename string) []*RunConfig { 136 var runconfigs []*RunConfig 137 masterConfig := NewRunConfig() 138 log.Lvl3("Reading file", filename) 139 140 file, err := os.Open(filename) 141 defer func() { 142 if err := file.Close(); err != nil { 143 log.Error("Couldn' close", file.Name()) 144 } 145 }() 146 if err != nil { 147 log.Fatal("Couldn't open file", filename, err) 148 } 149 150 // Decoding of the first part of the run config file 151 // where the config wont change for the whole set of the simulation's tests 152 scanner := bufio.NewScanner(file) 153 for scanner.Scan() { 154 text := scanner.Text() 155 log.Lvl3("Decoding", text) 156 // end of the first part 157 if text == "" { 158 break 159 } 160 if text[0] == '#' { 161 continue 162 } 163 164 // checking if format is good 165 vals := strings.Split(text, "=") 166 if len(vals) != 2 { 167 log.Fatal("Simulation file:", filename, " is not properly formatted ( key = value )") 168 } 169 // fill in the general config 170 masterConfig.Put(strings.TrimSpace(vals[0]), strings.TrimSpace(vals[1])) 171 // also put it in platform 172 if _, err := toml.Decode(text, p); err != nil { 173 log.Error("Error decoding", text) 174 } 175 log.Lvlf5("Platform is now %+v", p) 176 } 177 178 for scanner.Scan() { 179 if scanner.Text() != "" && scanner.Text()[0] != '#' { 180 break 181 } 182 } 183 args := strings.Split(scanner.Text(), ",") 184 for scanner.Scan() { 185 if scanner.Text()[0] == '#' { 186 continue 187 } 188 rc := masterConfig.Clone() 189 // put each individual test configs 190 for i, value := range strings.Split(scanner.Text(), ",") { 191 rc.Put(strings.TrimSpace(args[i]), strings.TrimSpace(value)) 192 } 193 runconfigs = append(runconfigs, rc) 194 } 195 196 return runconfigs 197 } 198 199 // RunConfig is a struct that represent the configuration to apply for one "test" 200 // Note: a "simulation" is a set of "tests" 201 type RunConfig struct { 202 fields map[string]string 203 sync.RWMutex 204 } 205 206 // NewRunConfig returns an initialised config to be used for reading 207 // in runconfig-files 208 func NewRunConfig() *RunConfig { 209 rc := new(RunConfig) 210 rc.fields = make(map[string]string) 211 return rc 212 } 213 214 // One problem for now is RunConfig read also the ' " ' char (34 ASCII) 215 // and thus when doing Get(), also return the value enclosed by ' " ' 216 // One fix is to each time we Get(), automatically delete those chars 217 var replacer = strings.NewReplacer("\"", "", "'", "") 218 219 // Get returns the associated value of the field in the config 220 func (r *RunConfig) Get(field string) string { 221 r.RLock() 222 defer r.RUnlock() 223 return replacer.Replace(r.fields[strings.ToLower(field)]) 224 } 225 226 // Delete a field from the runconfig (delete for example Simulation which we 227 // dont care in the final csv) 228 func (r *RunConfig) Delete(field string) { 229 r.Lock() 230 defer r.Unlock() 231 delete(r.fields, field) 232 } 233 234 // ErrorFieldNotPresent signals that a field is not in the RunConfig. 235 var ErrorFieldNotPresent = errors.New("field not present") 236 237 // GetInt returns the integer of the field, or error if not defined 238 func (r *RunConfig) GetInt(field string) (int, error) { 239 val := r.Get(field) 240 if val == "" { 241 return 0, ErrorFieldNotPresent 242 } 243 ret, err := strconv.Atoi(val) 244 return ret, err 245 } 246 247 // GetDuration returns the field parsed as a duration, or error if a parse error occurs. 248 func (r *RunConfig) GetDuration(field string) (time.Duration, error) { 249 val := r.Get(field) 250 if val == "" { 251 return 0, ErrorFieldNotPresent 252 } 253 return time.ParseDuration(val) 254 } 255 256 // Put inserts a new field - value relationship 257 func (r *RunConfig) Put(field, value string) { 258 r.Lock() 259 defer r.Unlock() 260 r.fields[strings.ToLower(field)] = value 261 } 262 263 // Toml returns this config as bytes in a Toml format 264 func (r *RunConfig) Toml() []byte { 265 r.RLock() 266 defer r.RUnlock() 267 var buf bytes.Buffer 268 for k, v := range r.fields { 269 fmt.Fprintf(&buf, "%s = %s\n", k, v) 270 } 271 return buf.Bytes() 272 } 273 274 // Map returns this config as a Map 275 func (r *RunConfig) Map() map[string]string { 276 r.RLock() 277 defer r.RUnlock() 278 tomap := make(map[string]string) 279 for k := range r.fields { 280 tomap[k] = r.Get(k) 281 } 282 return tomap 283 } 284 285 // Clone this runconfig so it has all fields-value relationship already present 286 func (r *RunConfig) Clone() *RunConfig { 287 r.RLock() 288 defer r.RUnlock() 289 rc := NewRunConfig() 290 for k, v := range r.fields { 291 rc.fields[k] = v 292 } 293 return rc 294 }