utils.go raw
1 package ibclient
2
3 import (
4 "encoding/json"
5 "fmt"
6 "net"
7 "net/url"
8 "reflect"
9 "regexp"
10 "strings"
11
12 "golang.org/x/net/idna"
13 )
14
15 type NotFoundError struct {
16 msg string
17 }
18
19 func (e *NotFoundError) Error() string {
20 return e.msg
21 }
22
23 func NewNotFoundError(msg string) *NotFoundError {
24 return &NotFoundError{msg: msg}
25 }
26
27 type GenericObj interface {
28 ObjectType() string
29 ReturnFields() []string
30 EaSearch() EASearch
31 SetReturnFields([]string)
32 }
33
34 // Handle []NameServer to be [] list
35 type NullableNameServers struct {
36 NameServers []NameServer
37 IsNull bool
38 }
39
40 func (d Dhcpoption) MarshalJSON() ([]byte, error) {
41 type Alias Dhcpoption
42 // List of allowed names
43 allowedNames := map[string]bool{
44 "routers": true,
45 "router-templates": true,
46 "domain-name-servers": true,
47 "domain-name": true,
48 "broadcast-address": true,
49 "broadcast-address-offset": true,
50 "dhcp-lease-time": true,
51 "dhcp6.name-servers": true,
52 }
53 aux := &struct {
54 Value string `json:"value"`
55 UseOption *bool `json:"use_option,omitempty"`
56 *Alias
57 }{
58 Value: d.Value,
59 Alias: (*Alias)(&d),
60 }
61 if allowedNames[d.Name] {
62 aux.UseOption = &d.UseOption
63 }
64 return json.Marshal(aux)
65 }
66
67 func (d *Dhcpoption) UnmarshalJSON(data []byte) error {
68 type Alias Dhcpoption
69 aux := &struct {
70 Value string `json:"value"`
71 UseOption bool `json:"use_option"`
72 *Alias
73 }{
74 Alias: (*Alias)(d),
75 }
76
77 if err := json.Unmarshal(data, &aux); err != nil {
78 return err
79 }
80
81 d.Value = aux.Value
82 d.UseOption = aux.UseOption
83 return nil
84 }
85
86 func (ns NullableNameServers) MarshalJSON() ([]byte, error) {
87 if reflect.DeepEqual(ns.NameServers, []NameServer{}) {
88 return []byte("[]"), nil
89 }
90
91 return json.Marshal(ns.NameServers)
92 }
93
94 func (ns *NullableNameServers) UnmarshalJSON(data []byte) error {
95 if string(data) == "null" {
96 ns.IsNull = true
97 ns.NameServers = nil
98 return nil
99 }
100 ns.IsNull = false
101 return json.Unmarshal(data, &ns.NameServers)
102 }
103
104 func BuildNetworkViewFromRef(ref string) *NetworkView {
105 // networkview/ZG5zLm5ldHdvcmtfdmlldyQyMw:global_view/false
106 r := regexp.MustCompile(`networkview/\w+:([^/]+)/\w+`)
107 m := r.FindStringSubmatch(ref)
108
109 if m == nil {
110 return nil
111 }
112
113 return &NetworkView{
114 Ref: ref,
115 Name: &m[1],
116 }
117 }
118
119 func getNetworkObjectType(isIPv6 bool, ipv4Object string, ipv6Object string) string {
120 if isIPv6 {
121 return ipv6Object
122 }
123 return ipv4Object
124 }
125
126 func BuildNetworkFromRef(ref string) (*Network, error) {
127 // network/ZG5zLm5ldHdvcmskODkuMC4wLjAvMjQvMjU:89.0.0.0/24/global_view
128 r := regexp.MustCompile(`network/\w+:(\d+\.\d+\.\d+\.\d+/\d+)/(.+)`)
129 m := r.FindStringSubmatch(ref)
130
131 if m == nil {
132 return nil, fmt.Errorf("CIDR format not matched")
133 }
134
135 newNet := NewNetwork(m[2], m[1], false, "", nil)
136 newNet.Ref = ref
137 return newNet, nil
138 }
139
140 func BuildNetworkContainerFromRef(ref string) (*NetworkContainer, error) {
141 // networkcontainer/ZG5zLm5ldHdvcmskODkuMC4wLjAvMjQvMjU:89.0.0.0/24/global_view
142 r := regexp.MustCompile(`networkcontainer/\w+:(\d+\.\d+\.\d+\.\d+/\d+)/(.+)`)
143 m := r.FindStringSubmatch(ref)
144
145 if m == nil {
146 return nil, fmt.Errorf("CIDR format not matched")
147 }
148
149 newNet := NewNetworkContainer(m[2], m[1], false, "", nil)
150 newNet.Ref = ref
151 return newNet, nil
152 }
153
154 func BuildIPv6NetworkContainerFromRef(ref string) (*NetworkContainer, error) {
155 // ipv6networkcontainer/ZG5zLm5ldHdvcmskODkuMC4wLjAvMjQvMjU:2001%3Adb8%3Aabcd%3A0012%3A%3A0/64/global_view
156 r := regexp.MustCompile(`ipv6networkcontainer/[^:]+:(([^\/]+)\/\d+)\/(.+)`)
157 m := r.FindStringSubmatch(ref)
158
159 if m == nil {
160 return nil, fmt.Errorf("CIDR format not matched")
161 }
162
163 cidr, err := url.QueryUnescape(m[1])
164 if err != nil {
165 return nil, fmt.Errorf(
166 "cannot extract network CIDR information from the reference '%s': %s",
167 ref, err.Error())
168 }
169
170 if _, _, err = net.ParseCIDR(cidr); err != nil {
171 return nil, fmt.Errorf("CIDR format not matched")
172 }
173
174 newNet := NewNetworkContainer(m[3], cidr, true, "", nil)
175 newNet.Ref = ref
176
177 return newNet, nil
178 }
179
180 func GetIPAddressFromRef(ref string) string {
181 // fixedaddress/ZG5zLmJpbmRfY25h:12.0.10.1/external
182 r := regexp.MustCompile(`fixedaddress/\w+:(\d+\.\d+\.\d+\.\d+)/.+`)
183 m := r.FindStringSubmatch(ref)
184
185 if m != nil {
186 return m[1]
187 }
188 return ""
189 }
190
191 // validation for match_client
192 func validateMatchClient(value string) bool {
193 matchClientList := [5]string{
194 "MAC_ADDRESS",
195 "CLIENT_ID",
196 "RESERVED",
197 "CIRCUIT_ID",
198 "REMOTE_ID"}
199
200 for _, val := range matchClientList {
201 if val == value {
202 return true
203 }
204 }
205 return false
206 }
207
208 func BuildIPv6NetworkFromRef(ref string) (*Network, error) {
209 // ipv6network/ZG5zLm5ldHdvcmskODkuMC4wLjAvMjQvMjU:2001%3Adb8%3Aabcd%3A0012%3A%3A0/64/global_view
210 r := regexp.MustCompile(`ipv6network/[^:]+:(([^\/]+)\/\d+)\/(.+)`)
211 m := r.FindStringSubmatch(ref)
212
213 if m == nil {
214 return nil, fmt.Errorf("CIDR format not matched")
215 }
216
217 cidr, err := url.QueryUnescape(m[1])
218 if err != nil {
219 return nil, fmt.Errorf(
220 "cannot extract network CIDR information from the reference '%s': %s",
221 ref, err.Error())
222 }
223
224 if _, _, err = net.ParseCIDR(cidr); err != nil {
225 return nil, fmt.Errorf("CIDR format not matched")
226 }
227
228 newNet := NewNetwork(m[3], cidr, true, "", nil)
229 newNet.Ref = ref
230
231 return newNet, nil
232 }
233
234 const dnsLabelFormat = "[a-z0-9]+(([a-z0-9-]*[a-z0-9]+))?"
235
236 // ValidateDomainName return an error if the domain name does not conform to standards.
237 // The domain name may be in Unicode format (internationalized domain name)
238 func ValidateDomainName(name string) error {
239 domainRegexpTemplate := fmt.Sprintf("^(?i)%s(\\.%s)*\\.?$", dnsLabelFormat, dnsLabelFormat)
240 domainRegexp := regexp.MustCompile(domainRegexpTemplate)
241
242 _, err := idna.ToASCII(name)
243 if err != nil {
244 return err
245 }
246
247 if !domainRegexp.MatchString(name) {
248 return fmt.Errorf("the name '%s' is not a valid domain name", name)
249 }
250
251 return nil
252 }
253
254 // ValidateSrvRecName return an error if the record's name does not conform to standards.
255 func ValidateSrvRecName(name string) error {
256 const protoLabelFormat = "[a-z0-9]+"
257
258 const errorMsgFormat = "SRV-record's name '%s' does not conform to standards"
259 var (
260 srvNamePartRegExp = regexp.MustCompile(fmt.Sprintf("^_%s", dnsLabelFormat))
261 srvProtoPartRegExp = regexp.MustCompile(fmt.Sprintf("^_%s", protoLabelFormat))
262 )
263
264 nameParts := strings.SplitN(name, ".", 3)
265 if len(nameParts) != 3 {
266 return fmt.Errorf(errorMsgFormat, name)
267 }
268 if !srvNamePartRegExp.MatchString(nameParts[0]) {
269 return fmt.Errorf(errorMsgFormat, name)
270 }
271 if !srvProtoPartRegExp.MatchString(nameParts[1]) {
272 return fmt.Errorf(errorMsgFormat, name)
273 }
274 if err := ValidateDomainName(nameParts[2]); err != nil {
275 return err
276 }
277
278 return nil
279 }
280
281 func CheckIntRange(name string, value int, min int, max int) error {
282 if value < min || value > max {
283 return fmt.Errorf("'%s' must be integer and must be in the range from 0 to 65535 inclusively", name)
284 }
285
286 return nil
287 }
288
289 func ValidateMultiValue(v string) ([]string, bool) {
290 res := strings.Split(v, ",")
291 if len(res) > 1 {
292 return res, true
293 } else {
294 return nil, false
295 }
296 }
297