github.com/unirita/cuto@v0.9.8-0.20160830082821-aa6652f877b7/master/jobnet/network.go (about) 1 // Copyright 2015 unirita Inc. 2 // Created 2015/04/10 honda 3 4 package jobnet 5 6 import ( 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "strconv" 12 "sync" 13 14 "github.com/unirita/cuto/console" 15 "github.com/unirita/cuto/db" 16 "github.com/unirita/cuto/db/tx" 17 "github.com/unirita/cuto/log" 18 "github.com/unirita/cuto/master/config" 19 "github.com/unirita/cuto/master/jobnet/parser" 20 "github.com/unirita/cuto/message" 21 "github.com/unirita/cuto/util" 22 ) 23 24 // ジョブネット全体を表す構造体 25 type Network struct { 26 ID int // ジョブネットワークID。 27 Name string // ジョブネットワーク名。 28 Start Element // スタートイベントのノード。 29 End Element // エンドイベントのノード。 30 MasterPath string // ジョブネットワークファイルパス。 31 JobExPath string // 拡張ジョブ定義ファイルパス。 32 elements map[string]Element // ジョブネットワークの構成要素Map。 33 Result *tx.ResultMap // 実行結果情報。 34 globalLock *util.LockHandle // マスタ間ロックハンドル 35 localMutex sync.Mutex // ゴルーチン間のミューテックス 36 } 37 38 // cuto masterが使用するミューテックス名。 39 const lock_name string = "Unirita_CutoMaster.lock" 40 41 // Network構造体のコンストラクタ関数 42 // 43 // param : name ジョブネットワーク名。 44 // 45 // return : ジョブネットワーク構造体。 46 func NewNetwork(name string) (*Network, error) { 47 nwk := new(Network) 48 nwk.Name = name 49 nwk.elements = make(map[string]Element) 50 filePrefix := filepath.Join(config.Dir.JobnetDir, name) 51 nwk.MasterPath = filePrefix + ".bpmn" 52 nwk.JobExPath = filePrefix + ".csv" 53 54 var err error 55 nwk.globalLock, err = util.InitLock(lock_name) 56 if err != nil { 57 return nil, err 58 } 59 return nwk, err 60 } 61 62 // ネットワーク名nameを元にネットワーク定義ファイルをロードし、Network構造体のオブジェクトを返す。 63 // 64 // param : name ジョブネットワーク名。 65 // 66 // return : ジョブネットワーク構造体。 67 func LoadNetwork(name string) *Network { 68 nwk, err := NewNetwork(name) 69 if err != nil { 70 console.Display("CTM019E", err) 71 return nil 72 } 73 74 file, err := os.Open(nwk.MasterPath) 75 if err != nil { 76 console.Display("CTM010E", nwk.MasterPath) 77 log.Error(err) 78 return nil 79 } 80 defer file.Close() 81 82 err = nwk.LoadElements(file) 83 if err != nil { 84 console.Display("CTM011E", nwk.MasterPath, err) 85 return nil 86 } 87 88 return nwk 89 } 90 91 // io.Readerからネットワーク定義を読み込み、n.Start/n.End/n.elementsへ値をセットする。 92 // 93 // param : r Reader。 94 // 95 // return : エラー情報。 96 func (n *Network) LoadElements(r io.Reader) error { 97 proc, err := parser.ParseNetwork(r) 98 if err != nil { 99 return err 100 } 101 102 return n.setElements(proc) 103 } 104 105 // BPMNパース結果のProcess構造体からネットワークの各要素を取得し、セットする。 106 func (n *Network) setElements(proc *parser.Process) error { 107 for _, t := range proc.Task { 108 if _, exists := n.elements[t.ID]; exists { 109 return fmt.Errorf("Element[id = %s] duplicated.", t.ID) 110 } 111 112 var err error 113 n.elements[t.ID], err = NewJob(t.ID, t.Name, n) 114 if err != nil { 115 return err 116 } 117 } 118 119 for _, g := range proc.Gateway { 120 if _, exists := n.elements[g.ID]; exists { 121 return fmt.Errorf("Element[id = %s] duplicated.", g.ID) 122 } 123 n.elements[g.ID] = NewGateway(g.ID) 124 } 125 126 sid := proc.Start[0].ID 127 eid := proc.End[0].ID 128 129 for _, f := range proc.Flow { 130 switch { 131 case f.From == sid: 132 if n.Start != nil { 133 return fmt.Errorf("StartEvent cannot connect with over 1 element.") 134 } 135 136 if f.To == eid { 137 return fmt.Errorf("Jobnet is empty.") 138 } 139 140 var ok bool 141 n.Start, ok = n.elements[f.To] 142 if !ok { 143 return fmt.Errorf("StartEvent connects with imaginary element[id = %s].", f.To) 144 } 145 case f.To == eid: 146 if n.End != nil { 147 return fmt.Errorf("EndEvent cannot connect with over 1 element.") 148 } 149 150 var ok bool 151 n.End, ok = n.elements[f.From] 152 if !ok { 153 return fmt.Errorf("EndEvent connects with imaginary element[id = %s].", f.From) 154 } 155 default: 156 from, ok := n.elements[f.From] 157 if !ok { 158 return fmt.Errorf("There is a sequenceFlow which refers imaginary element[id = %s].", f.From) 159 } 160 to, ok := n.elements[f.To] 161 if !ok { 162 return fmt.Errorf("There is a sequenceFlow which refers imaginary element[id = %s].", f.To) 163 } 164 if err := from.AddNext(to); err != nil { 165 return err 166 } 167 } 168 } 169 170 return nil 171 } 172 173 // JobExファイルをロードし、ネットワーク内のジョブへ拡張ジョブ定義をセットする。 174 // 175 // return : エラー情報。 176 func (n *Network) LoadJobEx() error { 177 jobEx, err := parser.ParseJobExFile(n.JobExPath) 178 if err != nil { 179 return err 180 } 181 n.setJobEx(jobEx) 182 183 return nil 184 } 185 186 // ネットワーク内のジョブへ拡張ジョブ定義のパース結果をセットする。 187 func (n *Network) setJobEx(m map[string]*parser.JobEx) { 188 for _, e := range n.elements { 189 switch e.(type) { 190 case *Job: 191 j := e.(*Job) 192 if je, ok := m[j.Name]; ok { 193 j.Node = je.Node 194 j.Port = je.Port 195 j.FilePath = je.FilePath 196 j.Param = je.Param 197 j.Env = je.Env 198 j.Workspace = je.Workspace 199 j.WrnRC = je.WrnRC 200 j.WrnPtn = je.WrnPtn 201 j.ErrRC = je.ErrRC 202 j.ErrPtn = je.ErrPtn 203 j.Timeout = je.TimeoutMin * 60 204 j.SecondaryNode = je.SecondaryNode 205 j.SecondaryPort = je.SecondaryPort 206 } 207 j.SetDefaultEx() 208 default: 209 continue 210 } 211 } 212 } 213 214 // 実行フローのエラー検出を行う。 215 // 216 // return : エラー情報。 217 func (n *Network) DetectFlowError() error { 218 if n.Start == nil { 219 return fmt.Errorf("There is no element which connects with startEvent.") 220 } 221 if n.End == nil { 222 return fmt.Errorf("There is no element which connects with endEvent.") 223 } 224 225 novisit := make(map[string]Element) 226 for k, v := range n.elements { 227 novisit[k] = v 228 } 229 230 err := n.scanFlow(n.Start, novisit) 231 if err != nil { 232 return err 233 } 234 235 if len(novisit) > 0 { 236 return fmt.Errorf("Isolated element is detected.") 237 } 238 239 return nil 240 } 241 242 func (n *Network) scanFlow(e Element, novisit map[string]Element) error { 243 delete(novisit, e.ID()) 244 if e == n.End { 245 if e.HasNext() { 246 return fmt.Errorf("Element which connects with endEvent cannot connect with another element.") 247 } 248 return nil 249 } else if !e.HasNext() { 250 return fmt.Errorf("Element[id = %s] cannot terminate network because it is not a endEvent.", e.ID) 251 } 252 253 switch e.(type) { 254 case *Job: 255 j := e.(*Job) 256 return n.scanFlow(j.Next, novisit) 257 case *Gateway: 258 g := e.(*Gateway) 259 260 if len(g.Nexts) == 1 { 261 return n.scanFlow(g.Nexts[0], novisit) 262 } else { 263 var jct Element = nil 264 for _, branch := range g.Nexts { 265 bind, err := n.scanFlowParallel(branch, novisit) 266 if err != nil { 267 return err 268 } 269 270 if jct == nil { 271 jct = bind 272 } else if jct != bind { 273 return fmt.Errorf("Cannot nest branches.") 274 } 275 } 276 277 return n.scanFlow(jct, novisit) 278 } 279 default: 280 return fmt.Errorf("Irregal element was detected.") 281 } 282 } 283 284 func (n *Network) scanFlowParallel(e Element, novisit map[string]Element) (Element, error) { 285 delete(novisit, e.ID()) 286 switch e.(type) { 287 case *Job: 288 j := e.(*Job) 289 if j.Next == nil { 290 return nil, fmt.Errorf("EndEvent cannot connect with branch.") 291 } 292 return n.scanFlowParallel(j.Next, novisit) 293 case *Gateway: 294 return e, nil 295 default: 296 return nil, fmt.Errorf("Irregal element was detected.") 297 } 298 } 299 300 // ネットワークを実行する。 301 // 302 // return : エラー情報。 303 func (n *Network) Run() error { 304 if n.Start == nil { 305 return fmt.Errorf("Start element of network is nil.") 306 } 307 308 err := n.start() 309 if err != nil { 310 console.Display("CTM019E", err) 311 return err 312 } 313 console.Display("CTM012I", n.Name, n.ID) 314 315 return n.runNodes() 316 } 317 318 // ネットワークをリランする。 319 // 320 // return : エラー情報。 321 func (n *Network) Rerun() error { 322 if n.Start == nil { 323 return fmt.Errorf("Start element of network is nil.") 324 } 325 326 err := n.resume() 327 if err != nil { 328 console.Display("CTM019E", err) 329 return err 330 } 331 332 prePID := n.Result.JobnetResult.PID 333 if util.IsProcessExists(prePID) { 334 return fmt.Errorf("JOBNETWORK [%d] still running.", n.ID) 335 } 336 337 console.Display("CTM012I", n.Name, n.ID) 338 n.setIsRerunJob() 339 340 return n.runNodes() 341 } 342 343 func (n *Network) runNodes() error { 344 current := n.Start 345 for { 346 next, err := current.Execute() 347 if err != nil { 348 return n.end(err) 349 } 350 if current == n.End { 351 return n.end(nil) 352 } else if next == nil { 353 err := fmt.Errorf("Element[id = %s] cannot terminate network because it is not a endEvent.", current.ID()) 354 return n.end(err) 355 } 356 current = next 357 } 358 panic("Not reached.") 359 } 360 361 func (n *Network) setIsRerunJob() { 362 for _, e := range n.elements { 363 if j, ok := e.(*Job); ok { 364 if _, exists := n.Result.GetJobResults(j.id); exists { 365 j.IsRerunJob = true 366 } 367 } 368 } 369 } 370 371 // ジョブネットワークの開始処理 372 func (n *Network) start() error { 373 timeout := config.Job.DefaultTimeoutMin * 60 * 1000 374 if timeout <= 0 { 375 timeout = 60000 376 } 377 378 err := n.globalLock.Lock(timeout) 379 if err != nil { 380 if err != util.ErrBusy { 381 return err 382 } 383 return fmt.Errorf("Lock Timeout.") 384 } 385 defer n.globalLock.Unlock() 386 387 n.Result, err = tx.StartJobNetwork(n.Name, config.DB.DBFile) 388 if err != nil { 389 return err 390 } 391 392 n.ID = n.Result.JobnetResult.ID 393 message.AddSysValue(`JOBNET`, `ID`, strconv.Itoa(n.ID)) 394 message.AddSysValue(`JOBNET`, `SD`, n.Result.JobnetResult.StartDate) 395 396 return nil 397 } 398 399 // ジョブネットワークの再実行開始処理 400 func (n *Network) resume() error { 401 timeout := config.Job.DefaultTimeoutMin * 60 * 1000 402 if timeout <= 0 { 403 timeout = 60000 404 } 405 406 err := n.globalLock.Lock(timeout) 407 if err != nil { 408 if err != util.ErrBusy { 409 return err 410 } 411 return fmt.Errorf("Lock Timeout.") 412 } 413 defer n.globalLock.Unlock() 414 415 n.Result, err = tx.ResumeJobNetwork(n.ID, config.DB.DBFile) 416 if err != nil { 417 return err 418 } 419 420 message.AddSysValue(`JOBNET`, `ID`, strconv.Itoa(n.ID)) 421 message.AddSysValue(`JOBNET`, `SD`, n.Result.JobnetResult.StartDate) 422 423 return nil 424 } 425 426 // ジョブネットワークの終了処理 427 func (n *Network) end(err error) error { 428 if err != nil { 429 n.Result.EndJobNetwork(db.ABNORMAL, err.Error()) 430 } else { 431 n.Result.EndJobNetwork(db.NORMAL, "") 432 } 433 return err 434 } 435 436 // 終了処理を行う。 437 func (n *Network) Terminate() { 438 n.globalLock.TermLock() 439 }