blob: 049de2d0e4b8d0a730fe7d1054461fde47c197c2 [file] [log] [blame]
Brian O'Connor6a37ea92017-08-03 22:45:59 -07001// Copyright 2016 Open Networking Foundation
David K. Bainbridgedf9df632016-07-07 18:47:46 -07002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
David K. Bainbridgef0da8732016-06-01 16:15:37 -070014package main
15
16import (
David K. Bainbridge528b3182017-01-23 08:51:59 -080017 "flag"
David K. Bainbridgef0da8732016-06-01 16:15:37 -070018 "fmt"
David K. Bainbridgea9c2e0a2016-07-01 18:33:50 -070019 "github.com/Sirupsen/logrus"
David K. Bainbridgef0da8732016-06-01 16:15:37 -070020 "github.com/gorilla/mux"
21 "github.com/kelseyhightower/envconfig"
David K. Bainbridgef0da8732016-06-01 16:15:37 -070022 "net/http"
David K. Bainbridge528b3182017-01-23 08:51:59 -080023 "os"
David K. Bainbridgef0da8732016-06-01 16:15:37 -070024)
25
David K. Bainbridge528b3182017-01-23 08:51:59 -080026const appName = "PROVISION"
27
David K. Bainbridgef0da8732016-06-01 16:15:37 -070028type Config struct {
David K. Bainbridge528b3182017-01-23 08:51:59 -080029 Port int `default:"4243" desc:"port on which to listen for requests"`
30 Listen string `default:"0.0.0.0" desc:"IP on which to listen for requests"`
31 RoleSelectorURL string `default:"" envconfig:"ROLE_SELECTOR_URL" desc:"connection string to query role for device"`
32 DefaultRole string `default:"compute-node" envconfig:"DEFAULT_ROLE" desc:"default role for device"`
33 Script string `default:"do-ansible" desc:"default script to execute to provision device"`
34 StorageURL string `default:"memory:" envconfig:"STORAGE_URL" desc:"connection string to persistence implementation"`
35 NumberOfWorkers int `default:"5" envconfig:"NUMBER_OF_WORKERS" desc:"number of concurrent provisioning workers"`
36 LogLevel string `default:"warning" envconfig:"LOG_LEVEL" desc:"detail level for logging"`
37 LogFormat string `default:"text" envconfig:"LOG_FORMAT" desc:"log output format, text or json"`
David K. Bainbridgef0da8732016-06-01 16:15:37 -070038}
39
40type Context struct {
41 config Config
42 storage Storage
43 workers []Worker
44 dispatcher *Dispatcher
45}
46
David K. Bainbridgea9c2e0a2016-07-01 18:33:50 -070047var log = logrus.New()
David K. Bainbridge528b3182017-01-23 08:51:59 -080048var appFlags = flag.NewFlagSet("", flag.ContinueOnError)
David K. Bainbridgea9c2e0a2016-07-01 18:33:50 -070049
David K. Bainbridgef0da8732016-06-01 16:15:37 -070050func main() {
51 context := &Context{}
52
David K. Bainbridge528b3182017-01-23 08:51:59 -080053 appFlags.Usage = func() {
54 envconfig.Usage(appName, &(context.config))
55 }
56 if err := appFlags.Parse(os.Args[1:]); err != nil {
57 if err != flag.ErrHelp {
58 os.Exit(1)
59 } else {
60 return
61 }
62 }
63 err := envconfig.Process(appName, &(context.config))
David K. Bainbridgef0da8732016-06-01 16:15:37 -070064 if err != nil {
David K. Bainbridgea9c2e0a2016-07-01 18:33:50 -070065 log.Fatalf("[ERRO] Unable to parse configuration options : %s", err)
David K. Bainbridgef0da8732016-06-01 16:15:37 -070066 }
67
David K. Bainbridgea9c2e0a2016-07-01 18:33:50 -070068 switch context.config.LogFormat {
69 case "json":
70 log.Formatter = &logrus.JSONFormatter{}
71 default:
72 log.Formatter = &logrus.TextFormatter{
73 FullTimestamp: true,
74 ForceColors: true,
75 }
76 }
77
78 level, err := logrus.ParseLevel(context.config.LogLevel)
79 if err != nil {
80 level = logrus.WarnLevel
81 }
82 log.Level = level
83
84 log.Infof(`Configuration:
David K. Bainbridgeca68f062016-10-27 11:04:33 -070085 LISTEN: %s
86 PORT: %d
87 ROLE_SELECTION_URL: %s
88 DEFAULT_ROLE: %s
89 SCRIPT: %s
90 STORAGE_URL: %s
91 NUMBER_OF_WORERS: %d
92 LOG_LEVEL: %s
93 LOG_FORMAT: %s`,
David K. Bainbridged86d96d2016-06-01 17:28:46 -070094 context.config.Listen, context.config.Port, context.config.RoleSelectorURL,
David K. Bainbridgea9c2e0a2016-07-01 18:33:50 -070095 context.config.DefaultRole, context.config.Script, context.config.StorageURL,
David K. Bainbridgeca68f062016-10-27 11:04:33 -070096 context.config.NumberOfWorkers,
David K. Bainbridgea9c2e0a2016-07-01 18:33:50 -070097 context.config.LogLevel, context.config.LogFormat)
David K. Bainbridgef0da8732016-06-01 16:15:37 -070098
David K. Bainbridge546cdc32016-06-29 15:30:22 -070099 context.storage, err = NewStorage(context.config.StorageURL)
100 if err != nil {
101 log.Fatalf("[error] Unable to connect to specified storage '%s' : %s",
102 context.config.StorageURL, err)
103 }
David K. Bainbridgef0da8732016-06-01 16:15:37 -0700104
105 router := mux.NewRouter()
106 router.HandleFunc("/provision/", context.ProvisionRequestHandler).Methods("POST")
107 router.HandleFunc("/provision/", context.ListRequestsHandler).Methods("GET")
108 router.HandleFunc("/provision/{nodeid}", context.QueryStatusHandler).Methods("GET")
David K. Bainbridge068e87d2016-06-30 13:53:19 -0700109 router.HandleFunc("/provision/{nodeid}", context.DeleteStatusHandler).Methods("DELETE")
David K. Bainbridgef0da8732016-06-01 16:15:37 -0700110 http.Handle("/", router)
111
112 // Start the dispatcher and workers
David K. Bainbridgeca68f062016-10-27 11:04:33 -0700113 context.dispatcher = NewDispatcher(context.config.NumberOfWorkers, context.storage)
David K. Bainbridgef0da8732016-06-01 16:15:37 -0700114 context.dispatcher.Start()
115
116 http.ListenAndServe(fmt.Sprintf("%s:%d", context.config.Listen, context.config.Port), nil)
117}