1// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package vmomi is in a separate package to avoid the transitive inclusion of govmomi
16// as a fundamental dependency of the main extraconfig
17package vmomi
18
19import (
20	"fmt"
21
22	"github.com/vmware/govmomi/vim25/types"
23	"github.com/vmware/vic/pkg/vsphere/extraconfig"
24)
25
26// OptionValueMap returns a map from array of OptionValues
27func OptionValueMap(src []types.BaseOptionValue) map[string]string {
28	// create the key/value store from the extraconfig slice for lookups
29	kv := make(map[string]string)
30	for i := range src {
31		k := src[i].GetOptionValue().Key
32		v := src[i].GetOptionValue().Value.(string)
33		kv[k] = UnescapeNil(v)
34	}
35	return kv
36}
37
38// OptionValueSource is a convenience method to generate a MapSource source from
39// and array of OptionValue's
40func OptionValueSource(src []types.BaseOptionValue) extraconfig.DataSource {
41	kv := OptionValueMap(src)
42	return extraconfig.MapSource(kv)
43}
44
45// OptionValueFromMap is a convenience method to convert a map into a BaseOptionValue array
46// escapeNil - if true a nil string is replaced with "<nil>". Allows us to distinguish between
47// deletion and nil as a value
48func OptionValueFromMap(data map[string]string, escape bool) []types.BaseOptionValue {
49	if len(data) == 0 {
50		return nil
51	}
52
53	array := make([]types.BaseOptionValue, len(data))
54
55	i := 0
56	for k, v := range data {
57		if escape {
58			v = EscapeNil(v)
59		}
60		array[i] = &types.OptionValue{Key: k, Value: v}
61		i++
62	}
63
64	return array
65}
66
67// OptionValueArrayToString translates the options array in to a Go formatted structure dump
68func OptionValueArrayToString(options []types.BaseOptionValue) string {
69	// create the key/value store from the extraconfig slice for lookups
70	kv := make(map[string]string)
71	for i := range options {
72		k := options[i].GetOptionValue().Key
73		v := options[i].GetOptionValue().Value.(string)
74		kv[k] = v
75	}
76
77	return fmt.Sprintf("%#v", kv)
78}
79
80// OptionValueUpdatesFromMap generates an optionValue array for those entries in the map that do not
81// already exist, are changed from the reference array, or a removed
82// A removed entry will have a nil string for the value
83// NOTE: DOES NOT CURRENTLY SUPPORT DELETION OF KEYS - KEYS MISSING FROM NEW MAP ARE IGNORED
84func OptionValueUpdatesFromMap(existing []types.BaseOptionValue, new map[string]string) []types.BaseOptionValue {
85	e := len(existing)
86	if e == 0 {
87		return OptionValueFromMap(new, true)
88	}
89
90	n := len(new)
91	updates := make(map[string]string, n+e)
92	unchanged := make(map[string]struct{}, n+e)
93
94	// first the existing keys
95	for i := range existing {
96		v := existing[i].GetOptionValue()
97		if nV, ok := new[v.Key]; ok && nV == v.Value.(string) {
98			unchanged[v.Key] = struct{}{}
99			// no change
100			continue
101		} else if ok {
102			// changed
103			updates[v.Key] = EscapeNil(nV)
104		} else {
105			// deletion
106			// NOTE: ignored as this also deletes non VIC entries currently
107			// there's no prefix for the non-guestinfo keys so cannot easily filter
108			// updates[v.Key] = ""
109		}
110	}
111
112	// now the new keys
113	for k, v := range new {
114		if _, ok := unchanged[k]; ok {
115			continue
116		}
117
118		if _, ok := updates[k]; !ok {
119			updates[k] = EscapeNil(v)
120		}
121	}
122
123	return OptionValueFromMap(updates, false)
124}
125
126func EscapeNil(input string) string {
127	if input == "" {
128		return "<nil>"
129	}
130
131	return input
132}
133
134func UnescapeNil(input string) string {
135	if input == "<nil>" {
136		return ""
137	}
138
139	return input
140}
141