airshipctl/pkg/remote/remote_direct.go
Alan Meadows e2cca32748 Add Redfish Authentication Support
This commit also introduces a dochelper concept.  This provides
some convenience methods to the document pkg that help extract data
from known document types as well as walk document relationships to
discover related information such as BMC credentials for baremetal
hosts.

Once merged, a follow up patchset will leverage these within the
cloud-init code to deduplicate some of these lookups.

Change-Id: Ie6a770ce4b34adbea30281917f0cb2fdc460b4fb
2020-03-27 15:29:07 -07:00

138 lines
3.2 KiB
Go

package remote
import (
"fmt"
"net/url"
"strings"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/environment"
alog "opendev.org/airship/airshipctl/pkg/log"
"opendev.org/airship/airshipctl/pkg/remote/redfish"
)
const (
AirshipRemoteTypeRedfish string = "redfish"
)
// Interface to be implemented by remoteDirect implementation
type Client interface {
DoRemoteDirect() error
}
// Get remotedirect client based on config
func getRemoteDirectClient(
remoteConfig *config.RemoteDirect,
remoteURL string,
username string,
password string) (Client, error) {
var client Client
switch remoteConfig.RemoteType {
case AirshipRemoteTypeRedfish:
alog.Debug("Remote type redfish")
rfURL, err := url.Parse(remoteURL)
if err != nil {
return nil, err
}
baseURL := fmt.Sprintf("%s://%s", rfURL.Scheme, rfURL.Host)
schemeSplit := strings.Split(rfURL.Scheme, redfish.RedfishURLSchemeSeparator)
if len(schemeSplit) > 1 {
baseURL = fmt.Sprintf("%s://%s", schemeSplit[len(schemeSplit)-1], rfURL.Host)
}
urlPath := strings.Split(rfURL.Path, "/")
nodeID := urlPath[len(urlPath)-1]
client, err = redfish.NewRedfishRemoteDirectClient(
baseURL,
nodeID,
username,
password,
remoteConfig.IsoURL,
remoteConfig.Insecure,
remoteConfig.UseProxy,
)
if err != nil {
alog.Debugf("redfish remotedirect client creation failed")
return nil, err
}
default:
return nil, NewRemoteDirectErrorf("invalid remote type")
}
return client, nil
}
func getRemoteDirectConfig(settings *environment.AirshipCTLSettings) (
remoteConfig *config.RemoteDirect,
remoteURL string,
username string,
password string,
err error) {
cfg := settings.Config()
bootstrapSettings, err := cfg.CurrentContextBootstrapInfo()
if err != nil {
return nil, "", "", "", err
}
remoteConfig = bootstrapSettings.RemoteDirect
if remoteConfig == nil {
return nil, "", "", "", config.ErrMissingConfig{What: "RemoteDirect options not defined in bootstrap config"}
}
bundlePath, err := cfg.CurrentContextEntryPoint(config.Ephemeral, "")
if err != nil {
return nil, "", "", "", err
}
docBundle, err := document.NewBundleByPath(bundlePath)
if err != nil {
return nil, "", "", "", err
}
selector := document.NewEphemeralBMHSelector()
doc, err := docBundle.SelectOne(selector)
if err != nil {
return nil, "", "", "", err
}
remoteURL, err = document.GetBMHBMCAddress(doc)
if err != nil {
return nil, "", "", "", err
}
username, password, err = document.GetBMHBMCCredentials(doc, docBundle)
if err != nil {
return nil, "", "", "", err
}
return remoteConfig, remoteURL, username, password, nil
}
// Top level function to execute remote direct based on remote type
func DoRemoteDirect(settings *environment.AirshipCTLSettings) error {
remoteConfig, remoteURL, username, password, err := getRemoteDirectConfig(settings)
if err != nil {
return err
}
client, err := getRemoteDirectClient(remoteConfig, remoteURL, username, password)
if err != nil {
return err
}
err = client.DoRemoteDirect()
if err != nil {
alog.Debugf("remote direct failed: %s", err)
return err
}
alog.Print("Remote direct successfully completed")
return nil
}