Skip to content

Commit 9837537

Browse files
authored
Merge pull request #25 from Stackmasters/inventory-commands
Implement inventory commands
2 parents 2612106 + 82e2e0f commit 9837537

File tree

5 files changed

+434
-0
lines changed

5 files changed

+434
-0
lines changed

cycleops/__main__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import typer
44

55
from .client import cycleops_client
6+
from .environments import app as environments_app
7+
from .hostgroups import app as hostgroups_app
8+
from .hosts import app as hosts_app
69
from .services import app as services_app
710
from .setups import app as setups_app
811
from .stacks import app as stacks_app
@@ -14,6 +17,11 @@
1417
cycleops.add_typer(stacks_app, name="stacks", help="Manage your stacks.")
1518
cycleops.add_typer(setups_app, name="setups", help="Manage your setups.")
1619
cycleops.add_typer(units_app, name="units", help="List all of the available units.")
20+
cycleops.add_typer(
21+
environments_app, name="environments", help="Manage your environments."
22+
)
23+
cycleops.add_typer(hosts_app, name="hosts", help="Manage your hosts.")
24+
cycleops.add_typer(hostgroups_app, name="hostgroups", help="Manage your hostgroups.")
1725

1826

1927
@cycleops.callback()

cycleops/client.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,75 @@ def delete(self, stack_id: int) -> Optional[Dict[str, Any]]:
192192
return self.client._request("DELETE", f"stacks/{stack_id}")
193193

194194

195+
class EnvironmentClient(SubClient):
196+
"""
197+
Client for managing Cycleops environments.
198+
"""
199+
200+
def list(self) -> Optional[Dict[str, Any]]:
201+
return self.client._request("GET", f"environments")
202+
203+
204+
class HostClient(SubClient):
205+
"""
206+
Client for managing Cycleops hosts.
207+
"""
208+
209+
def list(self) -> Optional[Dict[str, Any]]:
210+
return self.client._request("GET", f"hosts")
211+
212+
def retrieve(
213+
self, host_id: Optional[int] = None, params: Optional[Dict[str, Any]] = None
214+
) -> Optional[Dict[str, Any]]:
215+
if host_id:
216+
return self.client._request("GET", f"hosts/{host_id}")
217+
218+
return self.client._request("GET", f"hosts", params=params)
219+
220+
def create(self, **kwargs: Any) -> Optional[Dict[str, Any]]:
221+
payload: Dict[str, Any] = {k: v for (k, v) in kwargs.items() if v}
222+
223+
return self.client._request("POST", "hosts", payload)
224+
225+
def update(self, host_id: int, **kwargs: Any) -> Optional[Dict[str, Any]]:
226+
payload: Dict[str, Any] = {k: v for (k, v) in kwargs.items() if v}
227+
228+
return self.client._request("PATCH", f"hosts/{host_id}", payload)
229+
230+
def delete(self, host_id: int) -> Optional[Dict[str, Any]]:
231+
return self.client._request("DELETE", f"hosts/{host_id}")
232+
233+
234+
class HostgroupClient(SubClient):
235+
"""
236+
Client for managing Cycleops hostgroups.
237+
"""
238+
239+
def list(self) -> Optional[Dict[str, Any]]:
240+
return self.client._request("GET", f"hostgroups")
241+
242+
def retrieve(
243+
self,
244+
hostgroup_id: Optional[int] = None,
245+
params: Optional[Dict[str, Any]] = None,
246+
) -> Optional[Dict[str, Any]]:
247+
if hostgroup_id:
248+
return self.client._request("GET", f"hostgroups/{hostgroup_id}")
249+
250+
return self.client._request("GET", f"hostgroups", params=params)
251+
252+
def create(self, **kwargs: Any) -> Optional[Dict[str, Any]]:
253+
payload: Dict[str, Any] = {k: v for (k, v) in kwargs.items() if v}
254+
255+
return self.client._request("POST", "hostgroups", payload)
256+
257+
def update(self, hostgroup_id: int, **kwargs: Any) -> Optional[Dict[str, Any]]:
258+
payload: Dict[str, Any] = {k: v for (k, v) in kwargs.items() if v}
259+
260+
return self.client._request("PATCH", f"hostgroups/{hostgroup_id}", payload)
261+
262+
def delete(self, hostgroup_id: int) -> Optional[Dict[str, Any]]:
263+
return self.client._request("DELETE", f"hostgroups/{hostgroup_id}")
264+
265+
195266
cycleops_client: Client = Client()

cycleops/environments.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import typer
2+
from rich import print
3+
from rich.table import Table
4+
5+
from .client import EnvironmentClient, cycleops_client
6+
from .exceptions import NotFound
7+
from .utils import display_error_message
8+
9+
app = typer.Typer()
10+
11+
environment_client: EnvironmentClient = EnvironmentClient(cycleops_client)
12+
13+
14+
@app.command()
15+
def list() -> None:
16+
"""
17+
List all of the available environments.
18+
"""
19+
20+
try:
21+
environments = environment_client.list()
22+
23+
if not environments:
24+
raise NotFound("No environments available")
25+
26+
table = Table(show_header=True, leading=True)
27+
table.add_column("ID", width=5)
28+
table.add_column("Name", width=30)
29+
30+
for environment in environments:
31+
table.add_row(str(environment["id"]), environment["name"])
32+
33+
print(table)
34+
except Exception as error:
35+
display_error_message(error)
36+
raise typer.Exit(code=1)

cycleops/hostgroups.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
from typing import Any, Dict, List, Optional
2+
3+
import typer
4+
from rich import print
5+
6+
from .client import HostgroupClient, cycleops_client
7+
from .exceptions import NotFound
8+
from .utils import display_error_message, display_success_message
9+
10+
app = typer.Typer()
11+
12+
hostgroup_client: HostgroupClient = HostgroupClient(cycleops_client)
13+
14+
15+
@app.command()
16+
def list() -> None:
17+
"""
18+
List all of the available hostgroups.
19+
"""
20+
21+
try:
22+
hostgroups = hostgroup_client.list()
23+
24+
if not hostgroups:
25+
raise NotFound("No hostgroups available")
26+
27+
print(hostgroups)
28+
except Exception as error:
29+
display_error_message(error)
30+
raise typer.Exit(code=1)
31+
32+
33+
@app.command()
34+
def retrieve(
35+
hostgroup_identifier: str = typer.Argument(
36+
..., help="The ID or name of the hostgroup. Names take precedence."
37+
),
38+
) -> None:
39+
"""
40+
Retrieve the hostgroup specified by the given ID.
41+
"""
42+
43+
try:
44+
hostgroup = get_hostgroup(hostgroup_identifier)
45+
46+
print(hostgroup)
47+
except Exception as error:
48+
display_error_message(error)
49+
raise typer.Exit(code=1)
50+
51+
52+
@app.command()
53+
def create(
54+
name: str = typer.Option(
55+
...,
56+
help="The name of the hostgroup.",
57+
),
58+
environment_id: int = typer.Option(None, help="The ID of the environment."),
59+
hosts: Optional[List[int]] = typer.Option(
60+
None,
61+
"--host-id",
62+
help="The ID of the host.",
63+
),
64+
) -> None:
65+
"""
66+
Create a hostgroup with the specified option values.
67+
"""
68+
69+
try:
70+
hostgroup_client.create(
71+
name=name,
72+
environment=environment_id,
73+
hosts=hosts,
74+
)
75+
76+
display_success_message(f"Hostgroup {name} has been created")
77+
except Exception as error:
78+
display_error_message(error)
79+
raise typer.Abort()
80+
81+
82+
@app.command()
83+
def update(
84+
hostgroup_identifier: str = typer.Argument(
85+
..., help="The ID or name of the hostgroup. Names take precedence."
86+
),
87+
name: Optional[str] = typer.Option(
88+
None,
89+
help="The name of the hostgroup.",
90+
),
91+
environment_id: Optional[int] = typer.Option(
92+
None, help="The ID of the environment."
93+
),
94+
hosts: Optional[List[int]] = typer.Option(
95+
None,
96+
"--host-id",
97+
help="The ID of the host.",
98+
),
99+
) -> None:
100+
"""
101+
Update a hostgroup with the specified option values.
102+
"""
103+
104+
try:
105+
hostgroup = get_hostgroup(hostgroup_identifier)
106+
107+
hostgroup_client.update(
108+
hostgroup["id"],
109+
name=name,
110+
environment=environment_id,
111+
hosts=hosts,
112+
)
113+
114+
display_success_message(f"Hostgroup {hostgroup_identifier} has been updated")
115+
except Exception as error:
116+
display_error_message(error)
117+
raise typer.Abort()
118+
119+
120+
@app.command()
121+
def delete(
122+
hostgroup_identifier: str = typer.Argument(
123+
..., help="The ID or name of the hostgroup. Names take precedence."
124+
),
125+
) -> None:
126+
"""
127+
Delete the hostgroup specified by the given name.
128+
"""
129+
130+
try:
131+
hostgroup = get_hostgroup(hostgroup_identifier)
132+
133+
hostgroup_client.delete(hostgroup["id"])
134+
display_success_message(f"Hostgroup {hostgroup_identifier} has been deleted")
135+
except Exception as error:
136+
display_error_message(error)
137+
raise typer.Abort()
138+
139+
140+
def get_hostgroup(hostgroup_identifier: str) -> Optional[Dict[str, Any]]:
141+
"""
142+
Retrieves a hostgroup with either a name or ID. Names take precedence.
143+
"""
144+
145+
hostgroup = hostgroup_client.retrieve(params={"name": hostgroup_identifier})
146+
147+
if len(hostgroup) == 1:
148+
return hostgroup[0]
149+
150+
hostgroup = hostgroup_client.retrieve(hostgroup_identifier)
151+
152+
return hostgroup

0 commit comments

Comments
 (0)