Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
|
a5f6c60a71 | 3 years ago |
|
105273af48 | 3 years ago |
|
e501417166 | 3 years ago |
@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||
|
||||
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 decort
|
||||
|
||||
import (
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
// "hash/fnv"
|
||||
log "github.com/sirupsen/logrus"
|
||||
// "net/url"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
|
||||
)
|
||||
|
||||
|
||||
func flattenPfw(d *schema.ResourceData, pfwFacts string) error {
|
||||
// NOTE: this function modifies ResourceData argument - as such it should never be called
|
||||
// from resourcePfwExists(...) method
|
||||
pfwRecord := ComputePfwListResp{}
|
||||
err := json.Unmarshal([]byte(pfwFacts), &pfwRecord)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("flattenPfw: decoded %d PFW rules for compute ID %s on ViNS ID %d",
|
||||
len(pfwRecord.Rules), pfwRecord.Header.VinsID, pfwRecord.Header.VinsID)
|
||||
|
||||
/*
|
||||
Here it gets a little bit interesting.
|
||||
Unlike compute or disk, port forwaring rules are NOT represented by any cloud
|
||||
platform resource, which might have had a unique ID. They are just a subset of
|
||||
rules in the list maintained by the corresponding ViNS instance. However,
|
||||
Terraform needs a unique ID for each resource it manages so that it could be
|
||||
stored in the state file and retrieved for use.
|
||||
|
||||
Therefore we need to make up an ID and supply it to Terraform in a standard
|
||||
way (i.e. by calling d.SetId(...)).
|
||||
|
||||
Fortunately, a combination of Compute ID and ViNS ID with GW VNF, where this
|
||||
compute is plugged in, makes a unique string, so we use it as an ID for
|
||||
the PFW ruleset.
|
||||
|
||||
The following few lines are legacy from the first attempt to make an ID
|
||||
as a hash of concatenated Compute ID & ViNS ID, but it did not work as
|
||||
expected for a number of reasons, which explanation is not a primary
|
||||
intent of the comment in the source code.
|
||||
|
||||
combo := fmt.Sprintf("%d:%d", compId.(int), pfwRecord.ViNS.VinsID)
|
||||
hasher := fnv.New32a()
|
||||
hasher.Write([]byte(combo))
|
||||
d.SetId(fmt.Sprintf("%d", hasher.Sum32()))
|
||||
*/
|
||||
// set ID of this PFW rule set as "compute_id:vins_id"
|
||||
d.SetId(fmt.Sprintf("%d:%d", pfwRecord.Header.ComputeID, pfwRecord.Header.VinsID))
|
||||
log.Debugf("flattenPfw: PFW rule set ID %s", d.Id())
|
||||
d.Set("compute_id", pfwRecord.Header.ComputeID)
|
||||
d.Set("vins_id", pfwRecord.Header.VinsID)
|
||||
|
||||
pfwRulesList := []interface{}{}
|
||||
for _, runner := range pfwRecord.Rules {
|
||||
rule := map[string]interface{}{
|
||||
"pub_port_start": runner.PublicPortStart,
|
||||
"pub_port_end": runner.PublicPortEnd,
|
||||
"local_port": runner.LocalPort,
|
||||
"proto": runner.Protocol,
|
||||
"rule_id": runner.ID,
|
||||
}
|
||||
pfwRulesList = append(pfwRulesList, rule)
|
||||
}
|
||||
if err = d.Set("rule", pfwRulesList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dataSourcePfwRead(d *schema.ResourceData, m interface{}) error {
|
||||
pfwFacts, err := utilityPfwCheckPresence(d, m)
|
||||
if pfwFacts == "" {
|
||||
// if empty string is returned from dataSourcePfwRead then we got no
|
||||
// PFW rules. It could also be because there was some error, which
|
||||
// is indicated by non-nil err value
|
||||
d.SetId("") // ensure ID is empty in this case anyway
|
||||
return err
|
||||
}
|
||||
|
||||
return flattenPfw(d, pfwFacts)
|
||||
}
|
||||
|
||||
func dataSourcePfw() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
SchemaVersion: 1,
|
||||
|
||||
Read: dataSourcePfwRead,
|
||||
|
||||
Timeouts: &schema.ResourceTimeout{
|
||||
Read: &Timeout30s,
|
||||
Default: &Timeout60s,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"compute_id": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntAtLeast(1),
|
||||
Description: "ID of the compute instance to configure port forwarding rules for.",
|
||||
},
|
||||
|
||||
"vins_id": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntAtLeast(1),
|
||||
Description: "ID of the ViNS to configure port forwarding rules on. Compute must be already plugged into this ViNS and ViNS must have external network connection.",
|
||||
},
|
||||
|
||||
// TODO: consider making "rule" attribute Required with MinItems = 1
|
||||
"rule": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: rulesSubresourceSchemaMake(),
|
||||
},
|
||||
Description: "Port forwarding rule. You may specify several rules, one in each such block.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
/*
|
||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||
|
||||
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 decort
|
||||
|
||||
import (
|
||||
|
||||
// "encoding/json"
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/url"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
|
||||
)
|
||||
|
||||
func resourcePfwCreate(d *schema.ResourceData, m interface{}) error {
|
||||
compId := d.Get("compute_id")
|
||||
|
||||
rules_set, ok := d.GetOk("rule")
|
||||
if !ok || rules_set.(*schema.Set).Len() == 0 {
|
||||
log.Debugf("resourcePfwCreate: empty new PFW rules set requested for compute ID %d - nothing to create", compId.(int))
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("resourcePfwCreate: ready to setup %d PFW rules for compute ID %d",
|
||||
rules_set.(*schema.Set).Len(), compId.(int))
|
||||
|
||||
controller := m.(*ControllerCfg)
|
||||
apiErrCount := 0
|
||||
var lastSavedError error
|
||||
|
||||
for _, runner := range rules_set.(*schema.Set).List() {
|
||||
rule := runner.(map[string]interface{})
|
||||
params := &url.Values{}
|
||||
params.Add("computeId", fmt.Sprintf("%d", compId.(int)))
|
||||
params.Add("publicPortStart", fmt.Sprintf("%d", rule["pub_port_start"].(int)))
|
||||
params.Add("publicPortEnd", fmt.Sprintf("%d", rule["pub_port_end"].(int)))
|
||||
params.Add("localBasePort", fmt.Sprintf("%d", rule["local_port"].(int)))
|
||||
params.Add("proto", rule["proto"].(string))
|
||||
log.Debugf("resourcePfwCreate: ready to add rule %d:%d -> %d %s for Compute ID %d",
|
||||
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||
rule["local_port"].(int), rule["proto"].(string),
|
||||
compId.(int))
|
||||
_, err, _ := controller.decortAPICall("POST", ComputePfwAddAPI, params)
|
||||
if err != nil {
|
||||
log.Errorf("resourcePfwCreate: error adding rule %d:%d -> %d %s for Compute ID %d: %s",
|
||||
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||
rule["local_port"].(int), rule["proto"].(string),
|
||||
compId.(int),
|
||||
err)
|
||||
apiErrCount++
|
||||
lastSavedError = err
|
||||
}
|
||||
}
|
||||
|
||||
if apiErrCount > 0 {
|
||||
log.Errorf("resourcePfwCreate: there were %d error(s) adding PFW rules to Compute ID %s. Last error was: %s",
|
||||
apiErrCount, compId.(int), lastSavedError)
|
||||
return lastSavedError
|
||||
}
|
||||
|
||||
return resourcePfwRead(d, m)
|
||||
}
|
||||
|
||||
func resourcePfwRead(d *schema.ResourceData, m interface{}) error {
|
||||
pfwFacts, err := utilityPfwCheckPresence(d, m)
|
||||
if pfwFacts == "" {
|
||||
// if empty string is returned from dataSourcePfwRead then we got no
|
||||
// PFW rules. It could also be because there was some error, which
|
||||
// is indicated by non-nil err value
|
||||
d.SetId("") // ensure ID is empty in this case anyway
|
||||
return err
|
||||
}
|
||||
|
||||
return flattenPfw(d, pfwFacts)
|
||||
}
|
||||
|
||||
func resourcePfwUpdate(d *schema.ResourceData, m interface{}) error {
|
||||
// TODO: update not implemented yet
|
||||
compId := d.Get("compute_id")
|
||||
return fmt.Errorf("resourcePfwUpdate: method is not implemented yet (Compute ID %d)", compId.(int))
|
||||
|
||||
// return resourcePfwRead(d, m)
|
||||
}
|
||||
|
||||
func resourcePfwDelete(d *schema.ResourceData, m interface{}) error {
|
||||
compId := d.Get("compute_id")
|
||||
|
||||
rules_set, ok := d.GetOk("rule")
|
||||
if !ok || rules_set.(*schema.Set).Len() == 0 {
|
||||
log.Debugf("resourcePfwDelete: no PFW rules defined for compute ID %d - nothing to delete", compId.(int))
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("resourcePfwDelete: ready to delete %d PFW rules from compute ID %d",
|
||||
rules_set.(*schema.Set).Len(), compId.(int))
|
||||
|
||||
controller := m.(*ControllerCfg)
|
||||
apiErrCount := 0
|
||||
var lastSavedError error
|
||||
|
||||
for _, runner := range rules_set.(*schema.Set).List() {
|
||||
rule := runner.(map[string]interface{})
|
||||
params := &url.Values{}
|
||||
params.Add("computeId", fmt.Sprintf("%d", compId.(int)))
|
||||
params.Add("ruleId", fmt.Sprintf("%d", rule["rule_id"].(int)))
|
||||
log.Debugf("resourcePfwCreate: ready to delete rule ID%s (%d:%d -> %d %s) from Compute ID %d",
|
||||
rule["rule_id"].(int),
|
||||
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||
rule["local_port"].(int), rule["proto"].(string),
|
||||
compId.(int))
|
||||
_, err, _ := controller.decortAPICall("POST", ComputePfwDelAPI, params)
|
||||
if err != nil {
|
||||
log.Errorf("resourcePfwDelete: error deleting rule ID %d (%d:%d -> %d %s) from Compute ID %d: %s",
|
||||
rule["rule_id"].(int),
|
||||
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||
rule["local_port"].(int), rule["proto"].(string),
|
||||
compId.(int),
|
||||
err)
|
||||
apiErrCount++
|
||||
lastSavedError = err
|
||||
}
|
||||
}
|
||||
|
||||
if apiErrCount > 0 {
|
||||
log.Errorf("resourcePfwDelete: there were %d error(s) when deleting PFW rules from Compute ID %s. Last error was: %s",
|
||||
apiErrCount, compId.(int), lastSavedError)
|
||||
return lastSavedError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePfwExists(d *schema.ResourceData, m interface{}) (bool, error) {
|
||||
// Reminder: according to Terraform rules, this function should not modify its ResourceData argument
|
||||
log.Debugf("resourcePfwExists: called for Compute ID %d", d.Get("compute_id").(int))
|
||||
|
||||
pfwFacts, err := utilityPfwCheckPresence(d, m)
|
||||
if pfwFacts == "" {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func resourcePfw() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
SchemaVersion: 1,
|
||||
|
||||
Create: resourcePfwCreate,
|
||||
Read: resourcePfwRead,
|
||||
Update: resourcePfwUpdate,
|
||||
Delete: resourcePfwDelete,
|
||||
Exists: resourcePfwExists,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
||||
Timeouts: &schema.ResourceTimeout{
|
||||
Create: &Timeout180s,
|
||||
Read: &Timeout30s,
|
||||
Update: &Timeout180s,
|
||||
Delete: &Timeout60s,
|
||||
Default: &Timeout60s,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"compute_id": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntAtLeast(1),
|
||||
Description: "ID of the compute instance to configure port forwarding rules for.",
|
||||
},
|
||||
|
||||
"vins_id": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntAtLeast(1),
|
||||
Description: "ID of the ViNS to configure port forwarding rules on. Compute must be already plugged into this ViNS and ViNS must have external network connection.",
|
||||
},
|
||||
|
||||
// TODO: consider making "rule" attribute Required with MinItems = 1 to prevent
|
||||
// empty PFW list definition
|
||||
"rule": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: rulesSubresourceSchemaMake(),
|
||||
},
|
||||
Description: "Port forwarding rule. You may specify several rules, one in each such block.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||
|
||||
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 decort
|
||||
|
||||
import (
|
||||
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "bytes"
|
||||
// log "github.com/sirupsen/logrus"
|
||||
// "net/url"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
|
||||
)
|
||||
|
||||
// This is rules subresource of PFW resource used
|
||||
// when creating/managing port forwarding rules for a compute connected
|
||||
// to the corresponding network
|
||||
|
||||
func rulesSubresourceSchemaMake() map[string]*schema.Schema {
|
||||
rets := map[string]*schema.Schema{
|
||||
"pub_port_start": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntBetween(1, 65535),
|
||||
Description: "Port number on the external interface. For a ranged rule it set the starting port number.",
|
||||
},
|
||||
|
||||
"pub_port_end": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntBetween(1, 65535),
|
||||
Description: "End port number on the external interface for a ranged rule. Set it equal to start port for a single port rule.",
|
||||
},
|
||||
|
||||
"local_port": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntBetween(1, 65535),
|
||||
Description: "Port number on the local interface.",
|
||||
},
|
||||
|
||||
"proto": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
StateFunc: stateFuncToLower,
|
||||
ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false),
|
||||
Description: "Protocol for this rule. Could be either tcp or udp.",
|
||||
},
|
||||
|
||||
// the rest are computed
|
||||
|
||||
"rule_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "Rule ID as assigned by the cloud platform.",
|
||||
},
|
||||
|
||||
}
|
||||
return rets
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
Copyright (c) 2020-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
||||
Technology platfom.
|
||||
|
||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
||||
*/
|
||||
|
||||
package decort
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||
)
|
||||
|
||||
func utilityPfwCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
|
||||
//
|
||||
// This function does not modify its ResourceData argument, so it is safe to use it as core
|
||||
// method for the Terraform resource Exists method.
|
||||
//
|
||||
|
||||
// The string returned by this method mimics response of an imaginary API that will
|
||||
// report PFW rules (if any) as following:
|
||||
// {
|
||||
// "header": {
|
||||
// "computeId": <int>,
|
||||
// "vinsId": <int>,
|
||||
// },
|
||||
// "rules": [
|
||||
// {
|
||||
// "id": <int>,
|
||||
// "localPort": <int>,
|
||||
// "protocol": <int>,
|
||||
// "publicPortStart": <int>,
|
||||
// "publicPortEnd": <int>,
|
||||
// "vmId": <int>,
|
||||
// },
|
||||
// {
|
||||
// ...
|
||||
// },
|
||||
// ],
|
||||
// }
|
||||
// This response unmarshalls into ComputePfwListResp structure.
|
||||
// NOTE: If there are no rules for this compute, an empty string is returned along with err=nil
|
||||
//
|
||||
|
||||
controller := m.(*ControllerCfg)
|
||||
urlValues := &url.Values{}
|
||||
|
||||
var compId, vinsId int
|
||||
|
||||
// PFW resource ID is a combination of compute_id and vins_id separated by ":"
|
||||
// See a note in "flattenPfw" method explaining the rationale behind this approach.
|
||||
if d.Id() != "" {
|
||||
log.Debugf("utilityPfwCheckPresence: setting context from PFW ID %s", d.Id())
|
||||
idParts := strings.SplitN(d.Id(), ":", 2)
|
||||
compId, _ = strconv.Atoi(idParts[0])
|
||||
vinsId, _ = strconv.Atoi(idParts[1])
|
||||
log.Debugf("utilityPfwCheckPresence: extracted Compute ID %d, ViNS %d", compId, vinsId)
|
||||
if compId <= 0 || vinsId <= 0 {
|
||||
return "", fmt.Errorf("Ivalid context from d.Id %s", d.Id())
|
||||
}
|
||||
} else {
|
||||
scId, cSet := d.GetOk("compute_id")
|
||||
svId, vSet := d.GetOk("vins_id")
|
||||
if cSet || vSet {
|
||||
log.Debugf("utilityPfwCheckPresence: setting Compute ID from schema")
|
||||
compId = scId.(int)
|
||||
vinsId = svId.(int)
|
||||
log.Debugf("utilityPfwCheckPresence: extracted Compute ID %d, ViNS %d", compId, vinsId)
|
||||
} else {
|
||||
return "", fmt.Errorf("Cannot get context to check PFW rules neither from PFW ID nor from schema")
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("utilityPfwCheckPresence: preparing to get PFW rules for Compute ID %d on ViNS ID %d", compId, vinsId)
|
||||
|
||||
urlValues.Add("computeId", fmt.Sprintf("%d", compId))
|
||||
apiResp, err, respCode := controller.decortAPICall("POST", ComputePfwListAPI, urlValues)
|
||||
if respCode == 500 {
|
||||
// this is workaround for API 3.7.0 "feature" - will be removed in one of the future versions
|
||||
log.Errorf("utilityPfwCheckPresence: Compute ID %d has no PFW and no connection to PFW-ready ViNS", compId)
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
pfwListResp := ComputePfwListResp{}
|
||||
|
||||
// Note the specifics of compute/pfwList response in API 3.7.x (this may be changed in the future):
|
||||
// 1) if there are no PFW rules and compute is not connected to any PFW-able ViNS
|
||||
// the response will be empty string (or HTTP error code 500)
|
||||
// 2) if there are no PFW rules but compute is connected to a PFW-able ViNS
|
||||
// the response will contain a list with a single element - prefix (see PfwPrefixRecord)
|
||||
// 3) if there are port forwarding rules, the response will contain a list which starts
|
||||
// with prefix (see PfwPrefixRecord) and then followed by one or more rule records
|
||||
// (see PfwRuleRecord)
|
||||
//
|
||||
// EXTRA NOTE: in API 3.7.0 and the likes pfwList returns HTTP response code 500 for a compute
|
||||
// that is not connected to any PFW-able ViNS - need to implement temporary workaround
|
||||
|
||||
if apiResp == "" {
|
||||
// No port forward rules defined for this compute
|
||||
return "", nil
|
||||
}
|
||||
|
||||
log.Debugf("utilityPfwCheckPresence: ready to split API response string %s", apiResp)
|
||||
|
||||
twoParts := strings.SplitN(apiResp, "},", 2)
|
||||
if len(twoParts) < 1 || len(twoParts) > 2 {
|
||||
// Case: invalid format of API response
|
||||
log.Errorf("utilityPfwCheckPresence: non-empty pfwList response for compute ID %d failed to split properly", compId)
|
||||
return "", fmt.Errorf("Non-empty pfwList response failed to split properly")
|
||||
}
|
||||
|
||||
if len(twoParts) == 1 {
|
||||
// Case: compute is connected to a PWF-ready ViNS but has no PFW rules defined
|
||||
log.Debugf("utilityPfwCheckPresence: compute ID %d is connected to PFW-ready ViNS but has no PFW rules", compId)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Case: compute is connected to a PFW ready ViNS and has some PFW rule
|
||||
prefixResp := strings.TrimSuffix(strings.TrimPrefix(twoParts[0], "["), ",") + "}"
|
||||
log.Debugf("utilityPfwCheckPresence: ready to unmarshal prefix part %s", prefixResp)
|
||||
err = json.Unmarshal([]byte(prefixResp), &pfwListResp.Header)
|
||||
if err != nil {
|
||||
log.Errorf("utilityPfwCheckPresence: failed to unmarshal prefix part of API response: %s", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
rulesResp := "[" + twoParts[1]
|
||||
log.Debugf("utilityPfwCheckPresence: ready to unmarshal rules part %s", rulesResp)
|
||||
err = json.Unmarshal([]byte(rulesResp), &pfwListResp.Rules)
|
||||
if err != nil {
|
||||
log.Errorf("utilityPfwCheckPresence: failed to unmarshal rules part of API response: %s", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debugf("utilityPfwCheckPresence: successfully read %d port forward rules for Compute ID %d",
|
||||
len(pfwListResp.Rules), compId)
|
||||
|
||||
if len(pfwListResp.Rules) == 0 {
|
||||
// this compute technically can have rules, but no rules are currently defined
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Now we can double check that the PFWs we've got do belong to the compute & ViNS specified in the
|
||||
// PFW definition. Do so by reading compute details and comparing NatableVinsID value with the
|
||||
// specified ViNS ID
|
||||
//
|
||||
// We may reuse urlValues here, as it already contains computeId field (see initialization above)
|
||||
apiResp, err, _ = controller.decortAPICall("POST", ComputeGetAPI, urlValues)
|
||||
if err != nil || apiResp == "" {
|
||||
log.Errorf("utilityPfwCheckPresence: failed to get Compute ID %d details", compId)
|
||||
return "", err
|
||||
}
|
||||
compFacts := ComputeGetResp{}
|
||||
err = json.Unmarshal([]byte(rulesResp), &apiResp)
|
||||
if err != nil {
|
||||
log.Errorf("utilityPfwCheckPresence: failed to unmarshal compute details for ID %d: %s", compId, err)
|
||||
return "", err
|
||||
}
|
||||
if compFacts.NatableVinsID <= 0 {
|
||||
log.Errorf("utilityPfwCheckPresence: compute ID %d is not connected to a NAT-able ViNS", compId)
|
||||
return "", fmt.Errorf("Compute ID %d is not connected to a NAT-able ViNS", compId)
|
||||
}
|
||||
if compFacts.NatableVinsID != vinsId {
|
||||
log.Errorf("utilityPfwCheckPresence: ViNS ID mismatch for PFW rules on compute ID %d: actual %d, required %d",
|
||||
compId, compFacts.NatableVinsID, vinsId)
|
||||
return "", fmt.Errorf("ViNS ID mismatch for PFW rules on compute ID %d: actual %d, required %d",
|
||||
compId, compFacts.NatableVinsID, vinsId)
|
||||
}
|
||||
|
||||
// reconstruct API response string to return
|
||||
pfwListResp.Header.ComputeID = compId
|
||||
pfwListResp.Header.VinsID = compFacts.NatableVinsID
|
||||
reencodedItem, err := json.Marshal(pfwListResp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(reencodedItem[:]), nil
|
||||
}
|
Loading…
Reference in new issue