Skip to content

Commit 8e516be

Browse files
Merge pull request #1281 from allmightyspiff/tags
Tags
2 parents 81808b6 + 39627a9 commit 8e516be

23 files changed

+976
-59
lines changed

SoftLayer/CLI/core.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
PROG_NAME = "slcli (SoftLayer Command-line)"
3636
VALID_FORMATS = ['table', 'raw', 'json', 'jsonraw']
3737
DEFAULT_FORMAT = 'raw'
38+
3839
if sys.stdout.isatty():
3940
DEFAULT_FORMAT = 'table'
4041

@@ -157,6 +158,7 @@ def cli(env,
157158

158159
logger.setLevel(DEBUG_LOGGING_MAP.get(verbose, logging.DEBUG))
159160
env.vars['_timings'] = SoftLayer.DebugTransport(env.client.transport)
161+
env.vars['verbose'] = verbose
160162
env.client.transport = env.vars['_timings']
161163

162164

SoftLayer/CLI/routes.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,14 @@
290290
('subnet:list', 'SoftLayer.CLI.subnet.list:cli'),
291291
('subnet:lookup', 'SoftLayer.CLI.subnet.lookup:cli'),
292292

293+
('tags', 'SoftLayer.CLI.tags'),
294+
('tags:cleanup', 'SoftLayer.CLI.tags.cleanup:cli'),
295+
('tags:list', 'SoftLayer.CLI.tags.list:cli'),
296+
('tags:set', 'SoftLayer.CLI.tags.set:cli'),
297+
('tags:details', 'SoftLayer.CLI.tags.details:cli'),
298+
('tags:delete', 'SoftLayer.CLI.tags.delete:cli'),
299+
('tags:taggable', 'SoftLayer.CLI.tags.taggable:cli'),
300+
293301
('ticket', 'SoftLayer.CLI.ticket'),
294302
('ticket:create', 'SoftLayer.CLI.ticket.create:cli'),
295303
('ticket:detail', 'SoftLayer.CLI.ticket.detail:cli'),

SoftLayer/CLI/tags/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Manage Tags"""

SoftLayer/CLI/tags/cleanup.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Removes unused Tags"""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.managers.tags import TagManager
8+
9+
10+
@click.command()
11+
@click.option('--dry-run', '-d', is_flag=True, default=False,
12+
help="Don't delete, just show what will be deleted.")
13+
@environment.pass_env
14+
def cli(env, dry_run):
15+
"""Removes all empty tags."""
16+
17+
tag_manager = TagManager(env.client)
18+
empty_tags = tag_manager.get_unattached_tags()
19+
20+
for tag in empty_tags:
21+
if dry_run:
22+
click.secho("(Dry Run) Removing {}".format(tag.get('name')), fg='yellow')
23+
else:
24+
result = tag_manager.delete_tag(tag.get('name'))
25+
color = 'green' if result else 'red'
26+
click.secho("Removing {}".format(tag.get('name')), fg=color)

SoftLayer/CLI/tags/delete.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Delete Tags."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.managers.tags import TagManager
8+
9+
10+
@click.command()
11+
@click.argument('identifier')
12+
@click.option('--name', required=False, default=False, is_flag=True, show_default=False,
13+
help='Assume identifier is a tag name. Useful if your tag name is a number.')
14+
@environment.pass_env
15+
def cli(env, identifier, name):
16+
"""Delete a Tag. Tag names that contain spaces need to be encased in quotes"""
17+
18+
tag_manager = TagManager(env.client)
19+
tag_name = identifier
20+
# If the identifier is a int, and user didn't tell us it was a name.
21+
if str.isdigit(identifier) and not name:
22+
tag = tag_manager.get_tag(identifier)
23+
tag_name = tag.get('name', None)
24+
25+
result = tag_manager.delete_tag(tag_name)
26+
if result:
27+
click.secho("Tag {} has been removed".format(tag_name), fg='green')
28+
else:
29+
click.secho("Failed to remove tag {}".format(tag_name), fg='red')

SoftLayer/CLI/tags/details.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Details of a Tag."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI.tags.list import detailed_table
8+
from SoftLayer.managers.tags import TagManager
9+
10+
11+
@click.command()
12+
@click.argument('identifier')
13+
@click.option('--name', required=False, default=False, is_flag=True, show_default=False,
14+
help='Assume identifier is a tag name. Useful if your tag name is a number.')
15+
@environment.pass_env
16+
def cli(env, identifier, name):
17+
"""Get details for a Tag. Identifier can be either a name or tag-id"""
18+
19+
tag_manager = TagManager(env.client)
20+
21+
# If the identifier is a int, and user didn't tell us it was a name.
22+
if str.isdigit(identifier) and not name:
23+
tags = [tag_manager.get_tag(identifier)]
24+
else:
25+
tags = tag_manager.get_tag_by_name(identifier)
26+
table = detailed_table(tag_manager, tags)
27+
env.fout(table)

SoftLayer/CLI/tags/list.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""List Tags."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI import formatting
8+
from SoftLayer.exceptions import SoftLayerAPIError
9+
from SoftLayer.managers.tags import TagManager
10+
from SoftLayer import utils
11+
12+
# pylint: disable=unnecessary-lambda
13+
14+
15+
@click.command()
16+
@click.option('--detail', '-d', is_flag=True, default=False,
17+
help="Show information about the resources using this tag.")
18+
@environment.pass_env
19+
def cli(env, detail):
20+
"""List Tags."""
21+
22+
tag_manager = TagManager(env.client)
23+
24+
if detail:
25+
tables = detailed_table(tag_manager, tag_manager.get_attached_tags())
26+
for table in tables:
27+
env.fout(table)
28+
else:
29+
table = simple_table(tag_manager)
30+
env.fout(table)
31+
# pp(tags.list_tags())
32+
33+
34+
def tag_row(tag):
35+
"""Format a tag table row"""
36+
return [tag.get('id'), tag.get('name'), tag.get('referenceCount', 0)]
37+
38+
39+
def detailed_table(tag_manager, tags):
40+
"""Creates a table for each tag, with details about resources using it"""
41+
tables = []
42+
for tag in tags:
43+
references = tag_manager.get_tag_references(tag.get('id'))
44+
# pp(references)
45+
new_table = formatting.Table(['Id', 'Type', 'Resource'], title=tag.get('name'))
46+
for reference in references:
47+
tag_type = utils.lookup(reference, 'tagType', 'keyName')
48+
resource_id = reference.get('resourceTableId')
49+
resource_row = get_resource_name(tag_manager, resource_id, tag_type)
50+
new_table.add_row([resource_id, tag_type, resource_row])
51+
tables.append(new_table)
52+
53+
return tables
54+
55+
56+
def simple_table(tag_manager):
57+
"""Just tags and how many resources on each"""
58+
tags = tag_manager.list_tags()
59+
table = formatting.Table(['Id', 'Tag', 'Count'], title='Tags')
60+
for tag in tags.get('attached', []):
61+
table.add_row(tag_row(tag))
62+
for tag in tags.get('unattached', []):
63+
table.add_row(tag_row(tag))
64+
return table
65+
66+
67+
def get_resource_name(tag_manager, resource_id, tag_type):
68+
"""Returns a string to identify a resource"""
69+
name = None
70+
try:
71+
resource = tag_manager.reference_lookup(resource_id, tag_type)
72+
name = tag_manager.get_resource_name(resource, tag_type)
73+
except SoftLayerAPIError as exception:
74+
name = "{}".format(exception.reason)
75+
return name

SoftLayer/CLI/tags/set.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Set Tags."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.managers.tags import TagManager
8+
9+
10+
@click.command()
11+
@click.option('--tags', '-t', type=click.STRING, required=True,
12+
help='Comma seperated list of tags, enclosed in quotes. "tag1, tag2"')
13+
@click.option('--key-name', '-k', type=click.STRING, required=True,
14+
help="Key name of a tag type e.g. GUEST, HARDWARE. See slcli tags taggable output.")
15+
@click.option('--resource-id', '-r', type=click.INT, required=True, help="ID of the object being tagged")
16+
@environment.pass_env
17+
def cli(env, tags, key_name, resource_id):
18+
"""Set Tags."""
19+
20+
tag_manager = TagManager(env.client)
21+
tags = tag_manager.set_tags(tags, key_name, resource_id)
22+
23+
if tags:
24+
click.secho("Set tags successfully", fg='green')
25+
else:
26+
click.secho("Failed to set tags", fg='red')

SoftLayer/CLI/tags/taggable.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""List everything that could be tagged."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI import formatting
8+
from SoftLayer.managers.tags import TagManager
9+
10+
11+
@click.command()
12+
@environment.pass_env
13+
def cli(env):
14+
"""List everything that could be tagged."""
15+
16+
tag_manager = TagManager(env.client)
17+
tag_types = tag_manager.get_all_tag_types()
18+
for tag_type in tag_types:
19+
title = "{} ({})".format(tag_type['description'], tag_type['keyName'])
20+
table = formatting.Table(['Id', 'Name'], title=title)
21+
resources = tag_manager.taggable_by_type(tag_type['keyName'])
22+
for resource in resources:
23+
table.add_row([
24+
resource['resource']['id'],
25+
tag_manager.get_resource_name(resource['resource'], tag_type['keyName'])
26+
])
27+
env.fout(table)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
getObject = {
2+
'id': 1234,
3+
'globalIdentifier': 'xxxxc-asd',
4+
'datacenter': {'id': 12, 'name': 'DALLAS21',
5+
'description': 'Dallas 21'},
6+
'billingItem': {
7+
'id': 6327,
8+
'recurringFee': 1.54,
9+
'nextInvoiceTotalRecurringAmount': 16.08,
10+
'children': [
11+
{'description': 'test', 'nextInvoiceTotalRecurringAmount': 1},
12+
],
13+
'orderItem': {
14+
'order': {
15+
'userRecord': {
16+
'username': 'bob',
17+
}
18+
}
19+
}
20+
},
21+
'primaryIpAddress': '4.4.4.4',
22+
'hostname': 'testtest1',
23+
'domain': 'test.sftlyr.ws',
24+
'bareMetalInstanceFlag': True,
25+
'fullyQualifiedDomainName': 'testtest1.test.sftlyr.ws',
26+
'processorPhysicalCoreAmount': 4,
27+
'memoryCapacity': 4,
28+
'primaryBackendIpAddress': '10.4.4.4',
29+
'networkManagementIpAddress': '10.4.4.4',
30+
'hardwareStatus': {'status': 'ACTIVE'},
31+
'primaryNetworkComponent': {'maxSpeed': 1000, 'speed': 1000},
32+
'provisionDate': '2020-08-01 15:23:45',
33+
'notes': 'NOTES NOTES NOTES',
34+
'operatingSystem': {
35+
'softwareLicense': {
36+
'softwareDescription': {
37+
'referenceCode': 'UBUNTU_20_64',
38+
'name': 'Ubuntu',
39+
'version': 'Ubuntu 20.04 LTS',
40+
}
41+
},
42+
'passwords': [
43+
{'username': 'root', 'password': 'xxxxxxxxxxxx'}
44+
],
45+
},
46+
'remoteManagementAccounts': [
47+
{'username': 'root', 'password': 'zzzzzzzzzzzzzz'}
48+
],
49+
'networkVlans': [
50+
{
51+
'networkSpace': 'PRIVATE',
52+
'vlanNumber': 1234,
53+
'id': 11111
54+
},
55+
],
56+
'tagReferences': [
57+
{'tag': {'name': 'a tag'}}
58+
],
59+
}

0 commit comments

Comments
 (0)