package goctk import ( "encoding/json" "errors" "fmt" "log" "regexp" "strconv" "github.rackspace.com/gorax/clients/dcxSwitchapi" "github.rackspace.com/gorax/clients/unipede" "github.rackspace.com/gorax/libs/switchlib/switchy" ) // Start Computer Info qeury, requires a device and list of attributes you // want to pull from core. Attributes is returned as a map of string and interfaces // since the types are mixed and would require creating indvidiual keys for all // possible device attributes // https://ws.core.rackspace.com/ctkapi/browse/Computer type ComputerInfo struct { Class string `json:"class"` LoadArg int `json:"load_arg"` Attributes []map[string]interface{} `json:"attributes"` } func (c *Client) ComputerInfo(computer int, attributes []string) (*ComputerInfo, error) { type ctk struct { Class string `json:"class"` LoadArg int `json:"load_arg"` Attributes []string `json:"attributes"` } type Computer struct { Class string `json:"class"` LoadArg int `json:"load_arg"` Result json.RawMessage `json:"result"` } t := ctk{ Class: "Computer.Computer", LoadArg: computer, Attributes: attributes, } js, err := json.Marshal(&t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var tc Computer if err = json.Unmarshal(res, &[]interface{}{&tc}); err != nil { return nil, err } var m []map[string]interface{} if err = json.Unmarshal(tc.Result, &m); err != nil { return nil, err } return &ComputerInfo{ Class: tc.Class, LoadArg: tc.LoadArg, Attributes: m, }, nil } // End Computer info // Start Device by IP, this function will return a list of devices associated to a list // of public IPs type ComputerByIP struct { Count int `json:"count"` Class string `json:"class"` Limit int `json:"limit"` Result []ComputerIP `json:"result"` Offset int `json:"offset"` } type ComputerIP struct { AccountNumber string `json:"account.number"` PrimaryIP string `json:"primary_ip"` Name string `json:"name"` AccountName string `json:"account.name"` Number int `json:"number"` StatusCode int `json:"status.id"` PlatformType string `json:"platform_type"` PlatformModel string `json:"platform_model"` } func (c *ComputerIP) Decommissioned() bool { if c.StatusCode == -1 || c.StatusCode == 50 { return true } return false } type attributes struct { Number string `json:"number"` AccountNumber string `json:"account.number"` AccountName string `json:"account.name"` Name string `json:"name"` PrimaryIP string `json:"primary_ip"` StatusCode string `json:"status.id"` } type ctk struct { Class string `json:"class"` LoadArg LoadArg `json:"load_arg"` Attributes attributes `json:"attributes"` } func (c *Client) ComputersByIP(IPs []string) (*ComputerByIP, error) { type attributes struct { Number string `json:"number"` AccountNumber string `json:"account.number"` AccountName string `json:"account.name"` Name string `json:"name"` PrimaryIP string `json:"primary_ip"` StatusCode string `json:"status.id"` PlatformType string `json:"platform_type"` PlatformModel string `json:"platform_model"` } type ctk struct { Class string `json:"class"` LoadArg LoadArg `json:"load_arg"` Attributes attributes `json:"attributes"` } t := ctk{ Class: "Computer.Computer", LoadArg: LoadArg{ Values: []interface{}{ []interface{}{"primary_ip", "in", IPs}, }, Class: "Computer.ComputerWhere", }, Attributes: attributes{ Number: "number", AccountNumber: "account.number", AccountName: "account.name", Name: "name", PrimaryIP: "primary_ip", StatusCode: "status.id", PlatformType: "platform_type", PlatformModel: "platform_model", }, } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r ComputerByIP if err := json.Unmarshal(res, &[]interface{}{&r}); err != nil { return nil, err } return &r, nil } func (c *Client) ComputerByIP(IP string) (*ComputerIP, error) { type attributes struct { Number string `json:"number"` AccountNumber string `json:"account.number"` AccountName string `json:"account.name"` Name string `json:"name"` PrimarIP string `json:"primary_ip"` StatusCode string `json:"status.id"` PlatformType string `json:"platform_type"` PlatformModel string `json:"platform_model"` } type ctk struct { Class string `json:"class"` LoadArg LoadArg `json:"load_arg"` Attributes attributes `json:"attributes"` } t := ctk{ Class: "Computer.Computer", LoadArg: LoadArg{ Values: []interface{}{ []interface{}{"primary_ip", "=", IP}, }, Class: "Computer.ComputerWhere", }, Attributes: attributes{ Number: "number", AccountNumber: "account.number", AccountName: "account.name", Name: "name", PrimarIP: "primary_ip", StatusCode: "status.id", PlatformType: "platform_type", PlatformModel: "platform_model", }, } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r ComputerByIP if err := json.Unmarshal(res, &[]interface{}{&r}); err != nil { return nil, err } if len(r.Result) == 1 { return &r.Result[0], nil } return nil, errors.New("ip not found") } func (c *Client) ComputerByDRACIP(IP string) (*ComputerByIP, error) { t := ctk{ Class: "Computer.Computer", LoadArg: LoadArg{ Values: []interface{}{ []interface{}{"ip", "=", IP}, }, Class: "Computer.ComputerWhere", }, Attributes: attributes{ Number: "number", AccountNumber: "account.number", AccountName: "account.name", Name: "name", PrimaryIP: "primary_ip", StatusCode: "status.id", }, } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r ComputerByIP if err := json.Unmarshal(res, &[]interface{}{&r}); err != nil { return nil, err } return &r, nil } // End Device by Ip type ( ComputerQuery struct { Count int `json:"count"` Class string `json:"class"` Limit int `json:"limit"` Result []*ComputerQueryInfo `json:"result"` Offset int `json:"offset"` } ComputerQueryInfo struct { Number int `json:"number"` AccountNumber string `json:"accountNumber"` AccountName string `json:"accountName"` AccountTeam string `json:"accountTeam"` Name string `json:"name"` Status string `json:"status"` StatusCode int `json:"status.id"` DataCenter string `json:"DataCenter"` DC string `json:"datacenter.symbol"` Platform int `json:"platform"` PlatformType string `json:"platform_type"` PlatformModel string `json:"platform_model"` SwitchInfo []string `json:"ports.display_name"` SwitchPorts []*SwitchPort `json:"switch_ports"` SwitchPortsv2 []*switchy.SwitchPort `json:"switch_ports_v2"` PrimaryIP string `json:"primary_ip"` FailoverDevice int `json:"failover_device.number"` DracnetIP string `json:"dracnet_ip.ip_address"` ServiceNetIPs []string `json:"services_net_ips.ip_address"` MBUIP string `json:"managed_backup_ip.ip_address"` Nat []string `json:"ip_addresses"` Skus []int `json:"skus"` Exnet *string `json:"exnet_zone"` Tickets []string `json:"tickets"` SKUs skuData `json:"parts.partributes" bson:"parts.partributes"` PrimaryPassword string `json:"primary_userid_password"` } skuData struct { SubAttributes struct { Account struct { Value string `json:"value" bson:"value"` } `json:"Account" bson:"Account"` Device struct { Value string `json:"value" bson:"value"` } `json:"Device(s)" bson:"Device(s)"` Valid struct { Value string `json:"value" bson:"value"` } `json:"Product status"` } `json:"subattributes" bson:"subattributes"` } SwitchPort struct { Type string `json:"type"` Port int `json:"port"` SwitchName string `json:"switch_name"` Comment string `json:"comment"` PortStatus *dcxSwitchapi.SwitchApiInterface `json:"switch_api_status,omitmpty"` } ) func (c *ComputerQuery) PortNames(u *unipede.Client) error { var pl []*switchy.SwitchPort for _, res := range c.Result { pl = append(pl, res.SwitchPortsv2...) } if _, err := switchy.PortListNames(pl, u); err != nil { return err } log.Println(len(pl)) return nil } func (c *ComputerQueryInfo) PortNames(u *unipede.Client) error { if _, err := switchy.PortListNames(c.SwitchPortsv2, u); err != nil { return err } return nil } func (c *Client) ComputersQuery(computers []int) (*ComputerQuery, error) { type ctk struct { Class string `json:"class"` LoadArg LoadArg `json:"load_arg"` Attributes []string `json:"attributes"` } t := ctk{ Class: "Computer.Computer", LoadArg: LoadArg{ Values: []interface{}{ "number", "in", computers}, Class: "Computer.ComputerWhere", }, Attributes: []string{ "number", "account.number", "account.name", "account.support_team.name", "name", "status.name", "status.id", "datacenter.name", "platform.id", "platform_type", "platform_model", "ports.display_name", "primary_ip", "failover_device.number", "dracnet_ip.ip_address", "ip_addresses", "parts.sku_number", "datacenter.symbol", "exnet_zone.name", "tickets.number", }, } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r ComputerQuery if err = json.Unmarshal(res, &[]interface{}{&r}); err != nil { return nil, err } for k := range r.Result { r.Result[k].swParse() //r.Result[k].swParsev2() } return &r, nil } func (c *Client) ComputerQuery(computer int) (*ComputerQueryInfo, error) { type ctk struct { Class string `json:"class"` LoadArg LoadArg `json:"load_arg"` Attributes []interface{} `json:"attributes"` } t := ctk{ Class: "Computer.Computer", LoadArg: LoadArg{ Values: []interface{}{ "number", "=", computer}, Class: "Computer.ComputerWhere", }, Attributes: []interface{}{ "number", "account.number", "account.name", "account.support_team.name", "name", "status.name", "status.id", "datacenter.name", "platform.id", "platform_type", "platform_model", "ports.display_name", "primary_ip", "failover_device.number", "dracnet_ip.ip_address", "services_net_ips.ip_address", "managed_backup_ip.ip_address", "ip_addresses", "parts.sku_number", "datacenter.symbol", "exnet_zone.name", "primary_userid_password", map[string]interface{}{ "id": "sku_attribute.description", "attribute": "parts.partributes", "subattributes": map[string]string{ "value": "value", }, }, }, } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r ComputerQuery if err = json.Unmarshal(res, &[]interface{}{&r}); err != nil { return nil, err } for k := range r.Result { r.Result[k].swParse() //r.Result[k].swParsev2() } for _, c := range r.Result { if c.Number == computer { return c, nil } } return nil, fmt.Errorf("unable to find %d in result list", computer) } func (c *ComputerQueryInfo) Decommissioned() bool { if c.StatusCode == -1 || c.StatusCode == 50 { return true } return false } // function to parse the computer qury response, this is done to return the port // information in a structure instead of a list func (c *ComputerQueryInfo) swParsev2() { c.SwitchPortsv2 = switchy.ParseEMPorts(c.SwitchInfo, c.DC, c.Number) } func (c *ComputerQueryInfo) swParse() { rComment, err := regexp.Compile(`(?i)(.*?)\[([0-9]+)\]\s+\((.*?)\)\s+\((\S+)\)`) if err != nil { return } rNone, err := regexp.Compile(`(?i)(.*?)\[([0-9]+)\]\s+\((.*?)\)`) if err != nil { return } for _, v := range c.SwitchInfo { sw := rComment.FindStringSubmatch(v) if len(sw) > 0 { c.SwitchPorts = append(c.SwitchPorts, &SwitchPort{ Type: sw[3], Port: func() int { if n, err := strconv.Atoi(sw[2]); err == nil { return n } return 0 }(), SwitchName: sw[1], Comment: sw[4], }) } else { sw = rNone.FindStringSubmatch(v) if len(sw) > 0 { c.SwitchPorts = append(c.SwitchPorts, &SwitchPort{ Type: sw[3], Port: func() int { if n, err := strconv.Atoi(sw[2]); err == nil { return n } return 0 }(), SwitchName: sw[1], }) } } } } // end Device Query // Start Computer by sku query, returns a list of skus with that type of sku type ComputerBySku struct { Meta string `json:"meta"` Limit int `json:"limit"` Result []int `json:"result"` Count int `json:"count"` Class string `json:"class"` Offset int `json:"offset"` } func (c *Client) ComputerBySku(skus []int) (*ComputerBySku, error) { type ctk struct { Class string `json:"class"` LoadArg LoadArg `json:"load_arg"` Meta string `json:"meta"` Args string `json:"args"` } t := ctk{ Class: "Computer.Computer", LoadArg: LoadArg{ Class: "Computer.ComputerWhere", Values: []interface{}{ []interface{}{"sku", "in", skus}, "&", []interface{}{"status", ">", 0}, }, }, Meta: "grouped_count", Args: "number", } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r ComputerBySku err = r.skuList(res) if err != nil { return nil, err } return &r, nil } // End Comptuer by sku query // Start Comptuer by sku status query, retuns a list of devices with the sku and // status contained in a list func (c *Client) ComputerBySkuStatus(skus, status []int) (*ComputerBySku, error) { type ctk struct { Class string `json:"class"` LoadArg LoadArg `json:"load_arg"` Meta string `json:"meta"` Args string `json:"args"` } t := ctk{ Class: "Computer.Computer", LoadArg: LoadArg{ Class: "Computer.ComputerWhere", Values: []interface{}{ []interface{}{"sku", "in", skus}, "&", []interface{}{"status", "in", status}, }, }, Meta: "grouped_count", Args: "number", } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r ComputerBySku err = r.skuList(res) if err != nil { return nil, err } return &r, nil } // End Comptuer by sku status // Function to parse the string, it map that is returned by the sku calls, // creates an in list of devices and required ComptuerBySku fields func (c *ComputerBySku) skuList(res []byte) error { type computerBySku struct { Meta string `json:"meta"` Limit int `json:"limit"` Result map[string]int `json:"result"` // Result json.RawMessage `json:"result"` Count int `json:"count"` Class string `json:"class"` Offset int `json:"offset"` } var tc computerBySku if err := json.Unmarshal(res, &[]interface{}{&tc}); err != nil { return err } var cl []int for k := range tc.Result { n, err := strconv.Atoi(k) if err == nil { cl = append(cl, n) } } c.Meta = tc.Meta c.Limit = tc.Limit c.Result = cl c.Count = len(cl) c.Class = tc.Class c.Offset = tc.Offset return nil } func ComputerQueryNumbers(cl []*ComputerQueryInfo) (cs []int) { for _, c := range cl { if c.StatusCode == 50 || c.StatusCode == -1 { cs = append(cs, c.Number) } } return } type SetSkuResponse struct { Count int `json:"count"` Class string `json:"class"` LoadArg int `json:"load_arg"` Limit int `json:"limit"` Offset int `json:"offset"` Method string `json:"method"` Success bool `json:"success"` } func (c *Client) SetSkuforComputer(device int, sku int, skunit int) (*SetSkuResponse, error) { type KeywordArg struct { Sku int `json:"sku"` SkUnit int `json:"skunit"` } type ctk struct { Class string `json:"class"` LoadArg int `json:"load_arg"` Method string `json:"method"` KeywordArgs KeywordArg `json:"keyword_args"` } t := ctk{ Class: "Computer.Computer", LoadArg: device, Method: "addReplacePart", KeywordArgs: KeywordArg{ Sku: sku, SkUnit: skunit, }, } js, err := json.Marshal(t) if err != nil { return nil, err } res, err := c.Query(js) if err != nil { return nil, err } var r SetSkuResponse if err := json.Unmarshal(res, &[]interface{}{&r}); err != nil { return nil, err } return &r, nil } func (c *ComputerQueryInfo) HasSku(sku int) bool { for _, s := range c.Skus { if s == sku { return true } } return false } type ( SetAttribute struct { Class string `json:"class"` LoadArg string `json:"load_arg"` SetAttribute map[string]SetAttrs `json:"set_attribute"` } SetAttrs struct { Class string `json:"class"` LoadArg int `json:"load_arg"` } ) func (c *Client) ChangeDataCenter(device string, dc int) ([]byte, error) { payload := SetAttribute{ Class: "Computer.Computer", LoadArg: device, SetAttribute: map[string]SetAttrs{ "datacenter": { Class: "Network.Datacenter", LoadArg: dc, }, }, } js, err := json.Marshal(payload) res, err := c.Query(js) if err != nil { return nil, err } return res, nil } // StatusRequest is the request parameters to pass into the SetStatus function type StatusRequest struct { NewStatus int `json:"new_status"` Reason string `json:"reason,omitempty"` TicketNum string `json:"ticket_num,omitempty"` MigrDays string `json:"migr_days,omitempty"` NewMigrServer string `json:"new_migr_server,omitempty"` OfflineReason string `json:"offline_reason,omitempty"` OldMrr string `json:"old_mrr,omitempty"` NewMrr string `json:"new_mrr,omitempty"` MrrCurrency string `json:"mrr_currency,omitempty"` ContractID string `json:"contract_id,omitempty"` IncludedBandwidth string `json:"included_bandwidth,omitempty"` } // SetStatus will change the CORE status for a given computer func (c *Client) SetStatus(computer int, req StatusRequest) error { type Req struct { Class string `json:"class"` LoadArg int `json:"load_arg"` Method string `json:"method"` KeywordArgs StatusRequest `json:"keyword_args"` } payload := []Req{{ Class: "Computer.Computer", LoadArg: computer, Method: "changeStatus", KeywordArgs: req, }} js, err := json.Marshal(payload) if err != nil { return err } res, err := c.Query(js) if err != nil { return err } var resp ctkMethodResp if err := json.Unmarshal(res, &resp); err != nil { return err } if len(resp) == 0 { return fmt.Errorf("invalid response from CTK for %d", computer) } if !resp[0].Success { return fmt.Errorf("status change failed for %d", computer) } return nil }