Skip to content

Commit 35139b4

Browse files
Merge pull request #2112 from allmightyspiff/threads
Added a thread capable client.cf_call() function
2 parents 60b4f84 + 09547e6 commit 35139b4

File tree

4 files changed

+71
-42
lines changed

4 files changed

+71
-42
lines changed

SoftLayer/API.py

+38-8
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
import time
1010
import warnings
1111

12+
import concurrent.futures as cf
1213
import json
1314
import logging
15+
import math
1416
import requests
1517

16-
1718
from SoftLayer import auth as slauth
1819
from SoftLayer import config
1920
from SoftLayer import consts
@@ -289,13 +290,6 @@ def call(self, service, method, *args, **kwargs):
289290
request.verify = kwargs.get('verify')
290291

291292
if self.auth:
292-
extra_headers = self.auth.get_headers()
293-
if extra_headers:
294-
warnings.warn("auth.get_headers() is deprecated and will be "
295-
"removed in the next major version",
296-
DeprecationWarning)
297-
request.headers.update(extra_headers)
298-
299293
request = self.auth.get_request(request)
300294

301295
request.headers.update(kwargs.get('headers', {}))
@@ -352,6 +346,42 @@ def iter_call(self, service, method, *args, **kwargs):
352346

353347
offset += limit
354348

349+
def cf_call(self, service, method, *args, **kwargs):
350+
"""Uses threads to iterate through API calls.
351+
352+
:param service: the name of the SoftLayer API service
353+
:param method: the method to call on the service
354+
:param integer limit: result size for each API call (defaults to 100)
355+
:param \\*args: same optional arguments that ``Service.call`` takes
356+
:param \\*\\*kwargs: same optional keyword arguments that ``Service.call`` takes
357+
"""
358+
limit = kwargs.pop('limit', 100)
359+
offset = kwargs.pop('offset', 0)
360+
361+
if limit <= 0:
362+
raise AttributeError("Limit size should be greater than zero.")
363+
# This initial API call is to determine how many API calls we need to make after this first one.
364+
first_call = self.call(service, method, offset=offset, limit=limit, *args, **kwargs)
365+
366+
# This was not a list result, just return it.
367+
if not isinstance(first_call, transports.SoftLayerListResult):
368+
return first_call
369+
# How many more API calls we have to make
370+
api_calls = math.ceil((first_call.total_count - limit) / limit)
371+
372+
def this_api(offset):
373+
"""Used to easily call executor.map() on this fuction"""
374+
return self.call(service, method, offset=offset, limit=limit, *args, **kwargs)
375+
376+
with cf.ThreadPoolExecutor(max_workers=10) as executor:
377+
future_results = {}
378+
offset_map = [x * limit for x in range(1, api_calls)]
379+
future_results = list(executor.map(this_api, offset_map))
380+
# Append the results in the order they were called
381+
for call_result in future_results:
382+
first_call = first_call + call_result
383+
return first_call
384+
355385
def __repr__(self):
356386
return "Client(transport=%r, auth=%r)" % (self.transport, self.auth)
357387

SoftLayer/CLI/vlan/list.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,13 @@
2525

2626

2727
@click.command(cls=SoftLayer.CLI.command.SLCommand, )
28-
@click.option('--sortby',
29-
help='Column to sort by',
30-
type=click.Choice(COLUMNS))
28+
@click.option('--sortby', help='Column to sort by', type=click.Choice(COLUMNS))
3129
@click.option('--datacenter', '-d',
3230
help='Filter by datacenter shortname (sng01, dal05, ...)')
3331
@click.option('--number', '-n', help='Filter by VLAN number')
3432
@click.option('--name', help='Filter by VLAN name')
35-
@click.option('--limit', '-l',
36-
help='How many results to get in one api call, default is 100',
37-
default=100,
38-
show_default=True)
33+
@click.option('--limit', '-l', default=100, show_default=True,
34+
help='How many results to get in one api call, default is 100')
3935
@environment.pass_env
4036
def cli(env, sortby, datacenter, number, name, limit):
4137
"""List VLANs.

SoftLayer/managers/network.py

+12-21
Original file line numberDiff line numberDiff line change
@@ -515,46 +515,37 @@ def list_subnets(self, identifier=None, datacenter=None, version=0,
515515
kwargs['iter'] = True
516516
return self.client.call('Account', 'getSubnets', **kwargs)
517517

518-
def list_vlans(self, datacenter=None, vlan_number=None, name=None, limit=100, **kwargs):
518+
def list_vlans(self, datacenter=None, vlan_number=None, name=None, limit=100, mask=None, _filter=None):
519519
"""Display a list of all VLANs on the account.
520520
521521
This provides a quick overview of all VLANs including information about
522522
data center residence and the number of devices attached.
523523
524-
:param string datacenter: If specified, the list will only contain
525-
VLANs in the specified data center.
526-
:param int vlan_number: If specified, the list will only contain the
527-
VLAN matching this VLAN number.
528-
:param int name: If specified, the list will only contain the
529-
VLAN matching this VLAN name.
524+
:param string datacenter: If specified, the list will only contain VLANs in the specified data center.
525+
:param int vlan_number: If specified, the list will only contain the VLAN matching this VLAN number.
526+
:param int name: If specified, the list will only contain the VLAN matching this VLAN name.
530527
:param dict \\*\\*kwargs: response-level options (mask, limit, etc.)
531528
532529
"""
533-
_filter = utils.NestedDict(kwargs.get('filter') or {})
530+
_filter = utils.NestedDict(_filter or {})
534531

535532
_filter['networkVlans']['id'] = utils.query_filter_orderby()
536533

537534
if vlan_number:
538-
_filter['networkVlans']['vlanNumber'] = (
539-
utils.query_filter(vlan_number))
535+
_filter['networkVlans']['vlanNumber'] = utils.query_filter(vlan_number)
540536

541537
if name:
542538
_filter['networkVlans']['name'] = utils.query_filter(name)
543539

544540
if datacenter:
545-
_filter['networkVlans']['primaryRouter']['datacenter']['name'] = (
546-
utils.query_filter(datacenter))
547-
548-
kwargs['filter'] = _filter.to_dict()
541+
_filter['networkVlans']['primaryRouter']['datacenter']['name'] = utils.query_filter(datacenter)
549542

550-
if 'mask' not in kwargs:
551-
kwargs['mask'] = DEFAULT_VLAN_MASK
543+
if mask is None:
544+
mask = DEFAULT_VLAN_MASK
552545

553-
kwargs['iter'] = True
554-
if limit > 0:
555-
return self.account.getNetworkVlans(mask=kwargs['mask'], filter=_filter.to_dict(), limit=limit, iter=True)
556-
else:
557-
return self.account.getNetworkVlans(mask=kwargs['mask'], filter=_filter.to_dict(), iter=True)
546+
# cf_call uses threads to get all results.
547+
return self.client.cf_call('SoftLayer_Account', 'getNetworkVlans',
548+
mask=mask, filter=_filter.to_dict(), limit=limit)
558549

559550
def list_securitygroups(self, **kwargs):
560551
"""List security groups."""

tests/CLI/modules/vlan_tests.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,15 @@ def test_vlan_list_get_pod_with_closed_announcement(self, ngb_mock):
147147
ngb_mock.return_value = True
148148
vlan_mock = self.set_mock('SoftLayer_Account', 'getNetworkVlans')
149149
gpods_mock = self.set_mock('SoftLayer_Network_Pod', 'getAllObjects')
150-
vlan_mock.return_value = {'primaryRouter': {'fullyQualifiedDomainName': 'bcr01a.fra02.softlayer.com',
151-
'id': 462912},
152-
'tagReferences': ''}
150+
vlan_mock.return_value = [
151+
{
152+
"primaryRouter": {
153+
"fullyQualifiedDomainName": "bcr01a.fra02.softlayer.com",
154+
"id": 462912
155+
},
156+
"tagReferences": ""
157+
}
158+
]
153159
gpods_mock.return_value = [{'backendRouterId': 462912,
154160
'backendRouterName': 'bcr01a.fra02',
155161
'datacenterId': 449506,
@@ -166,9 +172,15 @@ def test_vlan_list_get_pod_with_closed_announcement_no_closure(self, ngb_mock):
166172
ngb_mock.return_value = True
167173
vlan_mock = self.set_mock('SoftLayer_Account', 'getNetworkVlans')
168174
gpods_mock = self.set_mock('SoftLayer_Network_Pod', 'getAllObjects')
169-
vlan_mock.return_value = {'primaryRouter': {'fullyQualifiedDomainName': 'bcr01a.fra02.softlayer.com',
170-
'id': 462912},
171-
'tagReferences': ''}
175+
vlan_mock.return_value = [
176+
{
177+
"primaryRouter": {
178+
"fullyQualifiedDomainName": "bcr01a.fra02.softlayer.com",
179+
"id": 462912
180+
},
181+
"tagReferences": ""
182+
}
183+
]
172184
gpods_mock.return_value = [{'backendRouterId': 462912,
173185
'backendRouterName': 'bcr01a.fra02',
174186
'datacenterId': 449506,

0 commit comments

Comments
 (0)