Skip to content

Commit 64ce630

Browse files
joechainzJoey Gibson
andauthored
category manage (#193)
* category manage * pre commit Co-authored-by: Joey Gibson <jgibson@saltstack.com>
1 parent 969a0fd commit 64ce630

6 files changed

Lines changed: 363 additions & 18 deletions

File tree

src/saltext/vmware/modules/datastore.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,49 +25,49 @@ def __virtual__():
2525
return __virtualname__
2626

2727

28-
def maintenance_mode(datastore_name, dc_name=None, service_instance=None):
28+
def maintenance_mode(datastore_name, datacenter_name=None, service_instance=None):
2929
"""
3030
Put datastore in maintenance mode.
3131
3232
datastore_name
3333
Name of datastore.
3434
35-
dc_name
36-
Name of datacenter where folder will be created.
35+
datacenter_name
36+
(optional) Name of datacenter where datastore exists.
3737
3838
service_instance
3939
(optional) The Service Instance from which to obtain managed object references.
4040
"""
4141
if service_instance is None:
4242
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
4343
dc_ref = None
44-
if dc_name:
45-
dc_ref = utils_common.get_datacenter(service_instance, dc_name)
44+
if datacenter_name:
45+
dc_ref = utils_common.get_datacenter(service_instance, datacenter_name)
4646
ds = utils_common.get_datastore(datastore_name, dc_ref, service_instance)
4747
ret = utils_common.datastore_enter_maintenance_mode(ds)
4848
if ret:
4949
return {"maintenanceMode": "inMaintenance"}
5050
return {"maintenanceMode": "failed to enter maintenance mode"}
5151

5252

53-
def exit_maintenance_mode(datastore_name, dc_name=None, service_instance=None):
53+
def exit_maintenance_mode(datastore_name, datacenter_name=None, service_instance=None):
5454
"""
5555
Take datastore out of maintenance mode.
5656
5757
datastore_name
5858
Name of datastore.
5959
60-
dc_name
61-
Name of datacenter where folder will be created.
60+
datacenter_name
61+
(optional) Name of datacenter where datastore exists.
6262
6363
service_instance
6464
(optional) The Service Instance from which to obtain managed object references.
6565
"""
6666
if service_instance is None:
6767
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
6868
dc_ref = None
69-
if dc_name:
70-
dc_ref = utils_common.get_datacenter(service_instance, dc_name)
69+
if datacenter_name:
70+
dc_ref = utils_common.get_datacenter(service_instance, datacenter_name)
7171
ds = utils_common.get_datastore(datastore_name, dc_ref, service_instance)
7272
ret = utils_common.datastore_exit_maintenance_mode(ds)
7373
if ret:

src/saltext/vmware/modules/tag.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,93 @@ def get_category(category_id):
142142
response = connect.request(url, "GET", opts=__opts__, pillar=__pillar__)
143143
response = response["response"].json()
144144
return {"category": response["value"]}
145+
146+
147+
def create_category(category_name, associable_types, cardinality, description=""):
148+
"""
149+
Create a new category.
150+
151+
category_name
152+
The display name of the category.
153+
154+
associable_types
155+
(list) Object types to which this category’s tags can be attached.
156+
157+
cardinality
158+
The CategoryModel.Cardinality enumerated type defines the number of tags in a category that can be assigned to an object. SINGLE, MULTIPLE
159+
160+
description
161+
(optional) The description of the category.
162+
"""
163+
data = {
164+
"create_spec": {
165+
"associable_types": associable_types,
166+
"cardinality": cardinality,
167+
"description": description,
168+
"name": category_name,
169+
}
170+
}
171+
response = connect.request(
172+
"/rest/com/vmware/cis/tagging/category", "POST", body=data, opts=__opts__, pillar=__pillar__
173+
)
174+
response = response["response"].json()
175+
return {"category": response["value"]}
176+
177+
178+
def update_category(
179+
category_id, category_name=None, associable_types=None, cardinality=None, description=""
180+
):
181+
"""
182+
Update a new category.
183+
184+
category_id
185+
The identifier of the category to be updated. The parameter must be an identifier for the resource type: com.vmware.cis.tagging.Category.
186+
187+
category_name
188+
The display name of the category.
189+
190+
associable_types
191+
(list) Object types to which this categorys tags can be attached.
192+
193+
cardinality
194+
The CategoryModel.Cardinality enumerated type defines the number of tags in a category that can be assigned to an object. SINGLE, MULTIPLE
195+
196+
description
197+
(optional) The description of the category.
198+
"""
199+
spec = {"update_spec": {}}
200+
if category_name:
201+
spec["update_spec"]["name"] = category_name
202+
if associable_types:
203+
spec["update_spec"]["associable_types"] = associable_types
204+
if cardinality:
205+
spec["update_spec"]["cardinality"] = cardinality
206+
if description:
207+
spec["update_spec"]["description"] = description
208+
url = f"/rest/com/vmware/cis/tagging/category/id:{category_id}"
209+
response = connect.request(url, "PATCH", body=spec, opts=__opts__, pillar=__pillar__)
210+
if response["response"].status_code == 200:
211+
return {"category": "updated"}
212+
return {
213+
"category": "failed to update",
214+
"status_code": response["response"].status_code,
215+
"reason": response["response"].reason,
216+
}
217+
218+
219+
def delete_category(category_id):
220+
"""
221+
Delete given category.
222+
223+
category_id
224+
The identifier of category to be deleted. The parameter must be an identifier for the resource type: com.vmware.cis.tagging.Category.
225+
"""
226+
url = f"/rest/com/vmware/cis/tagging/category/id:{category_id}"
227+
response = connect.request(url, "DELETE", opts=__opts__, pillar=__pillar__)
228+
if response["response"].status_code == 200:
229+
return {"category": "deleted"}
230+
return {
231+
"category": "failed to update",
232+
"status_code": response.status_code,
233+
"reason": response.reason,
234+
}

src/saltext/vmware/states/tag.py

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __virtual__():
2828

2929
def present(name, description=None, category_id=None):
3030
"""
31-
Create and update a tag instance.
31+
Create or update a tag instance.
3232
3333
name
3434
Name of tag.
@@ -151,3 +151,143 @@ def absent(name):
151151
else:
152152
ret["comment"] = "Tag does not exist"
153153
return ret
154+
155+
156+
def present_category(name, associable_types, cardinality, description=""):
157+
"""
158+
Create or update a category.
159+
160+
name
161+
The display name of the category.
162+
163+
associable_types
164+
(list) Object types to which this category’s tags can be attached.
165+
166+
cardinality
167+
The CategoryModel.Cardinality enumerated type defines the number of tags in a category that can be assigned to an object. SINGLE, MULTIPLE
168+
169+
description
170+
(optional) The description of the category.
171+
"""
172+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
173+
res = connect.request(
174+
"/rest/com/vmware/cis/tagging/category", "GET", opts=__opts__, pillar=__pillar__
175+
)
176+
response = res["response"].json()
177+
token = res["token"]
178+
found = None
179+
for cat in response["value"]:
180+
url = f"/rest/com/vmware/cis/tagging/category/id:{cat}"
181+
cat_ref = connect.request(url, "GET", token=token, opts=__opts__, pillar=__pillar__)
182+
cat_ref = cat_ref["response"].json()
183+
if cat_ref["value"]["name"] == name:
184+
found = cat_ref["value"]
185+
break
186+
if found:
187+
if (
188+
associable_types == found["associable_types"]
189+
and cardinality == found["cardinality"]
190+
and description == found["description"]
191+
):
192+
ret["comment"] = "category exists"
193+
return ret
194+
else:
195+
ret["changes"]["new"] = {}
196+
ret["changes"]["old"] = {}
197+
if associable_types != found["associable_types"]:
198+
ret["changes"]["old"]["associable_types"] = found["associable_types"]
199+
ret["changes"]["new"]["associable_types"] = associable_types
200+
if cardinality != found["cardinality"]:
201+
ret["changes"]["old"]["cardinality"] = found["cardinality"]
202+
ret["changes"]["new"]["cardinality"] = cardinality
203+
if description != found["description"]:
204+
ret["changes"]["old"]["description"] = found["description"]
205+
ret["changes"]["new"]["description"] = description
206+
if __opts__["test"]:
207+
ret["result"] = None
208+
ret["comment"] = f"{name} category will be updated"
209+
return ret
210+
id = found["id"]
211+
spec = {"update_spec": {}}
212+
if associable_types:
213+
spec["update_spec"]["associable_types"] = associable_types
214+
if cardinality:
215+
spec["update_spec"]["cardinality"] = cardinality
216+
if description:
217+
spec["update_spec"]["description"] = description
218+
url = f"/rest/com/vmware/cis/tagging/category/id:{id}"
219+
updated = connect.request(url, "PATCH", body=spec, opts=__opts__, pillar=__pillar__)
220+
if updated["response"].status_code == 200:
221+
ret["comment"] = "updated"
222+
return ret
223+
ret["status_code"] = updated["response"].status_code
224+
ret["reason"] = updated["response"].reason
225+
ret["comment"] = "failed to update"
226+
ret["result"] = False
227+
return ret
228+
else:
229+
if __opts__["test"]:
230+
ret["result"] = None
231+
ret["comment"] = f"{name} category will be created"
232+
return ret
233+
data = {
234+
"create_spec": {
235+
"associable_types": associable_types,
236+
"cardinality": cardinality,
237+
"description": description,
238+
"name": name,
239+
}
240+
}
241+
create = connect.request(
242+
"/rest/com/vmware/cis/tagging/category",
243+
"POST",
244+
body=data,
245+
opts=__opts__,
246+
pillar=__pillar__,
247+
)
248+
response = create["response"].json()
249+
ret["changes"]["category_id"] = response["value"]
250+
ret["comment"] = "created"
251+
return ret
252+
253+
254+
def absent_category(name):
255+
"""
256+
Delete category.
257+
258+
name
259+
Name of category.
260+
"""
261+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
262+
res = connect.request(
263+
"/rest/com/vmware/cis/tagging/category", "GET", opts=__opts__, pillar=__pillar__
264+
)
265+
response = res["response"].json()
266+
token = res["token"]
267+
found = None
268+
for cat in response["value"]:
269+
url = f"/rest/com/vmware/cis/tagging/category/id:{cat}"
270+
cat_ref = connect.request(url, "GET", token=token, opts=__opts__, pillar=__pillar__)
271+
cat_ref = cat_ref["response"].json()
272+
if cat_ref["value"]["name"] == name:
273+
found = cat_ref["value"]
274+
break
275+
if found:
276+
if __opts__["test"]:
277+
ret["result"] = None
278+
ret["comment"] = f"{name} category will be deleted"
279+
return ret
280+
id = found["id"]
281+
url = f"/rest/com/vmware/cis/tagging/category/id:{id}"
282+
delete = connect.request(url, "DELETE", opts=__opts__, pillar=__pillar__)
283+
if delete["response"].status_code == 200:
284+
ret["comment"] = "deleted"
285+
return ret
286+
ret["status_code"] = delete["response"].status_code
287+
ret["reason"] = delete["response"].reason
288+
ret["comment"] = "failed to delete"
289+
ret["result"] = False
290+
return ret
291+
else:
292+
ret["comment"] = "Category does not exist"
293+
return ret

tests/integration/conftest.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,29 @@ def patch_salt_globals_tag_state(vmware_conf):
278278
Patch __opts__ and __pillar__
279279
"""
280280

281-
setattr(tagging_state, "__opts__", {})
281+
setattr(
282+
tagging_state,
283+
"__opts__",
284+
{
285+
"test": False,
286+
},
287+
)
288+
setattr(tagging_state, "__pillar__", vmware_conf)
289+
290+
291+
@pytest.fixture
292+
def patch_salt_globals_tag_state_test(vmware_conf):
293+
"""
294+
Patch __opts__ and __pillar__
295+
"""
296+
297+
setattr(
298+
tagging_state,
299+
"__opts__",
300+
{
301+
"test": True,
302+
},
303+
)
282304
setattr(tagging_state, "__pillar__", vmware_conf)
283305

284306

tests/integration/modules/test_tag.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,47 @@ def test_tag_delete(patch_salt_globals_tag):
9292
break
9393
else:
9494
pytest.skip("test requires at least one tag")
95+
96+
97+
def test_cat_create(patch_salt_globals_tag):
98+
"""
99+
Test create category functionality
100+
"""
101+
res = tagging.create_category("test cat", ["string"], "SINGLE", "test category")
102+
assert "urn:vmomi:InventoryServiceCategory:" in res["category"]
103+
104+
105+
def test_cat_update(patch_salt_globals_tag):
106+
"""
107+
Test update category functionality
108+
"""
109+
cats = tagging.list_category()
110+
if len(cats["categories"]) > 0:
111+
for cat in cats["categories"]:
112+
res = tagging.get_category(cat)
113+
if res["category"]["name"] == "test cat":
114+
update_res = tagging.update_category(
115+
res["category"]["id"],
116+
res["category"]["name"],
117+
res["category"]["associable_types"],
118+
res["category"]["cardinality"],
119+
"new description",
120+
)
121+
assert "updated" in update_res["category"]
122+
else:
123+
pytest.skip("test requires at least one category")
124+
125+
126+
def test_cat_delete(patch_salt_globals_tag):
127+
"""
128+
Test update category functionality
129+
"""
130+
cats = tagging.list_category()
131+
if len(cats["categories"]) > 0:
132+
for cat in cats["categories"]:
133+
res = tagging.get_category(cat)
134+
if res["category"]["name"] == "test cat":
135+
delete_res = tagging.delete_category(res["category"]["id"])
136+
assert "deleted" in delete_res["category"]
137+
else:
138+
pytest.skip("test requires at least one category")

0 commit comments

Comments
 (0)