Merge "initial commit for CORD-213."
diff --git a/QUICKSTART.md b/QUICKSTART.md
index e108394..de8a5fe 100644
--- a/QUICKSTART.md
+++ b/QUICKSTART.md
@@ -131,13 +131,22 @@
Canonical MAAS provides the PXE and other bare metal provisioning services for CORD and will be deployed on the
head node via `Ansible`. To initiate this deployment issue the following `gradle` command. This `gradle` command
executes `ansible-playbook -i 10.100.198.202, --skip-tags=switch_support,interface_config --extra-vars=external_iface=eth0`.
+
The IP address, `10.100.198.202` is the IP address assigned to the head node on a private network. The
`skip-tags` option excludes Ansible tasks not required when utilizing the Vagrant based head node. The
`extra-vars` option overrides the default role vars for the external interface and is needed for the VirtualBox
based environment. Traffic from the compute nodes will be NAT-ed through this interface on the head node.
+The default MAAS deployment does not support power management for virtual box based hosts. As part of the MAAS
+installation support was added for power management, but it does require some additional configuration. This
+additional configuration is detailed below, but is mentioned here because when deploying the head node an
+additional parameter must be set. This parameter specified the username on the host machine that should be
+used when SSHing from the head node to the host machine to remotely execute the `vboxmanage` command. This
+is typically the username used when logging into your laptop or desktop development machine. This should
+be specified on the deploy command line using the `-PvboxUser` option.
+
```
-./gradlew deployMaas
+./gradlew -PvboxUser=<username> deploy
```
This task can take some time so be patient. It should complete without errors, so if an error is encountered
@@ -200,22 +209,18 @@
hostname made up, in the Canonical way, of a adjective and an noun, such as `popular-feast.cord.lab`. _The
name will be different for everyone._ The new node will be in the `New` state.
-For _real_ hosts, automation that leverages the MAAS APIs will transition the node from `New` through the
-states of `Commissioning` and `Acquired` to `Deployed`.
+If you have properly configured power management for virtualbox (see below) the host will be automatically
+transitioned from `New` through the start of `Comissioning` and `Acquired` to `Deployed`.
-#### Bad News
-For hosts that support IPMI, which includes the hosts recommended for the CORD POD, MAAS will automatically
-discover remote power management information. However, MAAS is not able to detect or manage power for
-VirtualBox machines.
+#### Virtual Box Power Management
+Virtual box power management is implemented via helper scripts that SSH to the virtual box host and
+execute `vboxmanage` commands. For this to work The scripts must be configured with a username and host
+to utilize when SSHing and that account must allow SSH from the head node guest to the host using
+SSH keys such that no password entry is required.
-A work-a-round has been created to support VirtualBox based VMs. This is accomplished by overriding the
-support script for the `Intel AMT` power support module; but unfortunately it is not fully automated at this
-point.
-
-The work-a-round uses SSH from the MAAS head node to the machine that is running VirtualBox. To enable this,
-assuming that VirtualBox is running on a Linux based system, you can copy the MAAS ssh public key from
-`/var/lib/maas/.ssh/id_rsa.pub` on the head known to your accounts `authorized_keys` files. You can verify
-that this is working by issuing the following commands from your host machine:
+To enable SSH key based login, assuming that VirtualBox is running on a Linux based system, you can copy
+the MAAS ssh public key from `/var/lib/maas/.ssh/id_rsa.pub` on the head known to your accounts `authorized_keys`
+files. You can verify that this is working by issuing the following commands from your host machine:
```
vagrant ssh headnode
sudo su - maas
@@ -224,53 +229,9 @@
If you are able to accomplish these commands the VirtualBox power management should operate correctly.
-To utilize this work-a-round the power management settings must be manual configured on the node. To
-accomplish this the VirtualBox ID for the compute node must first be discovered. To accomplish this issue
-the following command on the machine on which VirtualBox is running:
-```
-vboxmanage list vms
-```
-
-This will return to a list of all VMs being managed by VirtualBox. In this list will be an entry similar
-to the following for the compute node:
-```
-"maas_computenode_1463279637475_74274" {d18af821-91af-4d20-b3b2-67ed85e23c13}
-```
-
-The important part of this entry is the last 12 characters of the VM ID, `67ed85e23c13` in this example. This
-will be used as a fake MAC address for power management.
-
-To set the power settings for the compute node visit the MAAS UI page for the compute node. From there select
-the `Power type` to `Intel AMT`. This will display the additional fields: `MAC Address`, `Power Password`, and
-`Power Address`. The values of these fields should be set as follows:
-
- - MAC Address - the previously discovered last 12 characters of the VM ID, formatted like a MAC address,
- `67:ed:85:e2:3c:13` from the example above.
- - Power Password - the user name to use to ssh from the head node to the host on which VirtualBox is
- executing.
- - Power Address - the IP address of the host on which VirtualBox is executing.
-
-Once this information is saved the automation will eventually start the the compute node should transition
-to `Deployed` state. This will include several reboots and shutdowns of the compute node.
-
### Post Deployment Provisioning of the Compute Node
-Once the node is in the `Deployed` state, it can be provisioned for use in a CORD POD. Eventually, this action
-will be automated and triggered when a node moves to the `Deployed` state. Currently, this must be manually
-invoked. The post deployment provisioning consists of configuring the networking on the node to comply with
-best practices of a CORD POD installation and leverages an Ansible play boot to accomplish the provisioning.
-
-To provision the compute node `ssh` to the head node, change to the `/etc/maas/ansible` directory, and invoke
-the playbook on the new compute node. To ssh to the head node, you can use `vagrant ssh headnode` from the
-host system for the virtual machines. Once __on__ the head node the following commands can invoke the
-provisioning of the compute node, __the name of the compute node is an example name, the actual name in the
-deployment can be found in the nodes list of the MAAS UI.__:
-
-```
-cd /etc/maas/ansible
-ansible-playbook -i popular-feast.cord.lab, compute-node.yml --skip-tags=interface_config
-```
-
-_NOTE: the `skip-tags` option is required for the VirtualBox based environment_
+Once the node is in the `Deployed` state, it will be provisioned for use in a CORD POD by the execution of
+an `Ansible` playbook.
### Complete
Once the compute node is in the `Deployed` state and post deployment provisioning on the compute node is
diff --git a/automation/Dockerfile b/automation/Dockerfile
index d1b6318..ad78874 100644
--- a/automation/Dockerfile
+++ b/automation/Dockerfile
@@ -1,6 +1,6 @@
FROM golang:alpine
-RUN apk --update add git
+RUN apk --update add openssh-client git
WORKDIR /go
RUN go get github.com/tools/godep
@@ -13,4 +13,7 @@
RUN go install github.com/ciena/cord-maas-automation
+RUN mkdir -p /root/.ssh
+COPY ssh-config /root/.ssh/config
+
ENTRYPOINT ["/go/bin/cord-maas-automation"]
diff --git a/automation/Godeps/Godeps.json b/automation/Godeps/Godeps.json
index cd9aa7e..a267c3c 100644
--- a/automation/Godeps/Godeps.json
+++ b/automation/Godeps/Godeps.json
@@ -24,6 +24,11 @@
"ImportPath": "github.com/hashicorp/consul",
"Comment": "v0.6.4-341-g22938ab",
"Rev": "22938ab8b3ca069e490a4897a8ec13308ac61605"
- }
+ },
+ {
+ "ImportPath": "github.com/kelseyhightower/envconfig",
+ "Comment": "1.1.0-17-g91921eb",
+ "Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
+ }
]
}
diff --git a/automation/maas-flow.go b/automation/maas-flow.go
index 1f501b9..84337aa 100644
--- a/automation/maas-flow.go
+++ b/automation/maas-flow.go
@@ -3,6 +3,7 @@
import (
"encoding/json"
"flag"
+ "github.com/kelseyhightower/envconfig"
"log"
"net/url"
"os"
@@ -26,11 +27,16 @@
}
}`
defaultMapping = "{}"
- PROVISION_URL = "PROVISION_URL"
- PROVISION_TTL = "PROVISION_TTL"
- DEFAULT_TTL = "30m"
)
+type Config struct {
+ PowerHelperUser string `default:"cord" envconfig:"POWER_HELPER_USER"`
+ PowerHelperHost string `default:"127.0.0.1" envconfig:"POWER_HELPER_HOST"`
+ PowerHelperScript string `default:"" envconfig:"POWER_HELPER_SCRIPT"`
+ ProvisionUrl string `default:"" envconfig:"PROVISION_URL"`
+ ProvisionTtl string `default:"30m" envconfig:"PROVISION_TTL"`
+}
+
var apiKey = flag.String("apikey", "", "key with which to access MAAS server")
var maasURL = flag.String("maas", "http://localhost/MAAS", "url over which to access MAAS")
var apiVersion = flag.String("apiVersion", "1.0", "version of the API to access")
@@ -90,28 +96,23 @@
func main() {
flag.Parse()
+ config := Config{}
+ err := envconfig.Process("AUTOMATION", &config)
+ checkError(err, "[error] unable to parse environment options : %s", err)
options := ProcessingOptions{
- Preview: *preview,
- Verbose: *verbose,
- AlwaysRename: *always,
- ProvTracker: NewTracker(),
- ProvisionURL: os.Getenv(PROVISION_URL),
+ Preview: *preview,
+ Verbose: *verbose,
+ AlwaysRename: *always,
+ ProvTracker: NewTracker(),
+ ProvisionURL: config.ProvisionUrl,
+ PowerHelper: config.PowerHelperScript,
+ PowerHelperUser: config.PowerHelperUser,
+ PowerHelperHost: config.PowerHelperHost,
}
- var ttl string
- if ttl = os.Getenv(PROVISION_TTL); ttl == "" {
- ttl = "30m"
- }
-
- var err error
- options.ProvisionTTL, err = time.ParseDuration(ttl)
- if err != nil {
- log.Printf("[warn] unable to parse specified duration of '%s', defaulting to '%s'",
- ttl, DEFAULT_TTL)
- options.ProvisionTTL, err = time.ParseDuration("30m")
- checkError(err, "[error] unable to parse default TTL duration of '30m' : %s", err)
- }
+ options.ProvisionTTL, err = time.ParseDuration(config.ProvisionTtl)
+ checkError(err, "[error] unable to parse specified duration of '%s' : %s", err)
// Determine the filter, this can either be specified on the the command
// line as a value or a file reference. If none is specified the default
@@ -161,14 +162,19 @@
MAAS URL: %s
MAAS API Version: %s
MAAS Query Interval: %s
- Node Filtter: %s
+ Node Filter: %s
Node Name Mappings: %s
Preview: %v
Verbose: %v
Always Rename: %v
- Provision URL: %s `,
+ Provision URL: %s
+ Provision TTL: %s
+ Power Helper: %s
+ Power Helper User: %s
+ Power Helper Host: %s`,
*maasURL, *apiVersion, *queryPeriod, *filterSpec, *mappings, options.Preview,
- options.Verbose, options.AlwaysRename, options.ProvisionURL)
+ options.Verbose, options.AlwaysRename, options.ProvisionURL, options.ProvisionTTL,
+ options.PowerHelper, options.PowerHelperUser, options.PowerHelperHost)
authClient, err := maas.NewAuthenticatedClient(*maasURL, *apiKey, *apiVersion)
if err != nil {
diff --git a/automation/node.go b/automation/node.go
index fe61add..a414820 100644
--- a/automation/node.go
+++ b/automation/node.go
@@ -2,8 +2,10 @@
import (
"fmt"
+ "log"
maas "github.com/juju/gomaasapi"
+ "net/url"
)
// MaasNodeStatus MAAS lifecycle status for nodes
@@ -69,11 +71,28 @@
return id
}
+func (n *MaasNode) PowerType() string {
+ ptype, _ := n.GetString("power_type")
+ return ptype
+}
+
func (n *MaasNode) PowerState() string {
state, _ := n.GetString("power_state")
return state
}
+func (n *MaasNode) UpdatePowerParameters(ptype string, params map[string]string) {
+ values := url.Values{}
+ values.Add("power_type", ptype)
+ for k, v := range params {
+ values.Add("power_parameters_"+k, v)
+ }
+ _, err := n.Update(values)
+ if err != nil {
+ log.Printf("[error] error updating power settings : %s", err.Error())
+ }
+}
+
// Hostname get the hostname
func (n *MaasNode) Hostname() string {
hn, _ := n.GetString("hostname")
diff --git a/automation/ssh-config b/automation/ssh-config
new file mode 100644
index 0000000..990a43d
--- /dev/null
+++ b/automation/ssh-config
@@ -0,0 +1,3 @@
+Host *
+ StrictHostKeyChecking no
+ UserKnownHostsFile=/dev/null
diff --git a/automation/state.go b/automation/state.go
index 4e0dd1f..04626ed 100644
--- a/automation/state.go
+++ b/automation/state.go
@@ -26,6 +26,13 @@
Using Action
}
+type Power struct {
+ Name string `json:"name"`
+ MacAddress string `json:"mac_address"`
+ PowerPassword string `json:"power_password"`
+ PowerAddress string `json:"power_address"`
+}
+
// ProcessingOptions used to determine on what hosts to operate
type ProcessingOptions struct {
Filter struct {
@@ -38,13 +45,16 @@
Exclude []string
}
}
- Mappings map[string]interface{}
- Verbose bool
- Preview bool
- AlwaysRename bool
- ProvTracker Tracker
- ProvisionURL string
- ProvisionTTL time.Duration
+ Mappings map[string]interface{}
+ Verbose bool
+ Preview bool
+ AlwaysRename bool
+ ProvTracker Tracker
+ ProvisionURL string
+ ProvisionTTL time.Duration
+ PowerHelper string
+ PowerHelperUser string
+ PowerHelperHost string
}
// Transitions the actual map
@@ -496,7 +506,40 @@
break
default:
// We are in a state from which we can't move forward.
- log.Printf("ERROR: %s has invalid power state '%s'", node.Hostname(), state)
+ log.Printf("[warn]: %s has invalid power state '%s'", node.Hostname(), state)
+
+ // If a power helper script is set, we have an unknown power state, and
+ // we have not power type then attempt to use the helper script to discover
+ // and set the power settings
+ if options.PowerHelper != "" && node.PowerType() == "" {
+ cmd := exec.Command(options.PowerHelper,
+ append([]string{options.PowerHelperUser, options.PowerHelperHost},
+ node.MACs()...)...)
+ stdout, err := cmd.Output()
+ if err != nil {
+ log.Printf("[error] Failed while executing power helper script '%s' : %s",
+ options.PowerHelper, err)
+ return err
+ }
+ power := Power{}
+ err = json.Unmarshal(stdout, &power)
+ if err != nil {
+ log.Printf("[error] Failed to parse output of power helper script '%s' : %s",
+ options.PowerHelper, err)
+ return err
+ }
+ switch power.Name {
+ case "amt":
+ params := map[string]string{
+ "mac_address": power.MacAddress,
+ "power_pass": power.PowerPassword,
+ "power_address": power.PowerAddress,
+ }
+ node.UpdatePowerParameters(power.Name, params)
+ default:
+ log.Printf("[warn] Unsupported power type discovered '%s'", power.Name)
+ }
+ }
break
}
return nil
@@ -543,7 +586,8 @@
var err error
for _, action := range actions {
if err = action(client, node, options); err != nil {
- log.Printf("[error] Error while processing action for node '%s' : %s", node.Hostname, err)
+ log.Printf("[error] Error while processing action for node '%s' : %s",
+ node.Hostname(), err)
break
}
}
diff --git a/build.gradle b/build.gradle
index f5120d3..3f515a6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -28,6 +28,8 @@
deployConfig = project.hasProperty('deployConfig') ? project.getProperty('deployConfig') : './config/default.yml'
dockerPath = project.hasProperty('dockerPath') ? project.getProperty('dockerPath') : '/usr/bin'
+
+ vboxUser = project.hasProperty('vboxUser') ? project.getProperty('vboxUser') : 'cord'
}
task buildBootstrapImage(type: Exec) {
@@ -219,6 +221,10 @@
.p(config.seedServer.external_ip, "external_ip")
}
+ if (vboxUser != "") {
+ extraVars = extraVars.p(vboxUser, "power_helper_user")
+ }
+
if (config.otherServers) {
extraVars = extraVars.p(config.otherServers.location, "prov_location")
.p(config.otherServers.rolesPath, "prov_role_path")
diff --git a/roles/maas/files/amt.template b/roles/maas/files/amt.template
index 2f2df8c..22c0ccc 100755
--- a/roles/maas/files/amt.template
+++ b/roles/maas/files/amt.template
@@ -9,11 +9,11 @@
get_uuid () {
local DATA=$(echo -n "$1" | sed -e 's/://g')
- echo $(ssh $POWER_PASS@$POWER_ADDRESS vboxmanage list vms | grep "$DATA" | awk '{print $NF}' | sed -e 's/[{}]//g')
+ echo $(ssh $POWER_PASS@$POWER_ADDRESS vboxmanage list vms 2> /tmp/power_err.last | grep "$DATA" | awk '{print $NF}' | sed -e 's/[{}]//g')
}
query_state () {
- local state=$(ssh $POWER_PASS@$POWER_ADDRESS vboxmanage showvminfo $1 | grep "^State" | grep -i running | wc -l)
+ local state=$(ssh $POWER_PASS@$POWER_ADDRESS vboxmanage showvminfo $1 2> /tmp/power_err.last | grep "^State" | grep -i running | wc -l)
if [ $state -eq 1 ]; then
echo 'on'
else
diff --git a/roles/maas/files/power_discovery b/roles/maas/files/power_discovery
new file mode 100755
index 0000000..b691010
--- /dev/null
+++ b/roles/maas/files/power_discovery
@@ -0,0 +1,62 @@
+#!/bin/ash
+
+USERNAME=$1; shift
+HOSTIP=$1; shift
+SSH_KEY="-i /etc/maas/virtualbox/id_rsa"
+
+LIST=$(echo $* | awk '{print toupper($0)}')
+MATCH=
+
+best() {
+ local i
+ local BEST=
+ local BEST_CNT=0
+ for i in $MATCH; do
+ local CNT=$(echo $i | cut -d- -f2)
+ if [ $CNT > $BEST_CNT ]; then
+ BEST=$(echo $i | cut -d- -f1)
+ BEST_CNT=$CNT
+ fi
+ done
+ echo $BEST
+}
+
+inc() {
+ FOUND=$(echo $MATCH | grep "$1" | wc -l)
+ if [ $FOUND -eq 0 ]; then
+ MATCH="$MATCH $1-1"
+ else
+ NEXT=
+ local i=
+ for i in $MATCH; do
+ FOUND=$(echo $i | grep "$1" | wc -l)
+ if [ $FOUND -eq 1 ]; then
+ COUNT=$(echo $i | cut -d- -f2)
+ COUNT=$(expr $COUNT + 1)
+ NEXT="$NEXT $1-$COUNT"
+ else
+ NEXT="$NEXT $i"
+ fi
+ done
+ MATCH=$NEXT
+ fi
+}
+
+for i in $(ssh $SSH_KEY $USERNAME@$HOSTIP /usr/local/bin/vboxmanage list vms | awk '{print $NF}' | sed -e 's/[{}]//g'); do
+ for m in $(ssh $SSH_KEY $USERNAME@$HOSTIP /usr/local/bin/vboxmanage showvminfo --machinereadable $i | grep -i macaddress | cut -d= -f2 | sed -e 's/"//g' -e 's/\(..\)/\1:/g' -e 's/:$//g'); do
+ p=$(echo $i | cut -d- -f5)
+ for t in $LIST; do
+ if [ "$t" == "$m" ]; then
+ inc $p
+ fi
+ done
+ done
+done
+
+BEST=$(best)
+if [ "$BEST x" == " x" ]; then
+ echo "{}"
+else
+ MAC=$(echo $BEST | cut -d- -f5 | sed -e 's/"//g' -e 's/\(..\)/\1:/g' -e 's/:$//g')
+ echo "{\"name\":\"amt\",\"mac_address\":\"$MAC\",\"power_password\":\"$USERNAME\",\"power_address\":\"$HOSTIP\"}"
+fi
diff --git a/roles/maas/files/ssh_config b/roles/maas/files/ssh_config
index f30d239..990a43d 100644
--- a/roles/maas/files/ssh_config
+++ b/roles/maas/files/ssh_config
@@ -1,2 +1,3 @@
Host *
- StrictHostKeyChecking no
+ StrictHostKeyChecking no
+ UserKnownHostsFile=/dev/null
diff --git a/roles/maas/tasks/main.yml b/roles/maas/tasks/main.yml
index 5e7e349..c78cc4e 100644
--- a/roles/maas/tasks/main.yml
+++ b/roles/maas/tasks/main.yml
@@ -292,6 +292,21 @@
tags:
- interface_config
+- name: Default VirtualBox Host
+ become: no
+ set_fact:
+ virtualbox_host: "{{ virtualbox.power_helper_host }}"
+ when: virtualbox_support is defined
+ changed_when: false
+
+- name: Override VirtualBox Host
+ become: no
+ set_fact:
+ virtualbox_host: "{{ discovered_vbox_host.stdout }}"
+ when: virtualbox_support is defined and virtualbox_host == ''
+ changed_when: false
+
+
- name: Custom Automation Compose Configurations
become: yes
template:
diff --git a/roles/maas/tasks/virtualbox.yml b/roles/maas/tasks/virtualbox.yml
index b263886..88dcba7 100644
--- a/roles/maas/tasks/virtualbox.yml
+++ b/roles/maas/tasks/virtualbox.yml
@@ -14,7 +14,34 @@
group: maas
mode: 0755
-- name: Ensure SSH Directory
+- name: Ensure SSH Discovery Directory
+ become: yes
+ file:
+ path: /etc/maas/virtualbox
+ state: directory
+ owner: maas
+ group: maas
+ mode: 0755
+
+- name: VirtualBox Power Discovery Support
+ become: yes
+ copy:
+ src: files/cord_id_rsa
+ dest: /etc/maas/virtualbox/id_rsa
+ owner: root
+ group: root
+ mode: 0600
+
+- name: VirtualBox Power Discovery Script
+ become: yes
+ copy:
+ src: files/power_discovery
+ dest: /etc/maas/virtualbox/power_discovery
+ owner: maas
+ group: maas
+ mode: 0755
+
+- name: Ensure SSH Power Script Directory
become: yes
file:
path: /var/lib/maas/.ssh
@@ -23,7 +50,7 @@
group: maas
mode: 0700
-- name: VirtualBox SSH Support
+- name: VirtualBox Power Script Support
become: yes
copy:
src: files/{{ item.src }}
@@ -36,3 +63,9 @@
- { src: cord_id_rsa.pub, dest: id_rsa.pub }
- { src: ssh_config, dest: config }
+- name: Discover VirtualBox Host
+ become: yes
+ shell: netstat -rn | grep "^0.0.0.0 " | cut -d " " -f10
+ register: discovered_vbox_host
+ changed_when: false
+
diff --git a/roles/maas/templates/automation-compose.yml.j2 b/roles/maas/templates/automation-compose.yml.j2
index d014bec..241214c 100644
--- a/roles/maas/templates/automation-compose.yml.j2
+++ b/roles/maas/templates/automation-compose.yml.j2
@@ -42,8 +42,12 @@
environment:
# need to explicitly set the resolver, else go will skip the /etc/hosts file
- "GODEBUG=netdns=go"
- - "PROVISION_URL=http://provisioner:4243/provision/"
- - "PROVISION_TTL=30m"
+ - "AUTOMATION_PROVISION_URL=http://provisioner:4243/provision/"
+ - "AUTOMATION_PROVISION_TTL=30m"
+ - "AUTOMATION_POWER_HELPER_SCRIPT=/etc/maas/virtualbox/power_discovery"
+ - "AUTOMATION_POWER_HELPER_USER={{ virtualbox.power_helper_user }}"
+ - "AUTOMATION_POWER_HELPER_HOST={{ virtualbox_host }}"
volumes:
- "/etc/maas:/mappings"
+ - "/etc/maas/virtualbox:/etc/maas/virtualbox"
command: [ "-apiVersion", "1.0", "-apikey", "{{ apikey.stdout }}", "-maas", "http://{{ mgmt_ip_address.stdout }}/MAAS", "-period", "30s", "-mappings", "@/mappings/mappings.json", "-always-rename" ]
diff --git a/roles/maas/vars/main.yml b/roles/maas/vars/main.yml
index b7f5a64..f881137 100644
--- a/roles/maas/vars/main.yml
+++ b/roles/maas/vars/main.yml
@@ -1,6 +1,15 @@
accton_as5712_54x: 'http://178.33.61.6/putstorage/DownloadFileHash/AA90EC2D3A5A4A5QQWE3332069EWQS/onie-installer-x86_64-accton_as6712_32x-r0'
accton_as6712_32x: 'http://178.33.61.6/putstorage/DownloadFileHash/AA90EC2D3A5A4A5QQWE3332069EWQS/onie-installer-x86_64-accton_as6712_32x-r0'
+virtualbox:
+ # CHANGE:
+ # 'power_helper_user' specifies the user to use when SSHing to the host
+ # that is running virtualbox
+ # 'power_helper_host' specifies the IP address of host that is running
+ # virtualbox. will be dynamically set if empty string
+ power_helper_user: "{{ power_helper_user | default('cord') }}"
+ power_helper_host: "{{ power_helper_host | default('') }}"
+
maas:
admin_email: "{{ admin_email | default('admin@cord.lab') }}"
user: "{{ maas_user | default('cord') }}"