func (d *Driver) rebootNode(level string) error {
	if d.G5kReuseRefEnvironment {
		return fmt.Errorf("You can't reboot (%s) the node when reusing the Grid'5000 environment", level)
	}
	node, err := d.GetIP()
	if err != nil {
		return fmt.Errorf("Failed to get the node hostname: %s", err.Error())
	}
	op, err := d.g5kAPI.SubmitRebootOperation(api.RebootOperation{
		Kind:  "simple",
		Nodes: []string{node},
		Level: level,
	})
	if err != nil {
		return err
	}
	log.Infof("Reboot (%s) operation for '%s' node have been submitted successfully (workflow id: '%s')", level, node, op.WID)
	return d.waitUntilWorkflowIsDone("reboot", op.WID, node)
}
rebootNode reboot the node with the given level (soft/hard)
package driver
import (
	"encoding/base64"
	"fmt"
	"regexp"
	"time"
	"github.com/Spirals-Team/docker-machine-driver-g5k/api"
	"github.com/docker/machine/libmachine/log"
)
func (d *Driver) checkVpnConfiguration() error {
	if err := CheckSSHConnection(fmt.Sprintf("frontend.%s.grid5000.fr", d.G5kSite)); err != nil {
		return fmt.Errorf("Connection to frontend of '%s' site failed. Please check if the site is not undergoing maintenance and your VPN client is connected and properly configured (see driver documentation for more information)", d.G5kSite)
	}
	return nil
}
func (d *Driver) waitUntilJobIsReady() error {
	log.Info("Waiting for job to run...")
	for {
		job, err := d.g5kAPI.GetJob(d.G5kJobID)
		if err != nil {
			return err
		}
		if job.State == "running" {
			break
		}
		if job.State == "error" || job.State == "terminated" {
			return fmt.Errorf("Can't wait for a job in '%s' state", job.State)
		}
		if job.State == "hold" {
			log.Infof("Job '%s' is in hold state, dont forget to resume it", d.G5kJobID)
		}
		time.Sleep(3 * time.Second)
	}
	log.Info("Job is running")
	return nil
}
func (d *Driver) makeJobSubmission() error {
	jobCommand := "sleep 365d"
	jobTypes := ArrayRemoveDuplicate(append(d.G5kJobTypes, "deploy"))
	if d.G5kReuseRefEnvironment {
		jobTypes = ArrayRemoveEntry(jobTypes, "deploy")
		sshAuthorizedKeysBase64 := base64.StdEncoding.EncodeToString([]byte(GenerateSSHAuthorizedKeys(d.DriverSSHPublicKey, d.ExternalSSHPublicKeys)))
		jobCommand = fmt.Sprint(`sudo-g5k && printf ` + sshAuthorizedKeysBase64 + ` |base64 -d |sudo tee -a /root/.ssh/authorized_keys >/dev/null && sleep 365d`)
	}
	jobID, err := d.g5kAPI.SubmitJob(api.JobRequest{
		Resources:  fmt.Sprintf("nodes=1,walltime=%s", d.G5kWalltime),
		Command:    jobCommand,
		Properties: d.G5kResourceProperties,
		Types:      jobTypes,
		Queue:      d.G5kJobQueue,
	})
	if err != nil {
		return fmt.Errorf("Error when submitting new job: %s", err.Error())
	}
	log.Infof("Job submission have been successfully submitted. (job id: %d)", jobID)
	d.G5kJobID = jobID
	return nil
}
func (d *Driver) makeJobReservation() error {
	jobCommand := "sleep 365d"
	jobTypes := ArrayRemoveDuplicate(append(d.G5kJobTypes, "deploy"))
	jobID, err := d.g5kAPI.SubmitJob(api.JobRequest{
		Resources:   fmt.Sprintf("nodes=1,walltime=%s", d.G5kWalltime),
		Command:     jobCommand,
		Properties:  d.G5kResourceProperties,
		Reservation: d.G5kJobStartTime,
		Types:       jobTypes,
		Queue:       d.G5kJobQueue,
	})
	if err != nil {
		return fmt.Errorf("Error when submitting new job: %s", err.Error())
	}
	log.Infof("Job reservation have been successfully submitted. (job id: %d)", jobID)
	d.G5kJobID = jobID
	return nil
}
func (d *Driver) waitUntilWorkflowIsDone(operation string, wid string, node string) error {
	log.Infof("Waiting for workflow of '%s' operation to finish, it will take a few minutes...", operation)
	for {
		workflow, err := d.g5kAPI.GetOperationWorkflow(operation, wid)
		if err != nil {
			return err
		}
		if ArrayContainsString(workflow.Nodes["ok"], node) {
			break
		}
		if ArrayContainsString(workflow.Nodes["ko"], node) {
			return fmt.Errorf("Workflow for '%s' operation failed for the '%s' node", operation, node)
		}
		if ArrayContainsString(workflow.Nodes["processing"], node) {
			log.Debugf("Workflow for '%s' operation is in processing state for the '%s' node", operation, node)
		}
		time.Sleep(7 * time.Second)
	}
	log.Infof("Workflow for '%s' operation finished successfully for the '%s' node", operation, node)
	return nil
}
func (d *Driver) deployImageToNode() error {
	if d.G5kReuseRefEnvironment {
		log.Infof("Skipping image deployment and reusing Grid'5000 standard environment")
		return nil
	}
	job, err := d.g5kAPI.GetJob(d.G5kJobID)
	if err != nil {
		return fmt.Errorf("Error when getting job (id: '%d') informations: %s", d.G5kJobID, err.Error())
	}
	if !ArrayContainsString(job.Types, "deploy") {
		return fmt.Errorf("The job (id: %d) needs to have the type 'deploy'", d.G5kJobID)
	}
	node, err := d.GetIP()
	if err != nil {
		return fmt.Errorf("Failed to get the node hostname: %s", err.Error())
	}
	if !ArrayContainsString(job.Nodes, node) {
		return fmt.Errorf("The node '%s' is not allocated to the job (id: %d)", node, d.G5kJobID)
	}
	log.Infof("Submitting a new deployment for node '%s'... (image: '%s')", node, d.G5kImage)
	sshAuthorizedKeysBase64 := base64.StdEncoding.EncodeToString([]byte(GenerateSSHAuthorizedKeys(d.DriverSSHPublicKey, d.ExternalSSHPublicKeys)))
	op, err := d.g5kAPI.SubmitDeployment(api.DeploymentOperation{
		Nodes: []string{node},
		Environment: api.DeploymentOperationEnvironment{
			Kind: "database",
			Name: d.G5kImage,
		},
		CustomOperations: map[string]map[string]map[string][]api.DeploymentOperationCustomOperation{
			"BroadcastEnvKascade": {
				"manage_user_post_install": {
					"post-ops": {
						api.DeploymentOperationCustomOperation{
							Name:    "docker_machine_driver_ssh_root_pub_keys",
							Action:  "exec",
							Command: fmt.Sprint(`printf ` + sshAuthorizedKeysBase64 + ` |base64 -d >> $KADEPLOY_ENV_EXTRACTION_DIR/root/.ssh/authorized_keys`),
						},
					},
				},
			},
		},
	})
	if err != nil {
		return fmt.Errorf("Error when submitting new deployment: %s", err.Error())
	}
	log.Infof("Deployment operation for '%s' node have been submitted successfully (workflow id: '%s')", node, op.WID)
	if err = d.waitUntilWorkflowIsDone("deployment", op.WID, node); err != nil {
		return fmt.Errorf("Error when waiting for deployment to finish: %s", err.Error())
	}
	return nil
}
func (d *Driver) getNodePowerState() (string, error) {
	node, err := d.GetIP()
	if err != nil {
		return "", fmt.Errorf("Failed to get the node hostname: %s", err.Error())
	}
	op, err := d.g5kAPI.RequestPowerStatus(node)
	if err != nil {
		return "", fmt.Errorf("Failed to request power status: %s", err.Error())
	}
	if err := d.waitUntilWorkflowIsDone("power", op.WID, node); err != nil {
		return "", err
	}
	states, err := d.g5kAPI.GetOperationStates("power", op.WID)
	if err != nil {
		return "", err
	}
	state, ok := (*states)[node]
	if !ok {
		return "", fmt.Errorf("Failed to retrieve the workflow state of the power status operation")
	}
	re := regexp.MustCompile(`-bmc: (on|off)$`)
	matches := re.FindStringSubmatch(state.Out)
	if matches == nil {
		return "", fmt.Errorf("The BMC status in the workflow state is invalid: %s", state.Out)
	}
	return matches[1], nil
}
func (d *Driver) changeNodePowerStatus(status string, level string) error {
	if d.G5kReuseRefEnvironment {
		return fmt.Errorf("You can't power-%s (%s) the node when reusing the Grid'5000 environment", status, level)
	}
	node, err := d.GetIP()
	if err != nil {
		return fmt.Errorf("Failed to get the node hostname: %s", err.Error())
	}
	op, err := d.g5kAPI.SubmitPowerOperation(api.PowerOperation{
		Nodes:  []string{node},
		Status: status,
		Level:  level,
	})
	if err != nil {
		return err
	}
	log.Infof("Power-%s (%s) operation for '%s' node have been submitted successfully (workflow id: '%s')", status, level, node, op.WID)
	return d.waitUntilWorkflowIsDone("power", op.WID, node)
}