/*
// Copyright (c) 2016 Intel Corporation
//
// 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

package payloads

import (
	"github.com/golang/glog"
)

// StartFailureReason denotes the underlying error that prevented
// an SSNTP START command from launching a new instance on a CN
// or a NN.  Most, but not all, of these errors are returned by
// ciao-launcher
type StartFailureReason string

const (
	// FullCloud is returned by the scheduler when all nodes in the cluster
	// are FULL and it is unable to satisfy a START request.
	FullCloud StartFailureReason = "full_cloud"

	// FullComputeNode indicates that the node to which the START command
	// was sent had insufficient resources to start the requested instance.
	FullComputeNode = "full_cn"

	// NodeInMaintenance indicates that the node to which the START command
	// was sent cannot host the instance as it is currently in maintenance
	// mode.
	NodeInMaintenance = "node_maintenance"

	// NoComputeNodes is returned by the scheduler if no compute nodes are
	// running in the cluster upon which the instance can be started.
	NoComputeNodes = "no_cn"

	// NoNetworkNodes is returned by the scheduler if no network nodes are
	// running in the cluster upon which the instance can be started.
	NoNetworkNodes = "no_net_cn"

	// InvalidPayload indicates that the contents of the START payload are
	// corrupt
	InvalidPayload = "invalid_payload"

	// InvalidData indicates that the start section of the payload is
	// corrupt or missing information
	InvalidData = "invalid_data"

	// AlreadyRunning is returned when an attempt is made to start an
	// instance on a node upon which that very same instance is already
	// running.
	AlreadyRunning = "already_running"

	// InstanceExists is returned when an attempt is made to start an
	// instance on a node upon which that very same instance already
	// exists but is not currently running.
	InstanceExists = "instance_exists"

	// ImageFailure indicates that ciao-launcher is unable to locate
	// the rootfs for the instance, e.g., a VM instance is started
	// with no bootable volumes or a containers image cannot be
	// downloaded.
	ImageFailure = "image_failure"

	// LaunchFailure indicates that the instance has been successfully
	// created but could not be launched.  Actually, this is sort of an
	// odd situation as the START command partially succeeded.
	// ciao-launcher returns an error code, but the instance has been
	// created and could be booted a later stage via the RESTART command.
	LaunchFailure = "launch_failure"

	// NetworkFailure indicates that it was not possible to initialise
	// networking for the instance.
	NetworkFailure = "network_failure"
)

// ErrorStartFailure represents the unmarshalled version of the contents of a
// SSNTP ERROR frame whose type is set to ssntp.StartFailure.
type ErrorStartFailure struct {
	// NodeUUID is the UUID of the node that generated this error.
	NodeUUID string `yaml:"node_uuid"`

	// InstanceUUID is the UUID of the instance that could not be started.
	InstanceUUID string `yaml:"instance_uuid"`

	// Reason provides the reason for the start failure, e.g.,
	// LaunchFailure.
	Reason StartFailureReason `yaml:"reason"`

	// Restart is true if the failed start command was attempting to
	// restart an existing instance.
	Restart bool
}

func (r StartFailureReason) String() string {
	switch r {
	case FullCloud:
		return "Cloud is full"
	case FullComputeNode:
		return "Compute node is full"
	case NodeInMaintenance:
		return "Node is undergoing maintenance"
	case NoComputeNodes:
		return "No compute node available"
	case NoNetworkNodes:
		return "No network node available"
	case InvalidPayload:
		return "YAML payload is corrupt"
	case InvalidData:
		return "Command section of YAML payload is corrupt or missing required information"
	case AlreadyRunning:
		return "Instance is already running"
	case InstanceExists:
		return "Instance already exists"
	case ImageFailure:
		return "Failed to create instance image"
	case LaunchFailure:
		return "Failed to launch instance"
	case NetworkFailure:
		return "Failed to create VNIC for instance"
	}

	return ""
}

// IsFatal indicates that the failure should be treated as a fatal failure
// indicating the instance did not start.
func (r StartFailureReason) IsFatal() bool {
	switch r {
	case FullCloud,
		FullComputeNode,
		NodeInMaintenance,
		NoComputeNodes,
		NoNetworkNodes,
		InvalidPayload,
		InvalidData,
		ImageFailure,
		LaunchFailure,
		NetworkFailure:
		return true

	case AlreadyRunning,
		InstanceExists:
		return false
	}

	glog.Errorf("Unexpected StartFailureReason: %s", r)
	return false
}
