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