Skip to content

Commit 2bbbab1

Browse files
Merge pull request softlayer#1091 from allmightyspiff/issues1069
softlayer#1069 - Virtual Placement Group support
2 parents 27c9c6f + ed7b636 commit 2bbbab1

26 files changed

+1072
-18
lines changed

CONTRIBUTING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,15 @@ Code is tested and style checked with tox, you can run the tox tests individuall
2525
* create pull request
2626

2727

28+
## Documentation
29+
30+
CLI command should have a more human readable style of documentation.
31+
Manager methods should have a decent docblock describing any parameters and what the method does.
32+
33+
Docs are generated with [Sphinx](https://docs.readthedocs.io/en/latest/intro/getting-started-with-sphinx.html) and once Sphinx is setup, you can simply do
34+
35+
`make html` in the softlayer-python/docs directory, which should generate the HTML in softlayer-python/docs/_build/html for testing.
36+
37+
2838

2939

Makefile

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Makefile for Sphinx documentation
2+
#
3+
4+
# You can set these variables from the command line.
5+
SPHINXOPTS =
6+
SPHINXBUILD = sphinx-build
7+
PAPER =
8+
BUILDDIR = build
9+
10+
# User-friendly check for sphinx-build
11+
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12+
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13+
endif
14+
15+
# Internal variables.
16+
PAPEROPT_a4 = -D latex_paper_size=a4
17+
PAPEROPT_letter = -D latex_paper_size=letter
18+
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
19+
# the i18n builder cannot share the environment and doctrees with the others
20+
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
21+
22+
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
23+
24+
help:
25+
@echo "Please use \`make <target>' where <target> is one of"
26+
@echo " html to make standalone HTML files"
27+
@echo " dirhtml to make HTML files named index.html in directories"
28+
@echo " singlehtml to make a single large HTML file"
29+
@echo " pickle to make pickle files"
30+
@echo " json to make JSON files"
31+
@echo " htmlhelp to make HTML files and a HTML help project"
32+
@echo " qthelp to make HTML files and a qthelp project"
33+
@echo " applehelp to make an Apple Help Book"
34+
@echo " devhelp to make HTML files and a Devhelp project"
35+
@echo " epub to make an epub"
36+
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
37+
@echo " latexpdf to make LaTeX files and run them through pdflatex"
38+
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
39+
@echo " text to make text files"
40+
@echo " man to make manual pages"
41+
@echo " texinfo to make Texinfo files"
42+
@echo " info to make Texinfo files and run them through makeinfo"
43+
@echo " gettext to make PO message catalogs"
44+
@echo " changes to make an overview of all changed/added/deprecated items"
45+
@echo " xml to make Docutils-native XML files"
46+
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
47+
@echo " linkcheck to check all external links for integrity"
48+
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
49+
@echo " coverage to run coverage check of the documentation (if enabled)"
50+
51+
clean:
52+
rm -rf $(BUILDDIR)/*
53+
54+
html:
55+
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
56+
@echo
57+
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
58+
59+
dirhtml:
60+
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
61+
@echo
62+
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
63+
64+
singlehtml:
65+
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
66+
@echo
67+
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
68+
69+
pickle:
70+
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
71+
@echo
72+
@echo "Build finished; now you can process the pickle files."
73+
74+
json:
75+
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
76+
@echo
77+
@echo "Build finished; now you can process the JSON files."
78+
79+
htmlhelp:
80+
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
81+
@echo
82+
@echo "Build finished; now you can run HTML Help Workshop with the" \
83+
".hhp project file in $(BUILDDIR)/htmlhelp."
84+
85+
qthelp:
86+
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
87+
@echo
88+
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
89+
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
90+
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/softlayer-python.qhcp"
91+
@echo "To view the help file:"
92+
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/softlayer-python.qhc"
93+
94+
applehelp:
95+
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
96+
@echo
97+
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
98+
@echo "N.B. You won't be able to view it unless you put it in" \
99+
"~/Library/Documentation/Help or install it in your application" \
100+
"bundle."
101+
102+
devhelp:
103+
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
104+
@echo
105+
@echo "Build finished."
106+
@echo "To view the help file:"
107+
@echo "# mkdir -p $$HOME/.local/share/devhelp/softlayer-python"
108+
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/softlayer-python"
109+
@echo "# devhelp"
110+
111+
epub:
112+
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
113+
@echo
114+
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
115+
116+
latex:
117+
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
118+
@echo
119+
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
120+
@echo "Run \`make' in that directory to run these through (pdf)latex" \
121+
"(use \`make latexpdf' here to do that automatically)."
122+
123+
latexpdf:
124+
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
125+
@echo "Running LaTeX files through pdflatex..."
126+
$(MAKE) -C $(BUILDDIR)/latex all-pdf
127+
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
128+
129+
latexpdfja:
130+
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
131+
@echo "Running LaTeX files through platex and dvipdfmx..."
132+
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
133+
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
134+
135+
text:
136+
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
137+
@echo
138+
@echo "Build finished. The text files are in $(BUILDDIR)/text."
139+
140+
man:
141+
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
142+
@echo
143+
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
144+
145+
texinfo:
146+
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
147+
@echo
148+
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
149+
@echo "Run \`make' in that directory to run these through makeinfo" \
150+
"(use \`make info' here to do that automatically)."
151+
152+
info:
153+
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
154+
@echo "Running Texinfo files through makeinfo..."
155+
make -C $(BUILDDIR)/texinfo info
156+
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
157+
158+
gettext:
159+
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
160+
@echo
161+
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
162+
163+
changes:
164+
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
165+
@echo
166+
@echo "The overview file is in $(BUILDDIR)/changes."
167+
168+
linkcheck:
169+
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
170+
@echo
171+
@echo "Link check complete; look for any errors in the above output " \
172+
"or in $(BUILDDIR)/linkcheck/output.txt."
173+
174+
doctest:
175+
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
176+
@echo "Testing of doctests in the sources finished, look at the " \
177+
"results in $(BUILDDIR)/doctest/output.txt."
178+
179+
coverage:
180+
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
181+
@echo "Testing of coverage in the sources finished, look at the " \
182+
"results in $(BUILDDIR)/coverage/python.txt."
183+
184+
xml:
185+
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
186+
@echo
187+
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
188+
189+
pseudoxml:
190+
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
191+
@echo
192+
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ To get the exact API call that this library makes, you can do the following.
8888
For the CLI, just use the -vvv option. If you are using the REST endpoint, this will print out a curl command that you can use, if using XML, this will print the minimal python code to make the request without the softlayer library.
8989

9090
.. code-block:: bash
91+
9192
$ slcli -vvv vs list
9293
9394
9495
If you are using the library directly in python, you can do something like this.
9596

9697
.. code-bock:: python
98+
9799
import SoftLayer
98100
import logging
99101

@@ -118,6 +120,8 @@ If you are using the library directly in python, you can do something like this.
118120
main.main()
119121
main.debug()
120122

123+
124+
121125
System Requirements
122126
-------------------
123127
* Python 2.7, 3.3, 3.4, 3.5, 3.6, or 3.7.

SoftLayer/CLI/helpers.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,20 @@ def multi_option(*param_decls, **attrs):
3030
def resolve_id(resolver, identifier, name='object'):
3131
"""Resolves a single id using a resolver function.
3232
33-
:param resolver: function that resolves ids. Should return None or a list
34-
of ids.
33+
:param resolver: function that resolves ids. Should return None or a list of ids.
3534
:param string identifier: a string identifier used to resolve ids
3635
:param string name: the object type, to be used in error messages
3736
3837
"""
38+
try:
39+
return int(identifier)
40+
except ValueError:
41+
pass # It was worth a shot
42+
3943
ids = resolver(identifier)
4044

4145
if len(ids) == 0:
42-
raise exceptions.CLIAbort("Error: Unable to find %s '%s'"
43-
% (name, identifier))
46+
raise exceptions.CLIAbort("Error: Unable to find %s '%s'" % (name, identifier))
4447

4548
if len(ids) > 1:
4649
raise exceptions.CLIAbort(

SoftLayer/CLI/routes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
('virtual:upgrade', 'SoftLayer.CLI.virt.upgrade:cli'),
3232
('virtual:credentials', 'SoftLayer.CLI.virt.credentials:cli'),
3333
('virtual:capacity', 'SoftLayer.CLI.virt.capacity:cli'),
34+
('virtual:placementgroup', 'SoftLayer.CLI.virt.placementgroup:cli'),
3435

3536
('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'),
3637
('dedicatedhost:list', 'SoftLayer.CLI.dedicatedhost.list:cli'),
@@ -317,4 +318,5 @@
317318
'vm': 'virtual',
318319
'vs': 'virtual',
319320
'dh': 'dedicatedhost',
321+
'pg': 'placementgroup',
320322
}

SoftLayer/CLI/virt/create.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def _update_with_like_args(ctx, _, value):
3232
'postinstall': like_details.get('postInstallScriptUri'),
3333
'dedicated': like_details['dedicatedAccountHostOnlyFlag'],
3434
'private': like_details['privateNetworkOnlyFlag'],
35+
'placement_id': like_details.get('placementGroupId', None),
3536
}
3637

3738
like_args['flavor'] = utils.lookup(like_details,
@@ -138,6 +139,10 @@ def _parse_create_args(client, args):
138139
if args.get('host_id'):
139140
data['host_id'] = args['host_id']
140141

142+
if args.get('placementgroup'):
143+
resolver = SoftLayer.managers.PlacementManager(client).resolve_ids
144+
data['placement_id'] = helpers.resolve_id(resolver, args.get('placementgroup'), 'PlacementGroup')
145+
141146
return data
142147

143148

@@ -190,6 +195,8 @@ def _parse_create_args(client, args):
190195
help=('Security group ID to associate with the private interface'))
191196
@click.option('--wait', type=click.INT,
192197
help="Wait until VS is finished provisioning for up to X seconds before returning")
198+
@click.option('--placementgroup',
199+
help="Placement Group name or Id to order this guest on. See: slcli vs placementgroup list")
193200
@click.option('--ipv6', is_flag=True, help="Adds an IPv6 address to this guest")
194201
@environment.pass_env
195202
def cli(env, **args):
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Manages Reserved Capacity."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import importlib
5+
import os
6+
7+
import click
8+
9+
CONTEXT = {'help_option_names': ['-h', '--help'],
10+
'max_content_width': 999}
11+
12+
13+
class PlacementGroupCommands(click.MultiCommand):
14+
"""Loads module for placement group related commands.
15+
16+
Currently the base command loader only supports going two commands deep.
17+
So this small loader is required for going that third level.
18+
"""
19+
20+
def __init__(self, **attrs):
21+
click.MultiCommand.__init__(self, **attrs)
22+
self.path = os.path.dirname(__file__)
23+
24+
def list_commands(self, ctx):
25+
"""List all sub-commands."""
26+
commands = []
27+
for filename in os.listdir(self.path):
28+
if filename == '__init__.py':
29+
continue
30+
if filename.endswith('.py'):
31+
commands.append(filename[:-3].replace("_", "-"))
32+
commands.sort()
33+
return commands
34+
35+
def get_command(self, ctx, cmd_name):
36+
"""Get command for click."""
37+
path = "%s.%s" % (__name__, cmd_name)
38+
path = path.replace("-", "_")
39+
module = importlib.import_module(path)
40+
return getattr(module, 'cli')
41+
42+
43+
# Required to get the sub-sub-sub command to work.
44+
@click.group(cls=PlacementGroupCommands, context_settings=CONTEXT)
45+
def cli():
46+
"""Base command for all capacity related concerns"""
47+
pass
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Create a placement group"""
2+
3+
import click
4+
5+
from SoftLayer.CLI import environment
6+
from SoftLayer.CLI import helpers
7+
from SoftLayer.managers.vs_placement import PlacementManager as PlacementManager
8+
9+
10+
@click.command()
11+
@click.option('--name', type=click.STRING, required=True, prompt=True, help="Name for this new placement group.")
12+
@click.option('--backend_router', '-b', required=True, prompt=True,
13+
help="backendRouter, can be either the hostname or id.")
14+
@click.option('--rule', '-r', required=True, prompt=True,
15+
help="The keyName or Id of the rule to govern this placement group.")
16+
@environment.pass_env
17+
def cli(env, **args):
18+
"""Create a placement group"""
19+
manager = PlacementManager(env.client)
20+
backend_router_id = helpers.resolve_id(manager.get_backend_router_id_from_hostname,
21+
args.get('backend_router'),
22+
'backendRouter')
23+
rule_id = helpers.resolve_id(manager.get_rule_id_from_name, args.get('rule'), 'Rule')
24+
placement_object = {
25+
'name': args.get('name'),
26+
'backendRouterId': backend_router_id,
27+
'ruleId': rule_id
28+
}
29+
30+
result = manager.create(placement_object)
31+
click.secho("Successfully created placement group: ID: %s, Name: %s" % (result['id'], result['name']), fg='green')

0 commit comments

Comments
 (0)