gopkg.in/dedis/onet.v2@v2.0.0-20181115163211-c8f3724038a7/simul/platform/localhost.go (about) 1 package platform 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "runtime" 8 "strconv" 9 "sync" 10 11 "strings" 12 13 "time" 14 15 "gopkg.in/dedis/onet.v2" 16 "gopkg.in/dedis/onet.v2/app" 17 "gopkg.in/dedis/onet.v2/log" 18 "gopkg.in/dedis/onet.v2/simul/monitor" 19 ) 20 21 // Localhost is responsible for launching the app with the specified number of nodes 22 // directly on your machine, for local testing. 23 24 // Localhost is the platform for launching thee apps locally 25 type Localhost struct { 26 // Need mutex because build.go has a global variable that 27 // is used for multiple experiments 28 sync.Mutex 29 30 // Address of the logger (can be local or not) 31 logger string 32 33 // The simulation to run 34 Simulation string 35 36 // Where is the Localhost package located 37 localDir string 38 // Where to build the executables + 39 // where to read the config file 40 // it will be assembled like LocalDir/RunDir 41 runDir string 42 43 // Debug level 1 - 5 44 debug int 45 46 // The number of servers 47 servers int 48 // All addresses - we use 'localhost1'..'localhostn' to 49 // identify the different cothorities, but when opening the 50 // ports they will be converted to normal 'localhost' 51 addresses []string 52 53 // Whether we started a simulation 54 running bool 55 // WaitGroup for running processes 56 wgRun sync.WaitGroup 57 58 // errors go here: 59 errChan chan error 60 61 // Listening monitor port 62 monitorPort int 63 64 // Suite used for the simulation 65 Suite string 66 67 // SimulationConfig holds all things necessary for the run 68 sc *onet.SimulationConfig 69 70 // PreScript is run before the simulation is started 71 PreScript string 72 73 // RunWait for long simulations 74 RunWait string 75 } 76 77 // Configure various internal variables 78 func (d *Localhost) Configure(pc *Config) { 79 d.Lock() 80 defer d.Unlock() 81 pwd, _ := os.Getwd() 82 d.runDir = pwd + "/build" 83 os.RemoveAll(d.runDir) 84 log.ErrFatal(os.Mkdir(d.runDir, 0770)) 85 d.Suite = pc.Suite 86 d.localDir = pwd 87 d.debug = pc.Debug 88 d.running = false 89 d.monitorPort = pc.MonitorPort 90 if d.Simulation == "" { 91 log.Fatal("No simulation defined in simulation") 92 } 93 log.Lvl3(fmt.Sprintf("Localhost dirs: RunDir %s", d.runDir)) 94 log.Lvl3("Localhost configured ...") 95 } 96 97 // Build does nothing, as we're using our own binary, no need to build 98 func (d *Localhost) Build(build string, arg ...string) error { 99 return nil 100 } 101 102 // Cleanup kills all running cothority-binaryes 103 func (d *Localhost) Cleanup() error { 104 log.Lvl1("Nothing to clean up") 105 return nil 106 } 107 108 // Deploy copies all files to the run-directory 109 func (d *Localhost) Deploy(rc *RunConfig) error { 110 d.Lock() 111 defer d.Unlock() 112 if runtime.GOOS == "darwin" { 113 files, err := exec.Command("ulimit", "-n").Output() 114 if err != nil { 115 return err 116 } 117 filesNbr, err := strconv.Atoi(strings.TrimSpace(string(files))) 118 if err != nil { 119 return err 120 } 121 hosts, _ := strconv.Atoi(rc.Get("hosts")) 122 if filesNbr < hosts*2 { 123 maxfiles := 10000 + hosts*2 124 return fmt.Errorf("Maximum open files is too small. Please run the following command:\n"+ 125 "sudo sysctl -w kern.maxfiles=%d\n"+ 126 "sudo sysctl -w kern.maxfilesperproc=%d\n"+ 127 "ulimit -n %d\n"+ 128 "sudo sysctl -w kern.ipc.somaxconn=2048\n", 129 maxfiles, maxfiles, maxfiles) 130 } 131 } 132 133 // Check for PreScript and copy it to the deploy-dir 134 d.PreScript = rc.Get("PreScript") 135 if d.PreScript != "" { 136 _, err := os.Stat(d.PreScript) 137 if !os.IsNotExist(err) { 138 if err := app.Copy(d.runDir, d.PreScript); err != nil { 139 return err 140 } 141 } 142 } 143 144 d.servers, _ = strconv.Atoi(rc.Get("servers")) 145 log.Lvl2("Localhost: Deploying and writing config-files for", d.servers, "servers") 146 sim, err := onet.NewSimulation(d.Simulation, string(rc.Toml())) 147 if err != nil { 148 return err 149 } 150 d.addresses = make([]string, d.servers) 151 for i := range d.addresses { 152 d.addresses[i] = "127.0.0." + strconv.Itoa(i) 153 } 154 d.sc, err = sim.Setup(d.runDir, d.addresses) 155 if err != nil { 156 return err 157 } 158 d.sc.Config = string(rc.Toml()) 159 if err := d.sc.Save(d.runDir); err != nil { 160 return err 161 } 162 log.Lvl2("Localhost: Done deploying") 163 d.wgRun.Add(d.servers) 164 // add one to the channel length to indicate it's done 165 d.errChan = make(chan error, d.servers+1) 166 return nil 167 } 168 169 // Start will execute one cothority-binary for each server 170 // configured 171 func (d *Localhost) Start(args ...string) error { 172 d.Lock() 173 defer d.Unlock() 174 if err := os.Chdir(d.runDir); err != nil { 175 return err 176 } 177 log.Lvl4("Localhost: chdir into", d.runDir) 178 ex := d.runDir + "/" + d.Simulation 179 d.running = true 180 log.Lvl1("Starting", d.servers, "applications of", ex) 181 time.Sleep(100 * time.Millisecond) 182 183 // If PreScript is defined, run the appropriate script _before_ the simulation. 184 if d.PreScript != "" { 185 out, err := exec.Command("sh", "-c", "./"+d.PreScript+" localhost").CombinedOutput() 186 outStr := strings.TrimRight(string(out), "\n") 187 if err != nil { 188 return fmt.Errorf("error deploying PreScript: " + err.Error() + " " + outStr) 189 } 190 log.Lvl1(outStr) 191 } 192 193 err := monitor.ConnectSink("localhost:" + strconv.Itoa(d.monitorPort)) 194 if err != nil { 195 return err 196 } 197 198 for index := 0; index < d.servers; index++ { 199 log.Lvl3("Starting", index) 200 host := "127.0.0." + strconv.Itoa(index) 201 go func(i int, h string) { 202 log.Lvl3("Localhost: will start host", i, h) 203 err := Simulate(d.Suite, host, d.Simulation, "") 204 if err != nil { 205 log.Error("Error running localhost", h, ":", err) 206 d.errChan <- err 207 } 208 d.wgRun.Done() 209 log.Lvl3("host (index", i, ")", h, "done") 210 }(index, host) 211 } 212 return nil 213 } 214 215 // Wait for all processes to finish 216 func (d *Localhost) Wait() error { 217 d.Lock() 218 defer d.Unlock() 219 log.Lvl3("Waiting for processes to finish") 220 221 wait, err := time.ParseDuration(d.RunWait) 222 if err != nil || wait == 0 { 223 wait = 600 * time.Second 224 err = nil 225 } 226 227 go func() { 228 d.wgRun.Wait() 229 log.Lvl3("WaitGroup is 0") 230 // write to error channel when done: 231 d.errChan <- nil 232 }() 233 234 // if one of the hosts fails, stop waiting and return the error: 235 select { 236 case e := <-d.errChan: 237 log.Lvl3("Finished waiting for hosts:", e) 238 if e != nil { 239 if err := d.Cleanup(); err != nil { 240 log.Error("Couldn't cleanup running instances", 241 err) 242 } 243 err = e 244 } 245 case <-time.After(wait): 246 log.Lvl1("Quitting after waiting", wait) 247 } 248 monitor.EndAndCleanup() 249 log.Lvl2("Processes finished") 250 return err 251 }