Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #458 from najeal/najeal-228-vm-filtering
Browse files Browse the repository at this point in the history
Added: vm filtering
  • Loading branch information
stealthybox committed Nov 11, 2019
2 parents b0469c3 + 8cede12 commit ba63c96
Show file tree
Hide file tree
Showing 6 changed files with 735 additions and 9 deletions.
23 changes: 23 additions & 0 deletions cmd/ignite/cmd/vmcmd/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ func NewCmdPs(out io.Writer) *cobra.Command {
Long: dedent.Dedent(`
List all running VMs. By specifying the all flag (-a, --all),
also list VMs that are not currently running.
Using the -f (--filter) flag, you can give conditions VMs should fullfilled to be displayed.
You can filter on all the underlying fields of the VM struct, see the documentation:
https://ignite.readthedocs.io/en/stable/api/ignite_v1alpha2.html#VM.
Different operators can be used:
- "=" and "==" for the equal
- "!=" for the is not equal
- "=~" for the contains
- "!~" for the not contains
Non-exhaustive list of identifiers to apply filter on:
- the VM name
- CPUs usage
- Labels
- Image
- Kernel
- Memory
Example usage:
$ ignite ps -f "{{.ObjectMeta.Name}}=my-vm2,{{.Spec.CPUs}}!=3,{{.Spec.Image.OCI}}=~weaveworks/ignite-ubuntu"
$ ignite ps -f "{{.Spec.Memory}}=~1024,{{.Status.Running}}=true"
`),
Run: func(cmd *cobra.Command, args []string) {
// If `ps` is called via any of its aliases
Expand All @@ -46,4 +68,5 @@ func NewCmdPs(out io.Writer) *cobra.Command {

func addPsFlags(fs *pflag.FlagSet, pf *run.PsFlags) {
fs.BoolVarP(&pf.All, "all", "a", false, "Show all VMs, not just running ones")
fs.StringVarP(&pf.Filter, "filter", "f", "", "Filter the VMs")
}
32 changes: 27 additions & 5 deletions cmd/ignite/run/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (
)

type PsFlags struct {
All bool
All bool
Filter string
}

type psOptions struct {
Expand All @@ -25,16 +26,37 @@ func (pf *PsFlags) NewPsOptions() (po *psOptions, err error) {
}

func Ps(po *psOptions) error {
var filters *filter.MultipleMetaFilter
var err error
var filtering bool
if po.PsFlags.Filter != "" {
filtering = true
filters, err = filter.GenerateMultipleMetadataFiltering(po.PsFlags.Filter)
if err != nil {
return err
}
}
o := util.NewOutput()
defer o.Flush()

o.Write("VM ID", "IMAGE", "KERNEL", "SIZE", "CPUS", "MEMORY", "CREATED", "STATUS", "IPS", "PORTS", "NAME")
for _, vm := range po.allVMs {
o.Write(vm.GetUID(), vm.Spec.Image.OCI, vm.Spec.Kernel.OCI,
vm.Spec.DiskSize, vm.Spec.CPUs, vm.Spec.Memory, formatCreated(vm), formatStatus(vm), vm.Status.IPAddresses,
vm.Spec.Network.Ports, vm.GetName())
isExpectedVM := true
if filtering {
isExpectedVM, err = filters.AreExpected(vm)
if err != nil {
return err
}
}
if err != nil {
return err
}
if isExpectedVM {
o.Write(vm.GetUID(), vm.Spec.Image.OCI, vm.Spec.Kernel.OCI,
vm.Spec.DiskSize, vm.Spec.CPUs, vm.Spec.Memory, formatCreated(vm), formatStatus(vm), vm.Status.IPAddresses,
vm.Spec.Network.Ports, vm.GetName())
}
}

return nil
}

Expand Down
27 changes: 25 additions & 2 deletions docs/cli/ignite/ignite_ps.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ List running VMs

List all running VMs. By specifying the all flag (-a, --all),
also list VMs that are not currently running.
Using the -f (--filter) flag, you can give conditions VMs should fullfilled to be displayed.
You can filter on all the underlying fields of the VM struct, see the documentation:
https://ignite.readthedocs.io/en/stable/api/ignite_v1alpha2.html#VM.

Different operators can be used:
- "=" and "==" for the equal
- "!=" for the is not equal
- "=~" for the contains
- "!~" for the not contains

Non-exhaustive list of identifiers to apply filter on:
- the VM name
- CPUs usage
- Labels
- Image
- Kernel
- Memory

Example usage:
$ ignite ps -f "{{.ObjectMeta.Name}}=my-vm2,{{.Spec.CPUs}}!=3,{{.Spec.Image.OCI}}=~weaveworks/ignite-ubuntu"

$ ignite ps -f "{{.Spec.Memory}}=~1024,{{.Status.Running}}=true"


```
Expand All @@ -16,8 +38,9 @@ ignite ps [flags]
### Options

```
-a, --all Show all VMs, not just running ones
-h, --help help for ps
-a, --all Show all VMs, not just running ones
-f, --filter string Filter the VMs
-h, --help help for ps
```

### Options inherited from parent commands
Expand Down
27 changes: 25 additions & 2 deletions docs/cli/ignite/ignite_vm_ps.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ List running VMs

List all running VMs. By specifying the all flag (-a, --all),
also list VMs that are not currently running.
Using the -f (--filter) flag, you can give conditions VMs should fullfilled to be displayed.
You can filter on all the underlying fields of the VM struct, see the documentation:
https://ignite.readthedocs.io/en/stable/api/ignite_v1alpha2.html#VM.

Different operators can be used:
- "=" and "==" for the equal
- "!=" for the is not equal
- "=~" for the contains
- "!~" for the not contains

Non-exhaustive list of identifiers to apply filter on:
- the VM name
- CPUs usage
- Labels
- Image
- Kernel
- Memory

Example usage:
$ ignite ps -f "{{.ObjectMeta.Name}}=my-vm2,{{.Spec.CPUs}}!=3,{{.Spec.Image.OCI}}=~weaveworks/ignite-ubuntu"

$ ignite ps -f "{{.Spec.Memory}}=~1024,{{.Status.Running}}=true"


```
Expand All @@ -16,8 +38,9 @@ ignite vm ps [flags]
### Options

```
-a, --all Show all VMs, not just running ones
-h, --help help for ps
-a, --all Show all VMs, not just running ones
-f, --filter string Filter the VMs
-h, --help help for ps
```

### Options inherited from parent commands
Expand Down
118 changes: 118 additions & 0 deletions pkg/filter/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package filter

import (
"bytes"
"fmt"
"regexp"
"strings"
"text/template"

api "github.com/weaveworks/ignite/pkg/apis/ignite"
)

const (
filterSeparator = ","
filterApplyFailed = "failed to apply filtering"
regexString = `^(?P<key>{{(?:\.|[a-zA-Z0-9]+)+}})(?P<operator>(?:=|==|!=|=~|!~))(?P<value>[a-zA-Z0-9-_:/\.\s]+)$`
)

type metaFilter struct {
identifier string
expectedValue string
operator string
}

func (mf metaFilter) isExpected(object *api.VM) (bool, error) {
w := &bytes.Buffer{}
tm, err := template.New("generic-filtering-vm").Parse(mf.identifier)
if err != nil {
return false, fmt.Errorf("failed to configure filtering with following template: %s", mf.identifier)
}
err = tm.Execute(w, object)
if err != nil {
return false, fmt.Errorf("failed to apply filtering on VM, the filter might be incorrect")
}
res := w.String()
switch mf.operator {
case "==":
return mf.isEqual(res), nil
case "=":
return mf.isEqual(res), nil
case "!=":
return !mf.isEqual(res), nil
case "=~":
return mf.contains(res), nil
case "!~":
return !mf.contains(res), nil
default:
return false, fmt.Errorf("Unexpected operator: %s", mf.operator)
}
}

func (mf metaFilter) isEqual(value string) bool {
return mf.expectedValue == value
}

func (mf metaFilter) contains(value string) bool {
return strings.Contains(value, mf.expectedValue)
}

// MultipleMetaFilter stores multiples metaFilter rule
type MultipleMetaFilter struct {
filters []metaFilter
}

// AreExpected checks fileting rules are expected, an AND logical condition is applid between the underlying filters
func (mmf *MultipleMetaFilter) AreExpected(object *api.VM) (bool, error) {
for _, mf := range mmf.filters {
res, err := mf.isExpected(object)
if err != nil {
return false, err
} else if !res {
return false, nil
}
}
return true, nil
}

// extractKeyValueFiltering extracts the key to search for and the expected value form a string
func extractKeyValueFiltering(str string) (string, string, string, error) {
reg, err := regexp.Compile(regexString)
if err != nil {
return "", "", "", err
}
matches := reg.FindAllStringSubmatch(str, -1)
if len(matches) != 1 {
return "", "", "", fmt.Errorf("failed to generate filter")
}
match := matches[0]
if len(match) != 4 {
return "", "", "", fmt.Errorf("failed to generate filter")
}
return match[1], match[3], match[2], nil
}

// extractMultipleKeyValueFiltering extracts all the keys and values to filter
func extractMultipleKeyValueFiltering(f string) ([]metaFilter, error) {
filterList := strings.Split(f, filterSeparator)
captureList := make([]metaFilter, 0, len(filterList))
for _, filter := range filterList {
key, value, op, err := extractKeyValueFiltering(filter)
if err != nil {
return nil, fmt.Errorf("failed to extract keys-values from filter list %s", filterList)
}
captureList = append(captureList, metaFilter{identifier: key, expectedValue: value, operator: op})
}
return captureList, nil
}

// GenerateMultipleMetadataFiltering extract filterings and generates MultipleMetadataFiltering
func GenerateMultipleMetadataFiltering(str string) (*MultipleMetaFilter, error) {
metaFilterList, err := extractMultipleKeyValueFiltering(str)
if err != nil {
return nil, err
}
return &MultipleMetaFilter{
filters: metaFilterList,
}, nil
}
Loading

0 comments on commit ba63c96

Please sign in to comment.