mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-20 09:30:42 +08:00
641 lines
18 KiB
Go
641 lines
18 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// 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 zstack
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/util/osprofile"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
api "yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
"yunion.io/x/onecloud/pkg/multicloud"
|
|
"yunion.io/x/onecloud/pkg/util/billing"
|
|
)
|
|
|
|
type SInstanceCdrome struct {
|
|
}
|
|
|
|
type SInstance struct {
|
|
multicloud.SInstanceBase
|
|
host *SHost
|
|
|
|
ZStackBasic
|
|
ZoneUUID string `json:"zoneUuid"`
|
|
ClusterUUID string `json:"clusterUuid"`
|
|
HostUUID string `json:"hostUuid"`
|
|
LastHostUUID string `json:"lastHostUuid"`
|
|
RootVolumeUUID string `json:"rootVolumeUuid"`
|
|
Platform string `json:"platform"`
|
|
InstanceOfferingUUID string `json:"instanceOfferingUuid"`
|
|
|
|
DefaultL3NetworkUUID string `json:"defaultL3NetworkUuid"`
|
|
Type string `json:"type"`
|
|
HypervisorType string `json:"hypervisorType"`
|
|
MemorySize int `json:"memorySize"`
|
|
CPUNum int `json:"cpuNum"`
|
|
CPUSpeed int `json:"cpuSpeed"`
|
|
State string `json:"state"`
|
|
InternalID string `json:"internalId"`
|
|
VMNics []SInstanceNic `json:"vmNics"`
|
|
AllVolumes []SDisk `json:"allVolumes"`
|
|
VMCdRoms []SInstanceCdrome `json:"vmCdRoms"`
|
|
ZStackTime
|
|
}
|
|
|
|
func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) {
|
|
instance := &SInstance{}
|
|
err := region.client.getResource("vm-instances", instanceId, instance)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if instance.State == "Destroyed" {
|
|
return nil, cloudprovider.ErrNotFound
|
|
}
|
|
return instance, nil
|
|
}
|
|
|
|
func (region *SRegion) GetInstances(hostId string, instanceId string, nicId string) ([]SInstance, error) {
|
|
instance := []SInstance{}
|
|
params := url.Values{}
|
|
params.Add("q", "type=UserVm")
|
|
params.Add("q", "state!=Destroyed")
|
|
if len(hostId) > 0 {
|
|
params.Add("q", "lastHostUuid="+hostId)
|
|
}
|
|
if len(instanceId) > 0 {
|
|
params.Add("q", "uuid="+instanceId)
|
|
}
|
|
if len(nicId) > 0 {
|
|
params.Add("q", "vmNics.uuid="+nicId)
|
|
}
|
|
if SkipEsxi {
|
|
params.Add("q", "hypervisorType!=ESX")
|
|
}
|
|
return instance, region.client.listAll("vm-instances", params, &instance)
|
|
}
|
|
|
|
func (instance *SInstance) GetSecurityGroupIds() ([]string, error) {
|
|
ids := []string{}
|
|
secgroups, err := instance.host.zone.region.GetSecurityGroups("", instance.UUID, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, secgroup := range secgroups {
|
|
ids = append(ids, secgroup.UUID)
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (instance *SInstance) GetMetadata() *jsonutils.JSONDict {
|
|
data := jsonutils.NewDict()
|
|
|
|
return data
|
|
}
|
|
|
|
func (instance *SInstance) GetIHost() cloudprovider.ICloudHost {
|
|
return instance.host
|
|
}
|
|
|
|
func (instance *SInstance) GetIHostId() string {
|
|
if len(instance.LastHostUUID) > 0 {
|
|
return instance.LastHostUUID
|
|
}
|
|
return instance.HostUUID
|
|
}
|
|
|
|
func (instance *SInstance) GetId() string {
|
|
return instance.UUID
|
|
}
|
|
|
|
func (instance *SInstance) GetName() string {
|
|
return instance.Name
|
|
}
|
|
|
|
func (instance *SInstance) GetGlobalId() string {
|
|
return instance.GetId()
|
|
}
|
|
|
|
func (instance *SInstance) IsEmulated() bool {
|
|
return false
|
|
}
|
|
|
|
func (instance *SInstance) GetInstanceType() string {
|
|
if len(instance.InstanceOfferingUUID) > 0 {
|
|
offer, err := instance.host.zone.region.GetInstanceOffering(instance.InstanceOfferingUUID)
|
|
if err == nil {
|
|
return offer.Name
|
|
}
|
|
}
|
|
return instance.Type
|
|
}
|
|
|
|
func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
|
idisks := []cloudprovider.ICloudDisk{}
|
|
rootDisk, err := instance.host.zone.region.GetDiskWithStorage(instance.RootVolumeUUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
idisks = append(idisks, rootDisk)
|
|
for i := 0; i < len(instance.AllVolumes); i++ {
|
|
if instance.AllVolumes[i].UUID != instance.RootVolumeUUID {
|
|
dataDisk, err := instance.host.zone.region.GetDiskWithStorage(instance.AllVolumes[i].UUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
idisks = append(idisks, dataDisk)
|
|
}
|
|
}
|
|
return idisks, nil
|
|
}
|
|
|
|
func (instance *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
|
|
iNics := []cloudprovider.ICloudNic{}
|
|
for i := 0; i < len(instance.VMNics); i++ {
|
|
instance.VMNics[i].instance = instance
|
|
iNics = append(iNics, &instance.VMNics[i])
|
|
}
|
|
return iNics, nil
|
|
}
|
|
|
|
func (instance *SInstance) GetVcpuCount() int {
|
|
return instance.CPUNum
|
|
}
|
|
|
|
func (instance *SInstance) GetVmemSizeMB() int {
|
|
return instance.MemorySize / 1024 / 1024
|
|
}
|
|
|
|
func (instance *SInstance) GetBootOrder() string {
|
|
return instance.host.zone.region.GetBootOrder(instance.UUID)
|
|
}
|
|
|
|
func (region *SRegion) GetBootOrder(instanceId string) string {
|
|
resp, err := region.client.get("vm-instances", instanceId, "boot-orders")
|
|
if err != nil {
|
|
return "dcn"
|
|
}
|
|
orders := []string{}
|
|
err = resp.Unmarshal(&orders, "orders")
|
|
if err != nil {
|
|
return "dcn"
|
|
}
|
|
order := ""
|
|
for _, _order := range orders {
|
|
switch _order {
|
|
case "CdRom":
|
|
order += "c"
|
|
case "HardDisk":
|
|
order += "d"
|
|
default:
|
|
log.Errorf("Unknown BootOrder %s for instance %s", _order, instanceId)
|
|
}
|
|
}
|
|
return order
|
|
}
|
|
|
|
func (instance *SInstance) GetVga() string {
|
|
return "std"
|
|
}
|
|
|
|
func (instance *SInstance) GetVdi() string {
|
|
return "vnc"
|
|
}
|
|
|
|
func (instance *SInstance) GetOSType() string {
|
|
return osprofile.NormalizeOSType(instance.Platform)
|
|
}
|
|
|
|
func (instance *SInstance) GetOSName() string {
|
|
return instance.Platform
|
|
}
|
|
|
|
func (instance *SInstance) GetBios() string {
|
|
return "BIOS"
|
|
}
|
|
|
|
func (instance *SInstance) GetMachine() string {
|
|
return "pc"
|
|
}
|
|
|
|
func (instance *SInstance) GetStatus() string {
|
|
switch instance.State {
|
|
case "Stopped":
|
|
return api.VM_READY
|
|
case "Running":
|
|
return api.VM_RUNNING
|
|
case "Destroyed":
|
|
return api.VM_DEALLOCATED
|
|
default:
|
|
log.Errorf("Unknown instance %s status %s", instance.Name, instance.State)
|
|
return api.VM_UNKNOWN
|
|
}
|
|
}
|
|
|
|
func (instance *SInstance) Refresh() error {
|
|
new, err := instance.host.zone.region.GetInstance(instance.UUID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return jsonutils.Update(instance, new)
|
|
}
|
|
|
|
func (instance *SInstance) GetHypervisor() string {
|
|
return api.HYPERVISOR_ZSTACK
|
|
}
|
|
|
|
func (instance *SInstance) StartVM(ctx context.Context) error {
|
|
err := instance.host.zone.region.StartVM(instance.UUID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return cloudprovider.WaitStatus(instance, api.VM_RUNNING, 5*time.Second, 5*time.Minute)
|
|
}
|
|
|
|
func (region *SRegion) StartVM(instanceId string) error {
|
|
params := map[string]interface{}{
|
|
"startVmInstance": jsonutils.NewDict(),
|
|
}
|
|
_, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
|
|
return err
|
|
}
|
|
|
|
func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error {
|
|
err := instance.host.zone.region.StopVM(instance.UUID, isForce)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return cloudprovider.WaitStatus(instance, api.VM_READY, 5*time.Second, 5*time.Minute)
|
|
}
|
|
|
|
func (region *SRegion) StopVM(instanceId string, isForce bool) error {
|
|
option := "grace"
|
|
if isForce {
|
|
option = "cold"
|
|
}
|
|
params := map[string]interface{}{
|
|
"stopVmInstance": map[string]string{
|
|
"type": option,
|
|
"stopHA": "true",
|
|
},
|
|
}
|
|
_, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
|
|
return err
|
|
}
|
|
|
|
func (instance *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
|
|
info, err := instance.host.zone.region.GetInstanceConsoleInfo(instance.UUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authURL, _ := url.Parse(instance.host.zone.region.client.authURL)
|
|
url := fmt.Sprintf("%s://%s:5000/thirdparty/vnc_auto.html?host=%s&port=%d&token=%s&title=%s", info.Scheme, authURL.Hostname(), info.Hostname, info.Port, info.Token, instance.Name)
|
|
password, _ := instance.host.zone.region.GetInstanceConsolePassword(instance.UUID)
|
|
if len(password) > 0 {
|
|
url = url + fmt.Sprintf("&password=%s", password)
|
|
}
|
|
return jsonutils.Marshal(map[string]string{
|
|
"url": url,
|
|
"protocol": "zstack",
|
|
"instance_id": instance.UUID,
|
|
}), nil
|
|
}
|
|
|
|
func (instance *SInstance) UpdateVM(ctx context.Context, name string) error {
|
|
params := map[string]interface{}{
|
|
"updateVmInstance": map[string]string{
|
|
"name": name,
|
|
},
|
|
}
|
|
return instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params))
|
|
}
|
|
|
|
func (region *SRegion) UpdateVM(instanceId string, params jsonutils.JSONObject) error {
|
|
_, err := region.client.put("vm-instances", instanceId, params)
|
|
return err
|
|
}
|
|
|
|
func (instance *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
|
|
if instance.Name != name || instance.Description != description {
|
|
params := map[string]interface{}{
|
|
"updateVmInstance": map[string]string{
|
|
"name": name,
|
|
"description": description,
|
|
},
|
|
}
|
|
err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(password) > 0 {
|
|
params := map[string]interface{}{
|
|
"changeVmPassword": map[string]string{
|
|
"account": username,
|
|
"password": password,
|
|
},
|
|
}
|
|
err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(publicKey) > 0 {
|
|
params := map[string]interface{}{
|
|
"setVmSshKey": map[string]string{
|
|
"SshKey": publicKey,
|
|
},
|
|
}
|
|
err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if deleteKeypair {
|
|
err := instance.host.zone.region.client.delete("vm-instances", fmt.Sprintf("%s/ssh-keys", instance.UUID), "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (instance *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
|
return instance.host.zone.region.RebuildRoot(instance.UUID, desc.ImageId, desc.SysSizeGB)
|
|
}
|
|
|
|
func (region *SRegion) RebuildRoot(instanceId, imageId string, sysSizeGB int) (string, error) {
|
|
params := map[string]interface{}{
|
|
"changeVmImage": map[string]string{
|
|
"imageUuid": imageId,
|
|
},
|
|
}
|
|
instance := &SInstance{}
|
|
resp, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = resp.Unmarshal(instance, "inventory")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
disk, err := region.GetDisk(instance.RootVolumeUUID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if sysSizeGB > disk.GetDiskSizeMB()*1024 {
|
|
return instance.RootVolumeUUID, region.ResizeDisk(disk.UUID, int64(sysSizeGB)*1024)
|
|
}
|
|
return instance.RootVolumeUUID, nil
|
|
}
|
|
|
|
func (instance *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
|
|
offerings, err := instance.host.zone.region.GetInstanceOfferings("", "", config.Cpu, config.MemoryMB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(config.InstanceType) > 0 {
|
|
for _, offering := range offerings {
|
|
if offering.Name == config.InstanceType {
|
|
return instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID)
|
|
}
|
|
}
|
|
offering, err := instance.host.zone.region.CreateInstanceOffering(config.InstanceType, config.Cpu, config.MemoryMB, "UserVm")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID)
|
|
}
|
|
for _, offering := range offerings {
|
|
log.Debugf("try instance offering %s(%s) ...", offering.Name, offering.UUID)
|
|
err := instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID)
|
|
if err != nil {
|
|
log.Errorf("failed to change config for instance %s(%s) error: %v", instance.Name, instance.UUID, err)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("Failed to change vm config, specification not supported")
|
|
}
|
|
|
|
func (region *SRegion) ChangeConfig(instanceId, offeringId string) error {
|
|
params := map[string]interface{}{
|
|
"changeInstanceOffering": map[string]string{
|
|
"instanceOfferingUuid": offeringId,
|
|
},
|
|
}
|
|
return region.UpdateVM(instanceId, jsonutils.Marshal(params))
|
|
}
|
|
|
|
func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
|
return instance.host.zone.region.AttachDisk(instance.UUID, diskId)
|
|
}
|
|
|
|
func (region *SRegion) AttachDisk(instanceId string, diskId string) error {
|
|
_, err := region.client.post(fmt.Sprintf("volumes/%s/vm-instances/%s", diskId, instanceId), jsonutils.NewDict())
|
|
return err
|
|
}
|
|
|
|
func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
|
return instance.host.zone.region.DetachDisk(instance.UUID, diskId)
|
|
}
|
|
|
|
func (region *SRegion) DetachDisk(instanceId, diskId string) error {
|
|
url := fmt.Sprintf("volumes/%s/vm-instances?vmUuid=%s", diskId, instanceId)
|
|
err := region.client.delete(url, "", "")
|
|
if err != nil && strings.Contains(err.Error(), "is not attached to any vm") {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (instance *SInstance) DeleteVM(ctx context.Context) error {
|
|
return instance.host.zone.region.DeleteVM(instance.UUID)
|
|
}
|
|
|
|
func (region *SRegion) DeleteVM(instanceId string) error {
|
|
err := region.client.delete("vm-instances", instanceId, "Enforcing")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
params := map[string]interface{}{
|
|
"expungeVmInstance": jsonutils.NewDict(),
|
|
}
|
|
_, err = region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
|
|
return err
|
|
}
|
|
|
|
func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
|
eips, err := instance.host.zone.region.GetEips("", instance.UUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(eips) == 0 {
|
|
return nil, cloudprovider.ErrNotFound
|
|
}
|
|
if len(eips) == 1 {
|
|
return &eips[0], nil
|
|
}
|
|
return nil, cloudprovider.ErrDuplicateId
|
|
}
|
|
|
|
func (instance *SInstance) AssignSecurityGroup(secgroupId string) error {
|
|
return instance.host.zone.region.AssignSecurityGroup(instance.UUID, secgroupId)
|
|
}
|
|
|
|
func (instance *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
|
currentIds, err := instance.GetSecurityGroupIds()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, id := range currentIds {
|
|
if !utils.IsInStringArray(id, secgroupIds) {
|
|
err := instance.host.zone.region.RevokeSecurityGroup(instance.UUID, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for _, id := range secgroupIds {
|
|
if !utils.IsInStringArray(id, currentIds) {
|
|
err := instance.host.zone.region.AssignSecurityGroup(instance.UUID, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (region *SRegion) AssignSecurityGroup(instanceId, secgroupId string) error {
|
|
instance, err := region.GetInstance(instanceId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
secgroup, err := region.GetSecurityGroup(secgroupId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(instance.VMNics) > 0 {
|
|
if !utils.IsInStringArray(instance.VMNics[0].L3NetworkUUID, secgroup.AttachedL3NetworkUUIDs) {
|
|
resource := fmt.Sprintf("security-groups/%s/l3-networks/%s", secgroupId, instance.VMNics[0].L3NetworkUUID)
|
|
_, err := region.client.post(resource, jsonutils.NewDict())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
params := map[string]interface{}{
|
|
"params": map[string]interface{}{
|
|
"vmNicUuids": []string{instance.VMNics[0].UUID},
|
|
},
|
|
}
|
|
resource := fmt.Sprintf("security-groups/%s/vm-instances/nics", secgroupId)
|
|
_, err = region.client.post(resource, jsonutils.Marshal(params))
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (region *SRegion) RevokeSecurityGroup(instanceId, secgroupId string) error {
|
|
instance, err := region.GetInstance(instanceId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, nic := range instance.VMNics {
|
|
resource := fmt.Sprintf("security-groups/%s/vm-instances/nics?vmNicUuids=%s", secgroupId, nic.UUID)
|
|
err := region.client.delete(resource, "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (instance *SInstance) GetBillingType() string {
|
|
return ""
|
|
}
|
|
|
|
func (instance *SInstance) GetCreatedAt() time.Time {
|
|
return instance.CreateDate
|
|
}
|
|
|
|
func (instance *SInstance) GetExpiredAt() time.Time {
|
|
return time.Time{}
|
|
}
|
|
|
|
func (instance *SInstance) UpdateUserData(userData string) error {
|
|
return cloudprovider.ErrNotSupported
|
|
}
|
|
|
|
func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
|
return cloudprovider.ErrNotSupported
|
|
}
|
|
|
|
func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
|
|
return cloudprovider.ErrNotSupported
|
|
}
|
|
|
|
func (instance *SInstance) GetProjectId() string {
|
|
return ""
|
|
}
|
|
|
|
func (instance *SInstance) GetError() error {
|
|
return nil
|
|
}
|
|
|
|
type SConsoleInfo struct {
|
|
Scheme string `json:"scheme"`
|
|
Hostname string `json:"hostname"`
|
|
Port int `json:"port"`
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
func (region *SRegion) GetInstanceConsoleInfo(instnaceId string) (*SConsoleInfo, error) {
|
|
params := map[string]interface{}{
|
|
"params": map[string]string{
|
|
"vmInstanceUuid": instnaceId,
|
|
},
|
|
}
|
|
resp, err := region.client.post("consoles", jsonutils.Marshal(params))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
info := &SConsoleInfo{}
|
|
err = resp.Unmarshal(info, "inventory")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (region *SRegion) GetInstanceConsolePassword(instnaceId string) (string, error) {
|
|
resp, err := region.client.get("vm-instances", instnaceId, "console-passwords")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if resp.Contains("consolePassword") {
|
|
return resp.GetString("consolePassword")
|
|
}
|
|
return "", nil
|
|
}
|