Skip to content

Commit

Permalink
Merge pull request #2 from emccode/feature/remove
Browse files Browse the repository at this point in the history
v0.1.0
  • Loading branch information
kacole2 committed Apr 25, 2016
2 parents b5202df + 3fd65e7 commit 47b5a58
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 59 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Docker Machine Driver for RackHD

Use [Docker Machine](https://github.com/docker/machine) to create Docker hosts with [RackHD](https://github.com/RackHD/RackHD).
Use [Docker Machine](https://github.com/docker/machine) to create Docker hosts with [RackHD](https://github.com/RackHD/RackHD). This is the first solution that enables bare-metal provisioning of [Docker](https://github.com/docker/docker) and [Docker Swarm](https://github.com/docker/swarm).

## Installation

**Linux & Mac OSX**:
```
curl -L https://github.com/emccode/docker-machine-rackhd/releases/download/v0.0.1/docker-machine-driver-rackhd.`uname -s`-`uname -m` >/usr/local/bin/docker-machine-driver-rackhd && chmod +x /usr/local/bin/docker-machine-driver-rackhd
curl -L https://github.com/emccode/docker-machine-rackhd/releases/download/v0.1.0/docker-machine-driver-rackhd.`uname -s`-`uname -m` >/usr/local/bin/docker-machine-driver-rackhd && chmod +x /usr/local/bin/docker-machine-driver-rackhd
```

## Using the driver
Expand Down Expand Up @@ -41,15 +41,15 @@ Options:
| --rackhd-ssh-password | RACKHD_SSH_PASSWORD | root | SSH Password for the node | N |
| --rackhd-ssh-port | RACKHD_SSH_PORT | 22 | SSH Port for the node | N |

This initial version of the driver uses explicit creation instructions. The user must specify the Node ID from RackHD. The NodeID is characterized as a `compute` instance. Do not use `enclosure`.
This initial version of the driver uses explicit instructions. The user must specify the Node ID from RackHD. The NodeID is characterized as a `compute` instance. Do not use `enclosure`.

Create a Docker host using the following example. This will function as expected if Docker Machine has access to the DHCP network of RackHD.

```
$ docker-machine create -d rackhd --rackhd-node-id 56c61189f21f01b608b3e594 rackhdtest
Running pre-create checks...
(rackhdtest) Testing accessibility of endpoint: localhost:8080
(rackhdtest) Test Passed. localhost:8080 is accessbile and installation will begin
(rackhdtest) Test Passed. localhost:8080 Monorail and Redfish API's are accessible and installation will begin
Creating machine...
(rackhdtest) Connection succeeded on: 172.31.128.16:22
(rackhdtest) Creating SSH key...
Expand All @@ -69,6 +69,10 @@ To see how to connect your Docker Client to the Docker Engine running on this vi

Check out the [RackHD Vagrant + Docker Machine Example](https://github.com/emccode/machine/tree/master/rackhd) to view a complete in-depth configuration and walk-through.

## Other Machine Functions

The other functions for **Start**, **Stop**, **Restart**, **Kill**, and **Remove** require the use of IPMI. This must be configured in RackHD.

# Licensing
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0>

Expand Down
269 changes: 214 additions & 55 deletions rackhd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import (
"strconv"
"strings"

apiclient "github.com/emccode/gorackhd/client"
apiclientRedfish "github.com/emccode/gorackhd-redfish/client"
"github.com/emccode/gorackhd-redfish/client/redfish_v1"
modelsRedfish "github.com/emccode/gorackhd-redfish/models"
apiclientMonorail "github.com/emccode/gorackhd/client"
"github.com/emccode/gorackhd/client/lookups"
"github.com/emccode/gorackhd/client/nodes"

httptransport "github.com/go-swagger/go-swagger/httpkit/client"
"github.com/go-swagger/go-swagger/strfmt"
Expand All @@ -25,14 +29,15 @@ import (

type Driver struct {
*drivers.BaseDriver
Endpoint string
NodeID string
SSHUser string
SSHPassword string
SSHPort int
SSHKey string
Transport string
client *apiclient.Monorail
Endpoint string
NodeID string
SSHUser string
SSHPassword string
SSHPort int
SSHKey string
Transport string
clientMonorail *apiclientMonorail.Monorail
clientRedfish *apiclientRedfish.Redfish
}

const (
Expand Down Expand Up @@ -148,21 +153,27 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
func (d *Driver) PreCreateCheck() error {
log.Infof("Testing accessibility of endpoint: %v", d.Endpoint)
//Generate the client
client := d.getClient()

clientMonorail := d.getClientMonorail()
//do a test to see if the server is available. 2nd Nil is authentication params
// that need to be determined in v2.0 of API
_, err := client.Config.GetConfig(nil, nil)
_, err := clientMonorail.Config.GetConfig(nil, nil)
if err != nil {
return fmt.Errorf("The Endpoint is not accessible. Error: %s", err)
return fmt.Errorf("The Monorail API Endpoint is not accessible. Error: %s", err)
}
log.Infof("Test Passed. %v is accessbile and installation will begin", d.Endpoint)

clientRedfish := d.getClientRedfish()

_, err2 := clientRedfish.RedfishV1.GetRoles(nil)
if err2 != nil {
return fmt.Errorf("The Redfish API Endpoint is not accessible. Error: %s", err2)
}

log.Infof("Test Passed. %v Monorail and Redfish API's are accessible and installation will begin", d.Endpoint)
return nil
}

func (d *Driver) Create() error {
//Generate the client
client := d.getClient()
client := d.getClientMonorail()

// do a lookup on the ID to retrieve IP information
resp, err := client.Lookups.GetLookups(&lookups.GetLookupsParams{Q: d.NodeID}, nil)
Expand Down Expand Up @@ -279,71 +290,219 @@ func (d *Driver) GetIP() (string, error) {
}

func (d *Driver) GetState() (state.State, error) {
/*
TODO: THIS REQUIRES THE REDFISH API WHICH IS STILL IN DEVELOPMENT
switch instance.State {
case "online":

//Get the Out of Band Management Type
clientMonorail := d.getClientMonorail()
respObm, errObm := clientMonorail.Nodes.GetNodesIdentifierObm(&nodes.GetNodesIdentifierObmParams{Identifier: d.NodeID}, nil)
if errObm != nil {
return state.None, errObm
}

//If there is no obm (such as Vagrant), send back as Running
switch respObm.Payload.([]interface{})[0].(map[string]interface{})["service"] {
case "noop-obm-service":
return state.Running, nil
default:
//Generate the client
clientRedfish := d.getClientRedfish()

// do a lookup on the Node ID to retrieve Power information
resp, err := clientRedfish.RedfishV1.GetSystem(&redfish_v1.GetSystemParams{Identifier: d.NodeID})
if err != nil {
return state.None, nil
}
switch resp.Payload.PowerState {
case "Online", "online", "Up", "up", "On", "on":
return state.Running, nil
case "offline":
case "Offline", "offline", "Down", "down", "Off", "off":
return state.Stopped, nil
case "Unknown", "unknown":
return state.None, nil
default:
return state.Running, nil
}
return state.None, nil
*/
return state.Running, nil
}
}

func (d *Driver) Start() error {
/*
TODO: THIS REQUIRES THE REDFISH API WHICH IS STILL IN DEVELOPMENT
REMOTELY POWER ON A SERVER VIA IPMI
*/
return nil

//Get the Out of Band Management Type
clientMonorail := d.getClientMonorail()
respObm, errObm := clientMonorail.Nodes.GetNodesIdentifierObm(&nodes.GetNodesIdentifierObmParams{Identifier: d.NodeID}, nil)
if errObm != nil {
return errObm
}

//If there is no obm (such as Vagrant), nil
switch respObm.Payload.([]interface{})[0].(map[string]interface{})["service"] {
case "noop-obm-service":
return fmt.Errorf("OBM %#v Type Not Supported For Starting: %#v", "noop-obm-service", d.NodeID)
default:
log.Debugf("Attempting Turn On: %#v", d.NodeID)
action := &modelsRedfish.RackHDResetAction{
ResetType: "On",
}

clientRedfish := d.getClientRedfish()

_, err := clientRedfish.RedfishV1.DoReset(&redfish_v1.DoResetParams{Identifier: d.NodeID, Payload: action})
if err != nil {
return fmt.Errorf("There was an issue Powering On the Server. Error: %s", err)
}

log.Debugf("Node has succussfully been powered on: %#v", d.NodeID)
return nil
}
}

func (d *Driver) Stop() error {
/*
TODO: THIS REQUIRES THE REDFISH API WHICH IS STILL IN DEVELOPMENT
SEND A SIGKILL TO THE OS. OR USE THE API TO GRACEFULLY SHUTDOWN THE HOST
*/
return nil
//Get the Out of Band Management Type
clientMonorail := d.getClientMonorail()
respObm, errObm := clientMonorail.Nodes.GetNodesIdentifierObm(&nodes.GetNodesIdentifierObmParams{Identifier: d.NodeID}, nil)
if errObm != nil {
return errObm
}

//If there is no obm (such as Vagrant), nil
switch respObm.Payload.([]interface{})[0].(map[string]interface{})["service"] {
case "noop-obm-service":
return fmt.Errorf("OBM %#v Type Not Supported For Stopping: %#v", "noop-obm-service", d.NodeID)
default:
log.Debugf("Attempting Graceful Shutdown of: %#v", d.NodeID)
action := &modelsRedfish.RackHDResetAction{
ResetType: "GracefulShutdown",
}

clientRedfish := d.getClientRedfish()

_, err := clientRedfish.RedfishV1.DoReset(&redfish_v1.DoResetParams{Identifier: d.NodeID, Payload: action})
if err != nil {
return fmt.Errorf("There was an issue Shutting Down the Server. Error: %s", err)
}
log.Debugf("Node has succussfully been shutdown: %#v", d.NodeID)
return nil
}
}

func (d *Driver) Remove() error {
/*
TODO: DECIDE WHETHER TO UNINSTALL DOCKER OR
1. ADD A GENERIC WORKFLOW
2. REBOOT THE HOST
3. HOPE THAT GENERIC WORKFLOW WILL RESET THE HOST BACK TO A BLANK SLATE
*/
//Get the Out of Band Management Type
clientMonorail := d.getClientMonorail()
respObm, errObm := clientMonorail.Nodes.GetNodesIdentifierObm(&nodes.GetNodesIdentifierObmParams{Identifier: d.NodeID}, nil)
if errObm != nil {
return errObm
}

//If there is no obm (such as Vagrant), nil
switch respObm.Payload.([]interface{})[0].(map[string]interface{})["service"] {
case "noop-obm-service":
log.Debugf("OBM %#v Type Not Supported For Shutdown: %#v", "noop-obm-service", d.NodeID)
default:
log.Debugf("Attempting Graceful Shutdown of: %#v", d.NodeID)
action := &modelsRedfish.RackHDResetAction{
ResetType: "GracefulShutdown",
}

clientRedfish := d.getClientRedfish()

_, err := clientRedfish.RedfishV1.DoReset(&redfish_v1.DoResetParams{Identifier: d.NodeID, Payload: action})
if err != nil {
log.Infof("There was an issue Shutting Down the Server. Error: %s", err)
//return fmt.Errorf("There was an issue Shutting Down the Server. Error: %s", err)
} else {
log.Debugf("Node has succussfully been shutdown: %#v", d.NodeID)
}
}

//Remove the Node from RackHD Inventory
log.Debugf("Removing Node From RackHD: %#v", d.NodeID)
_, err2 := clientMonorail.Nodes.DeleteNodesIdentifier(&nodes.DeleteNodesIdentifierParams{Identifier: d.NodeID}, nil)
if err2 != nil {
return err2
}
log.Debugf("Successfully Removed Node From RackHD: %#v", d.NodeID)

return nil
}

func (d *Driver) Restart() error {
/*
TODO: THIS REQUIRES THE REDFISH API WHICH IS STILL IN DEVELOPMENT
REMOTELY RESET OFF A SERVER VIA IPMI
*/
return nil
//Get the Out of Band Management Type
clientMonorail := d.getClientMonorail()
respObm, errObm := clientMonorail.Nodes.GetNodesIdentifierObm(&nodes.GetNodesIdentifierObmParams{Identifier: d.NodeID}, nil)
if errObm != nil {
return errObm
}

//If there is no obm (such as Vagrant), nil
switch respObm.Payload.([]interface{})[0].(map[string]interface{})["service"] {
case "noop-obm-service":
return fmt.Errorf("OBM Type Not Supported: %#v, %#v", "noop-obm-service", d.NodeID)
default:
log.Debugf("Attempting Restart of: %#v", d.NodeID)
action := &modelsRedfish.RackHDResetAction{
ResetType: "GracefulRestart",
}

clientRedfish := d.getClientRedfish()

_, err := clientRedfish.RedfishV1.DoReset(&redfish_v1.DoResetParams{Identifier: d.NodeID, Payload: action})
if err != nil {
return fmt.Errorf("There was an issue Shutting Down the Server. Error: %s", err)
}
log.Debugf("Successfully restarted node: %#v", d.NodeID)
return nil
}
}

func (d *Driver) Kill() error {
/*
TODO: THIS REQUIRES THE REDFISH API WHICH IS STILL IN DEVELOPMENT
POWER OFF THE HOST VIA IMPI
*/
return nil
//Get the Out of Band Management Type
clientMonorail := d.getClientMonorail()
respObm, errObm := clientMonorail.Nodes.GetNodesIdentifierObm(&nodes.GetNodesIdentifierObmParams{Identifier: d.NodeID}, nil)
if errObm != nil {
return errObm
}

//If there is no obm (such as Vagrant), nil
switch respObm.Payload.([]interface{})[0].(map[string]interface{})["service"] {
case "noop-obm-service":
return fmt.Errorf("OBM Type Not Supported: %#v, %#v", "noop-obm-service", d.NodeID)
default:
log.Debugf("Attempting Force Off of: %#v", d.NodeID)
action := &modelsRedfish.RackHDResetAction{
ResetType: "ForceOff",
}

clientRedfish := d.getClientRedfish()

_, err := clientRedfish.RedfishV1.DoReset(&redfish_v1.DoResetParams{Identifier: d.NodeID, Payload: action})
if err != nil {
return fmt.Errorf("There was an issue Shutting Down the Server. Error: %s", err)
}
log.Debugf("Successfully turned off node: %#v", d.NodeID)
return nil
}
}

func (d *Driver) getClient() *apiclient.Monorail {
log.Debugf("Getting RackHD Client")
if d.client == nil {
func (d *Driver) getClientMonorail() *apiclientMonorail.Monorail {
log.Debugf("Getting RackHD Monorail Client")
if d.clientMonorail == nil {
// create the transport
/** Will Need to determine changes for v 2.0 API **/
transport := httptransport.New(d.Endpoint, "/api/1.1", []string{d.Transport})
// create the API client, with the transport
d.client = apiclient.New(transport, strfmt.Default)
d.clientMonorail = apiclientMonorail.New(transport, strfmt.Default)
}
return d.clientMonorail
}

func (d *Driver) getClientRedfish() *apiclientRedfish.Redfish {
log.Debugf("Getting RackHD Redfish Client")
if d.clientRedfish == nil {
// create the transport
transport := httptransport.New(d.Endpoint, "/redfish/v1", []string{d.Transport})
// create the API client, with the transport
d.clientRedfish = apiclientRedfish.New(transport, strfmt.Default)
}
return d.client
return d.clientRedfish
}

func (d *Driver) publicSSHKeyPath() string {
Expand Down

0 comments on commit 47b5a58

Please sign in to comment.