Skip to content

Commit d3dbc22

Browse files
committed
Adds more documentation about filters
* Adds 'filters' module so that 'sl help filters' will output help text about how to use filters * adds reference to 'sl help filters' in 'sl cci list' help text. * makes filters easier to use. The space is not required in between the operation and the query value. For example '>2' will work as a filter.
1 parent 9deca81 commit d3dbc22

7 files changed

Lines changed: 114 additions & 56 deletions

File tree

SoftLayer/CLI/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def main(args=sys.argv[1:], env=Environment()):
157157
parse_main_args(['--help'])
158158

159159
module = env.load_module(module_name)
160-
actions = env.plugins[module_name]
160+
actions = env.plugins.get(module_name) or []
161161

162162
# handle `sl <command> ...`
163163
module_args = parse_module_args(

SoftLayer/CLI/modules/cci.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ class ListCCIs(CLIRunnable):
3636
3737
List CCIs
3838
39+
Examples:
40+
sl cci list --datacenter=dal05
41+
sl cci list --network=100 --cpu=2
42+
sl cci list --memory='>= 2048'
43+
sl cci list --tags=production,db
44+
45+
Options:
46+
--sortby=ARG Column to sort by. options: id, datacenter, host,
47+
Cores, memory, primary_ip, backend_ip
48+
3949
Filters:
4050
--hourly Show hourly instances
4151
--monthly Show monthly instances
@@ -45,17 +55,10 @@ class ListCCIs(CLIRunnable):
4555
-m --memory=MEMORY Memory in mebibytes (n * 1024)
4656
-d DC, --datacenter=DC datacenter shortname (sng01, dal05, ...)
4757
-n MBPS, --network=MBPS Network port speed in Mbps
48-
--tags=ARG Only show instances that have one of these tags
58+
--tags=ARG Only show instances that have one of these tags.
59+
Comma-separated. (production,db)
4960
50-
Options:
51-
--sortby=ARG Column to sort by. options: id, datacenter, host,
52-
Cores, memory, primary_ip, backend_ip
53-
54-
Examples:
55-
sl cci list --datacenter=dal05
56-
sl cci list --network=100 --cpu=2
57-
sl cci list --memory='>= 2048'
58-
sl cci list --tags=production,db
61+
For more on filters see 'sl help filters'
5962
"""
6063
action = 'list'
6164

@@ -140,8 +143,8 @@ def execute(client, args):
140143
t.add_row(['cores', result['maxCpu']])
141144
t.add_row(['memory', mb_to_gb(result['maxMemory'])])
142145
t.add_row(['public_ip', result.get('primaryIpAddress', blank())])
143-
t.add_row(['private_ip',
144-
result.get('primaryBackendIpAddress', blank())])
146+
t.add_row(
147+
['private_ip', result.get('primaryBackendIpAddress', blank())])
145148
t.add_row([
146149
'os',
147150
FormattedItem(

SoftLayer/CLI/modules/filters.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
usage: sl help filters
3+
4+
Filters are used to limit the amount of results. Some commands will except a
5+
filter operation for certain fields.
6+
7+
Available Operations:
8+
'value' exact value match
9+
'> value' greater than value
10+
'< value' less than value
11+
'>= value' greater than or equal to value
12+
'<= value' less than or equal to value
13+
'value*' begins with value
14+
'*value' ends with value
15+
'*value*' contains value
16+
17+
Examples:
18+
sl cci list --hostname='prod.*'
19+
sl cci list --datacenter=dal05
20+
sl cci list --network=100 --cpu=2
21+
sl cci list --memory='>= 2048'
22+
sl cci list --tags=production,db
23+
24+
"""
25+
# :copyright: (c) 2013, SoftLayer Technologies, Inc. All rights reserved.
26+
# :license: BSD, see LICENSE for more details.

SoftLayer/tests/API/cci_tests.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,6 @@ def test_list_instances_with_filters(self):
7171
mask=ANY,
7272
))
7373

74-
def test_query_filter(self):
75-
result = SoftLayer.CCI.query_filter('test')
76-
self.assertEqual({'operation': 'test'}, result)
77-
78-
result = SoftLayer.CCI.query_filter('*test')
79-
self.assertEqual({'operation': '$= test'}, result)
80-
81-
result = SoftLayer.CCI.query_filter('test*')
82-
self.assertEqual({'operation': '^= test'}, result)
83-
84-
result = SoftLayer.CCI.query_filter('*test*')
85-
self.assertEqual({'operation': '~ test'}, result)
86-
87-
result = SoftLayer.CCI.query_filter('> 10')
88-
self.assertEqual({'operation': '> 10'}, result)
89-
90-
result = SoftLayer.CCI.query_filter(10)
91-
self.assertEqual({'operation': 10}, result)
92-
9374
def test_get_instance(self):
9475
self.client.__getitem__().getObject.return_value = {
9576
'hourlyVirtualGuests': "this is unique"}

SoftLayer/tests/CLI/core_tests.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -204,27 +204,3 @@ def test_sequentialoutput(self):
204204
t.blanks = True
205205
output = cli.core.format_output(t)
206206
self.assertEqual("This is a test\n\nMore tests", output)
207-
208-
def test_nesteddict(self):
209-
n = cli.helpers.NestedDict()
210-
211-
self.assertEqual(n['test'], cli.helpers.NestedDict())
212-
213-
n['test_set'] = 1
214-
self.assertEqual(n['test_set'], 1)
215-
216-
d = {
217-
'test': {
218-
'nested': 1
219-
}}
220-
221-
n = cli.helpers.NestedDict(d)
222-
self.assertEqual(d, n)
223-
self.assertEqual(n['test']['nested'], 1)
224-
225-
# new default top level elements should return a new NestedDict()
226-
self.assertEqual(n['not']['nested'], cli.helpers.NestedDict())
227-
228-
# NestedDict doesn't convert dict children, just the top level dict
229-
# so assuming this behavior down inside of a dict is not plausible
230-
self.assertRaises(KeyError, lambda: n['test']['not']['nested'])

SoftLayer/tests/basic_tests.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616

1717
class TestExceptions(unittest.TestCase):
18+
1819
def test_softlayer_api_error(self):
1920
e = SoftLayer.SoftLayerAPIError('fault code', 'fault string')
2021
self.assertEquals(e.faultCode, 'fault code')
@@ -34,3 +35,68 @@ def test_parse_error(self):
3435
repr(e), "<ParseError(fault code): fault string>")
3536
self.assertEquals(
3637
str(e), "ParseError(fault code): fault string")
38+
39+
40+
class TestUtils(unittest.TestCase):
41+
42+
def test_query_filter(self):
43+
result = SoftLayer.utils.query_filter('test')
44+
self.assertEqual({'operation': 'test'}, result)
45+
46+
result = SoftLayer.utils.query_filter('*test')
47+
self.assertEqual({'operation': '$= test'}, result)
48+
49+
result = SoftLayer.utils.query_filter('test*')
50+
self.assertEqual({'operation': '^= test'}, result)
51+
52+
result = SoftLayer.utils.query_filter('*test*')
53+
self.assertEqual({'operation': '~ test'}, result)
54+
55+
result = SoftLayer.utils.query_filter('> 10')
56+
self.assertEqual({'operation': '> 10'}, result)
57+
58+
result = SoftLayer.utils.query_filter('>10')
59+
self.assertEqual({'operation': '> 10'}, result)
60+
61+
result = SoftLayer.utils.query_filter(10)
62+
self.assertEqual({'operation': 10}, result)
63+
64+
65+
class TestNestedDict(unittest.TestCase):
66+
67+
def test_basic(self):
68+
n = SoftLayer.utils.NestedDict()
69+
self.assertEqual(n['test'], SoftLayer.utils.NestedDict())
70+
71+
n['test_set'] = 1
72+
self.assertEqual(n['test_set'], 1)
73+
74+
d = {
75+
'test': {
76+
'nested': 1
77+
}}
78+
79+
n = SoftLayer.utils.NestedDict(d)
80+
self.assertEqual(d, n)
81+
self.assertEqual(n['test']['nested'], 1)
82+
83+
# new default top level elements should return a new NestedDict()
84+
self.assertEqual(n['not']['nested'], SoftLayer.utils.NestedDict())
85+
86+
# NestedDict doesn't convert dict children, just the top level dict
87+
# so you can't assume the same behavior with children
88+
self.assertRaises(KeyError, lambda: n['test']['not']['nested'])
89+
90+
def test_to_dict(self):
91+
n = SoftLayer.utils.NestedDict()
92+
n['test']['test1']['test2']['test3'] = {}
93+
d = n.to_dict()
94+
95+
self.assertEqual({
96+
'test': {'test1': {'test2': {'test3': {}}}}
97+
}, d)
98+
self.assertEqual(dict, type(d))
99+
self.assertEqual(dict, type(d['test']))
100+
self.assertEqual(dict, type(d['test']['test1']))
101+
self.assertEqual(dict, type(d['test']['test1']['test2']))
102+
self.assertEqual(dict, type(d['test']['test1']['test2']['test3']))

SoftLayer/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
KNOWN_OPERATIONS = ['<=', '>=', '<', '>', '~', '*=', '^=', '$=', '_=', '!~']
12

23

34
class NestedDict(dict):
@@ -39,5 +40,10 @@ def query_filter(query):
3940
query = "$= %s" % query.strip('*')
4041
elif query.endswith('*'):
4142
query = "^= %s" % query.strip('*')
43+
else:
44+
for op in KNOWN_OPERATIONS:
45+
if query.startswith(op):
46+
query = "%s %s" % (op, query[len(op):].strip())
47+
break
4248

4349
return {'operation': query}

0 commit comments

Comments
 (0)