initial checking of provisioning container
diff --git a/build.gradle b/build.gradle
index 6c0babf..7e3ea90 100644
--- a/build.gradle
+++ b/build.gradle
@@ -58,6 +58,20 @@
     commandLine "$dockerPath/docker", 'push', "$targetReg/cord-ip-allocator:$targetTag"
 }
 
+task buildProvisionerImage(type: Exec) {
+    commandLine "$dockerPath/docker", 'build', '-t', 'cord-provisioner', './ip-allocator'
+}
+
+task tagProvisionerImage(type: Exec) {
+   dependsOn buildProvisionerImage
+   commandLine "$dockerPath/docker", 'tag', 'cord-provisioner', "$targetReg/cord-provisioner:$targetTag"
+}
+
+task publishProvisionerImage(type: Exec) {
+    dependsOn tagProvisionerImage
+    commandLine "$dockerPath/docker", 'push', "$targetReg/cord-provisioner:$targetTag"
+}
+
 task buildAutomationImage(type: Exec) {
     commandLine "$dockerPath/docker", 'build', '-t', "cord-maas-automation", "-f", "./automation/Dockerfile", "./automation"
 }
@@ -132,6 +146,7 @@
     dependsOn buildHarvesterImage
     dependsOn buildAutomationImages
     dependsOn buildAllocationImage
+    dependsOn buildProvisionerImage
 }
 
 task tagImages {
@@ -139,6 +154,7 @@
     dependsOn tagHarvesterImage
     dependsOn tagAutomationImages
     dependsOn tagAllocationImage
+    dependsOn tagProvisionerImage
 }
 
 task publish {
@@ -146,6 +162,7 @@
     dependsOn publishHarvesterImage
     dependsOn publishAutomationImages
     dependsOn publishAllocationImage
+    dependsOn publishProvisionerImage
 }
 
 // ~~~~~~~~~~~~~~~~~~~ Deployment / Test Tasks  ~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/provisioner/Dockerfile b/provisioner/Dockerfile
new file mode 100644
index 0000000..478bff8
--- /dev/null
+++ b/provisioner/Dockerfile
@@ -0,0 +1,44 @@
+FROM ubuntu:14.04
+
+# Base image information borrowed by official golang wheezy Dockerfile
+RUN apt-get update && apt-get install -y --no-install-recommends \
+		g++ \
+		gcc \
+		libc6-dev \
+		make \
+                curl \
+	&& rm -rf /var/lib/apt/lists/*
+
+ENV GOLANG_VERSION 1.6.2
+ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
+ENV GOLANG_DOWNLOAD_SHA256 e40c36ae71756198478624ed1bb4ce17597b3c19d243f3f0899bb5740d56212a
+
+RUN curl -kfsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
+	&& echo "$GOLANG_DOWNLOAD_SHA256  golang.tar.gz" | sha256sum -c - \
+	&& tar -C /usr/local -xzf golang.tar.gz \
+	&& rm golang.tar.gz
+
+ENV GOPATH /go
+ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
+
+RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
+
+# CORD Provisioner Dockerfile
+WORKDIR $GOPATH
+
+RUN apt-get update && \
+	apt-get install -y  software-properties-common && \
+	apt-add-repository ppa:ansible/ansible && \
+	apt-get update -y  -m && \
+	apt-get install -y git ansible
+
+RUN go get github.com/tools/godep
+ADD . $GOPATH/src/github.com/ciena/cord-provisioner
+
+WORKDIR $GOPATH/src/github.com/ciena/cord-provisioner
+RUN $GOPATH/bin/godep restore
+
+WORKDIR $GOPATH
+RUN go install github.com/ciena/cord-provisioner
+
+ENTRYPOINT ["$GOPATH/bin/cord-provisioner"]
diff --git a/provisioner/Godeps/Godeps.json b/provisioner/Godeps/Godeps.json
new file mode 100644
index 0000000..a68b4ef
--- /dev/null
+++ b/provisioner/Godeps/Godeps.json
@@ -0,0 +1,22 @@
+{
+	"ImportPath": "github.com/ciena/provisioner",
+	"GoVersion": "go1.6",
+	"GodepVersion": "v72",
+	"Deps": [
+		{
+			"ImportPath": "github.com/gorilla/context",
+			"Comment": "v1.1-4-gaed02d1",
+			"Rev": "aed02d124ae4a0e94fea4541c8effd05bf0c8296"
+		},
+		{
+			"ImportPath": "github.com/gorilla/mux",
+			"Comment": "v1.1-9-gbd09be0",
+			"Rev": "bd09be08ed4377796d312df0a45314e11b8f5dc1"
+		},
+		{
+			"ImportPath": "github.com/kelseyhightower/envconfig",
+			"Comment": "1.1.0-17-g91921eb",
+			"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
+		}
+	]
+}
diff --git a/provisioner/dispatcher.go b/provisioner/dispatcher.go
new file mode 100644
index 0000000..44e16e0
--- /dev/null
+++ b/provisioner/dispatcher.go
@@ -0,0 +1,137 @@
+package main
+
+import (
+	"log"
+	"os/exec"
+)
+
+type WorkRequest struct {
+	Info   *RequestInfo
+	Script string
+	Role   string
+}
+
+type Worker struct {
+	ID          int
+	Work        chan WorkRequest
+	StatusChan  chan StatusMsg
+	WorkerQueue chan chan WorkRequest
+	QuitChan    chan bool
+}
+
+type StatusMsg struct {
+	Request *WorkRequest
+	Worker  int
+	Status  TaskStatus
+	Message string
+}
+
+func NewWorker(id int, workerQueue chan chan WorkRequest, statusChan chan StatusMsg) Worker {
+	// Create, and return the worker.
+	worker := Worker{
+		ID:          id,
+		Work:        make(chan WorkRequest),
+		StatusChan:  statusChan,
+		WorkerQueue: workerQueue,
+		QuitChan:    make(chan bool)}
+
+	return worker
+}
+
+func (w *Worker) Start() {
+	go func() {
+		for {
+			// Add ourselves into the worker queue.
+			w.WorkerQueue <- w.Work
+
+			select {
+			case work := <-w.Work:
+				// Receive a work request.
+				w.StatusChan <- StatusMsg{&work, w.ID, Running, ""}
+				err := exec.Command(work.Script, work.Info.Id, work.Info.Name,
+					work.Info.Ip, work.Info.Mac, work.Role).Run()
+				if err != nil {
+					w.StatusChan <- StatusMsg{&work, w.ID, Failed, err.Error()}
+				} else {
+					w.StatusChan <- StatusMsg{&work, w.ID, Complete, ""}
+				}
+			case <-w.QuitChan:
+				// We have been asked to stop.
+				log.Printf("worker%d stopping\n", w.ID)
+				return
+			}
+		}
+	}()
+}
+
+func (w *Worker) Stop() {
+	go func() {
+		w.QuitChan <- true
+	}()
+}
+
+type Dispatcher struct {
+	Storage     Storage
+	WorkQueue   chan WorkRequest
+	WorkerQueue chan chan WorkRequest
+	StatusChan  chan StatusMsg
+	QuitChan    chan bool
+	NumWorkers  int
+}
+
+func NewDispatcher(numWorkers int, storage Storage) *Dispatcher {
+	d := Dispatcher{
+		Storage:     storage,
+		WorkQueue:   make(chan WorkRequest, 100),
+		StatusChan:  make(chan StatusMsg, 100),
+		NumWorkers:  numWorkers,
+		WorkerQueue: make(chan chan WorkRequest, numWorkers),
+		QuitChan:    make(chan bool),
+	}
+
+	return &d
+}
+
+func (d *Dispatcher) Dispatch(info *RequestInfo, role string) error {
+	d.WorkQueue <- WorkRequest{
+		Info: info,
+		Role: role,
+	}
+	return nil
+}
+
+func (d *Dispatcher) Start() {
+	// Now, create all of our workers.
+	for i := 0; i < d.NumWorkers; i++ {
+		log.Printf("Creating worker %d", i)
+		worker := NewWorker(i, d.WorkerQueue, d.StatusChan)
+		worker.Start()
+	}
+
+	go func() {
+		for {
+			select {
+			case work := <-d.WorkQueue:
+				log.Println("Received work requeust")
+				go func() {
+					d.StatusChan <- StatusMsg{&work, -1, Pending, ""}
+					worker := <-d.WorkerQueue
+
+					log.Println("Dispatching work request")
+					worker <- work
+				}()
+			case update := <-d.StatusChan:
+				d.Storage.Put(update.Request.Info.Id, update)
+			case <-d.QuitChan:
+				log.Println("[info] Stopping dispatcher")
+				return
+			}
+		}
+	}()
+}
+
+func (d *Dispatcher) Stop() {
+	go func() {
+		d.QuitChan <- true
+	}()
+}
diff --git a/provisioner/handlers.go b/provisioner/handlers.go
new file mode 100644
index 0000000..b915674
--- /dev/null
+++ b/provisioner/handlers.go
@@ -0,0 +1,105 @@
+package main
+
+import (
+	"bufio"
+	"encoding/json"
+	"github.com/gorilla/mux"
+	"log"
+	"net/http"
+	"strings"
+)
+
+type RequestInfo struct {
+	Id   string
+	Name string
+	Ip   string
+	Mac  string
+}
+
+func (c *Context) GetRole(info *RequestInfo) (string, error) {
+	if c.config.RoleSelectorURL == "" {
+		return c.config.DefaultRole, nil
+	}
+	r, err := http.Get(c.config.RoleSelectorURL)
+	if err != nil {
+		return "", err
+	}
+
+	s := bufio.NewScanner(r.Body)
+	defer r.Body.Close()
+	role := strings.TrimSpace(s.Text())
+	if role == "" {
+		return c.config.DefaultRole, nil
+	}
+	return role, nil
+}
+
+func (c *Context) validateData(info *RequestInfo) bool {
+	if strings.TrimSpace(info.Id) == "" ||
+		strings.TrimSpace(info.Name) == "" ||
+		strings.TrimSpace(info.Ip) == "" ||
+		strings.TrimSpace(info.Mac) == "" {
+		return false
+	}
+	return true
+}
+
+func (c *Context) ProvisionRequestHandler(w http.ResponseWriter, r *http.Request) {
+	var info RequestInfo
+	decoder := json.NewDecoder(r.Body)
+	defer r.Body.Close()
+	if err := decoder.Decode(&info); err != nil || !c.validateData(&info) {
+		log.Printf("ERROR: %v", err)
+		w.WriteHeader(http.StatusBadRequest)
+		return
+	}
+
+	log.Printf("GOT: %v", info)
+
+	role, err := c.GetRole(&info)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	err = c.dispatcher.Dispatch(&info, role)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	w.WriteHeader(http.StatusAccepted)
+}
+
+func (c *Context) ListRequestsHandler(w http.ResponseWriter, r *http.Request) {
+	list, err := c.storage.List()
+	bytes, err := json.Marshal(list)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	w.Write(bytes)
+}
+
+func (c *Context) QueryStatusHandler(w http.ResponseWriter, r *http.Request) {
+	vars := mux.Vars(r)
+	id, ok := vars["nodeid"]
+	if !ok || strings.TrimSpace(id) == "" {
+		w.WriteHeader(http.StatusBadRequest)
+		return
+	}
+	s, err := c.storage.Get(id)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	if s == nil {
+		w.WriteHeader(http.StatusNotFound)
+		return
+	}
+	bytes, err := json.Marshal(s)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	w.Write(bytes)
+}
diff --git a/provisioner/provisioner.go b/provisioner/provisioner.go
new file mode 100644
index 0000000..aeb237a
--- /dev/null
+++ b/provisioner/provisioner.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+	"fmt"
+	"github.com/gorilla/mux"
+	"github.com/kelseyhightower/envconfig"
+	"log"
+	"net/http"
+)
+
+type Config struct {
+	Port            int    `default:"4243"`
+	Listen          string `default:"0.0.0.0"`
+	RoleSelectorURL string `default:"" envconfig:"role_selector_url"`
+	DefaultRole     string `default:"compute-node" envconfig:"default_role"`
+	Script          string `default:"do-ansible"`
+}
+
+type Context struct {
+	config     Config
+	storage    Storage
+	workers    []Worker
+	dispatcher *Dispatcher
+}
+
+func main() {
+	context := &Context{}
+
+	err := envconfig.Process("PROVISION", &(context.config))
+	if err != nil {
+		log.Fatalf("[error] Unable to parse configuration options : %s", err)
+	}
+
+	log.Printf(`Configuration:
+	    Listen:          %s
+	    Port:            %d
+	    RoleSelectorURL: %s
+	    DefaultRole:     %s`,
+		context.config.Listen, context.config.Port, context.config.RoleSelectorURL, context.config.DefaultRole)
+
+	context.storage = NewMemoryStorage()
+
+	router := mux.NewRouter()
+	router.HandleFunc("/provision/", context.ProvisionRequestHandler).Methods("POST")
+	router.HandleFunc("/provision/", context.ListRequestsHandler).Methods("GET")
+	router.HandleFunc("/provision/{nodeid}", context.QueryStatusHandler).Methods("GET")
+	http.Handle("/", router)
+
+	// Start the dispatcher and workers
+	context.dispatcher = NewDispatcher(5, context.storage)
+	context.dispatcher.Start()
+
+	http.ListenAndServe(fmt.Sprintf("%s:%d", context.config.Listen, context.config.Port), nil)
+}
diff --git a/provisioner/storage.go b/provisioner/storage.go
new file mode 100644
index 0000000..db64df2
--- /dev/null
+++ b/provisioner/storage.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+	"log"
+)
+
+type Storage interface {
+	Put(id string, update StatusMsg) error
+	Get(id string) (*StatusMsg, error)
+	List() ([]StatusMsg, error)
+}
+
+type MemoryStorage struct {
+	Data map[string]StatusMsg
+}
+
+func NewMemoryStorage() *MemoryStorage {
+	return &MemoryStorage{
+		Data: make(map[string]StatusMsg),
+	}
+}
+
+func (s *MemoryStorage) Put(id string, update StatusMsg) error {
+	s.Data[id] = update
+	log.Printf("%s : %s", id, update.Status.String())
+	return nil
+}
+
+func (s *MemoryStorage) Get(id string) (*StatusMsg, error) {
+	m, ok := s.Data[id]
+	if !ok {
+		return nil, nil
+	}
+	return &m, nil
+}
+
+func (s *MemoryStorage) List() ([]StatusMsg, error) {
+	r := make([]StatusMsg, len(s.Data))
+	i := 0
+	for _, v := range s.Data {
+		r[i] = v
+	}
+	return r, nil
+}
diff --git a/provisioner/task.go b/provisioner/task.go
new file mode 100644
index 0000000..4017268
--- /dev/null
+++ b/provisioner/task.go
@@ -0,0 +1,38 @@
+package main
+
+type TaskStatus uint8
+
+const (
+	Pending TaskStatus = iota
+	Running
+	Complete
+	Failed
+)
+
+func (s TaskStatus) String() string {
+	switch s {
+	case Pending:
+		return "PENDING"
+	case Running:
+		return "RUNNING"
+	case Complete:
+		return "COMPLETE"
+	case Failed:
+		return "FAILED"
+	}
+	return "INVALID TASK STATUS"
+}
+
+type Task struct {
+	nodeId string
+	status TaskStatus
+}
+
+type TaskQueueEntry struct {
+	previous *TaskQueueEntry
+	next     *TaskQueueEntry
+	task     *Task
+}
+
+type TaskQueue struct {
+}