Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network Port QoS Policy API #1027

Closed
28 tasks done
Twister915 opened this issue Jun 12, 2018 · 9 comments
Closed
28 tasks done

Network Port QoS Policy API #1027

Twister915 opened this issue Jun 12, 2018 · 9 comments
Assignees

Comments

@Twister915
Copy link
Contributor

Twister915 commented Jun 12, 2018

Adding implementations for the following routes to the client would be desirable:

Specifically, these routes, under network/v2.0/qos (list here, as well: https://developer.openstack.org/api-ref/network/v2/index.html#quality-of-service)

QoS Extenstion common CRUD

QoS Extension Ports CRUD

QoS Extension Networks CRUD

@ozerovandrei
Copy link
Contributor

@jtopjian I have a question related to QoS support needed by terraform-provider-openstack#246.

Should we add the qos_policy_id port field into the Port structure or should we implement it as a separate extension similar to extradhcpopts?

If we add it as extensions how do we need to structure the package?

Should we add the extensions/qos/policies that will contain all logic for the qos-policies-qos with policies CRUD and port qos PortQoSPolicyOptsExt structure?
In that case we will also add NetworkQoSPolicyOptsExt to support the qos-policies-qos field for networks.

Or should we create the separate extensions/qos/portpolicies, extensions/qos/networkpolicies, etc.

Or it will be better if we just add the qos-policies-qos field into the existing Port structure?

@jtopjian
Copy link
Contributor

Or it will be better if we just add the qos-policies-qos field into the existing Port structure?

Every once in a while I get the urge to combine all network extensions into their "base" resources. But then something comes along that makes me realize that's not a good idea. I recently looked at some DNS extensions which made me think we would run into problems when someone doesn't have the DNS extensions enabled.

The networking extensions are a bear to work with and I really want to simply them, but for now, we shouldn't do this.

If we add it as extensions how do we need to structure the package?

Should we add the extensions/qos/policies that will contain all logic for the qos-policies-qos with policies CRUD and port qos PortQoSPolicyOptsExt structure?
In that case we will also add NetworkQoSPolicyOptsExt to support the qos-policies-qos field for networks.

Yes. We can put everything in extensions/qos/policies/requests.go.

If the request opts for Ports and Networks are the same, unfortunately we will have to duplicate the structs. You can see an example of this with port security: https://github.com/gophercloud/gophercloud/blob/master/openstack/networking/v2/extensions/portsecurity/requests.go

But if the response fields are the same, we can cheat a little bit. Again, see port security: https://github.com/gophercloud/gophercloud/blob/master/openstack/networking/v2/extensions/portsecurity/results.go

Notice how there is only 1 struct, but we can use it for both:

Let me know if that helps.

@ozerovandrei
Copy link
Contributor

ozerovandrei commented May 19, 2019

@jtopjian thanks!

Is started playing with QoS and now I have more questions 😅

1 - Regarding qos_policy_id for the Port UpdateOpts.
How can we pass null as the string parameter into update request in case when we want to unset a QoS policy for a port?
It doesn't allow us to just pass an empty string.
I used commands from the config-qos#user-workflow, and got the following request:

{
    "port": {
        "qos_policy_id": null
    }
}

2 - Regarding design of the policies/rules CRUD.

We need to implement rule attribute of the policy object: #show-qos-policy-details.

To do this we must implement the rule object separately.

And the bad thing is that there are different rules objects with different URL paths and different attributes:

I guess that it's better to implement each rule in it's separate package. But how in that case we will be able to use slice of those different rules in the policy structure?

Can we do it with the structure composition? I mean can we implement a rule structure with common attributes across all rule types (id, tags) and then just use it across dscpmarkingrules, minimumbandwidthrules, bandwidthlimitrules packages?

@jtopjian
Copy link
Contributor

How can we pass null as the string parameter into update request in case when we want to unset a QoS policy for a port?

You'll probably want to have the field type as:

QosPolicyID *string `json:"qos_policy_id,omitempty"`

Then when a user specifies an empty string, setopts["qos_policy_id"] = nil in the ToPortUpdateMap method. For example:

func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) {
  base, err := opts.UpdateOptsBuilder.ToPortUpdateMap()
  if err != nil {
    return nil, err
  }

  port := base["port"].(map[string]interface{})

  if opts.QoSPolicyID != nil {
    qosPolicyID := *(opts.QosPolicyID)
    if qosPolicyID != "" {
      port["qos_policy_id"] = qosPolicyID
    } else {
      port["qos_policy_id"] = nil
    }
  }

  return base, nil
}

I guess that it's better to implement each rule in it's separate package.

The different types of rules can be in the same package (rules) and you can have different CRUD functions and structs for each one (CreateDSCPMarkingRuleOpts, CreateDSCPMarkingRule(), etc).

However, for a policy's results, the rules field will have to be map[string]interface{}. I don't see any other way around this. In order to provide a typed set of rules, two things can be done:

The dev/user using Gophercloud can do it themselves:

for _, rule := range policy.Rules {
  ruleID := rule["id"].(string)
  ruleType := rule["type"].(string)

  switch ruleType {
  case "bandwidth_limit":
    typedRule, err := rules.GetBandWidthLimitRule(client, ruleID).Extract()
  }
}

or you can create a helper in gophercloud/utils:

for _, rule := range policy.Rules {
  typedRule, err := qos_utils.ConvertMapToRule(rule)
}

Where qos_utils.ConvertMapToRule is a function in gophercloud/utils which does something like the following:

switch rule["type"].(string) {
case "bandwidth_limit":
  typedRule := rules.BandwidthLimitRule{
    ID: rule["id"].(string),
    Type: rule["type"].(string),
    MaxKBPS: rule["max_kbps"].(int64),
  }
}

However, in above solution, typedRule will always be interface{} and the dev/user will always have to do a type assertion when working with the rules:

if v, ok := typedRule.(rules.BandwidthLimitRule); ok {
  ...
}

or policies/results.go could have a type like:

type BaseRule {
  ID string `json:"id"`
  Type string `json:"type"`
  Extra map[string]interface{}
}

And use the internal.RemainingKeys helper function. This will allow the common fields of id and type to be in an explicit field while putting all unique fields in Extra. Either of the two other solutions can still be done to create a fully typed field. However, this is getting a little complicated and I'd opt not to do this method - just have policies.Policy.Rules[] be []map[string]interface{}.

Unfortunately none of the solutions are great, but at least they are solvable :)

Let me know if this helps or if I'm misunderstanding something.

@ozerovandrei
Copy link
Contributor

@jtopjian thank you, this is really good explanation. I think I will do it an add all of QoS stuff in terraform-provider-openstack in some near future.

@jtopjian
Copy link
Contributor

@ozerovandrei Slight nit with the List action: how about we rename it to ListBandwidthLimitRules instead of BandwidthLimitRulesList? This will help match the format of the other actions (Get and Create).

@ozerovandrei
Copy link
Contributor

@jtopjian sure

@ozerovandrei
Copy link
Contributor

All done.

@jtopjian
Copy link
Contributor

Nice work! 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants