Skip to content

Commit efe7af0

Browse files
committed
Add models_search and devices_by_name methods
1 parent 1223237 commit efe7af0

File tree

3 files changed

+102
-23
lines changed

3 files changed

+102
-23
lines changed

.gitignore

100644100755
+3
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ venv.bak/
102102

103103
# mypy
104104
.mypy_cache/
105+
106+
# Editors
107+
.vscode

setup.py

100644100755
+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name='spectrum-client',
9-
version='0.0.8',
9+
version='0.0.9',
1010
description='CA Spectrum Web Services API wrapper',
1111
long_description=README,
1212
long_description_content_type='text/markdown',

spectrum_client/spectrum.py

100644100755
+98-22
Original file line numberDiff line numberDiff line change
@@ -22,57 +22,137 @@ class SpectrumClientParameterError(SpectrumClientException):
2222

2323

2424
class Spectrum(object):
25-
26-
_namespace = {'ca': 'http://www.ca.com/spectrum/restful/schema/response'}
25+
xml_namespace = {'ca': 'http://www.ca.com/spectrum/restful/schema/response'}
26+
default_attributes = '''
27+
<rs:requested-attribute id="0x1006e"/> <!-- Model Name -->
28+
<rs:requested-attribute id="0x1000a"/> <!-- Condition -->
29+
<rs:requested-attribute id="0x11ee8"/> <!-- Model Class -->
30+
<rs:requested-attribute id="0x129e7"/> <!-- Site ID -->
31+
<rs:requested-attribute id="0x12d7f"/> <!-- IP Address -->
32+
<rs:requested-attribute id="0x1290c"/> <!-- Criticality -->
33+
<rs:requested-attribute id="0x10000"/> <!-- Model Type Name -->
34+
<rs:requested-attribute id="0x23000e"/> <!-- Device Type -->
35+
<rs:requested-attribute id="0x11d42"/> <!-- Landscape Name-->
36+
'''
37+
models_search_template = '''<?xml version="1.0" encoding="UTF-8"?>
38+
<rs:model-request throttlesize="9999"
39+
xmlns:rs="http://www.ca.com/spectrum/restful/schema/request"
40+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
41+
xsi:schemaLocation="http://www.ca.com/spectrum/restful/schema/request ../../../xsd/Request.xsd ">
42+
<rs:target-models>
43+
<rs:models-search>
44+
<rs:search-criteria xmlns="http://www.ca.com/spectrum/restful/schema/filter">
45+
<filtered-models>
46+
{models_filter}
47+
</filtered-models>
48+
</rs:search-criteria>
49+
</rs:models-search>
50+
</rs:target-models>
51+
''' + default_attributes + '</rs:model-request>'
2752

2853
def __init__(self, url=SPECTRUM_URL, username=SPECTRUM_USERNAME, password=SPECTRUM_PASSWORD):
2954
if url is None:
3055
raise ValueError('Spectrum (OneClick) url must be provided either in the constructor or as an environment variable')
3156
self.url = url if not url.endswith('/') else url[:-1]
3257
self.auth = HTTPBasicAuth(username, password)
3358

34-
def _check_http(self, res):
35-
if res.status_code == 401:
36-
raise SpectrumClientAuthException('Authorization Failure. Invalid user name or password.')
37-
res.raise_for_status()
38-
3959
def _parse_get(self, res):
40-
self._check_http(res)
60+
self.check_http_response(res)
4161

4262
root = ET.fromstring(res.text)
43-
model_error = root.find('.//ca:model', self._namespace).get('error')
63+
model_error = root.find('.//ca:model', self.xml_namespace).get('error')
4464
if model_error:
4565
raise SpectrumClientParameterError('Model Error: ' + model_error)
46-
attr_error = root.find('.//ca:attribute', self._namespace).get('error')
66+
attr_error = root.find('.//ca:attribute', self.xml_namespace).get('error')
4767
if attr_error:
4868
raise SpectrumClientParameterError(attr_error)
4969

5070
def _parse_update(self, res):
51-
self._check_http(res)
71+
self.check_http_response(res)
5272

5373
root = ET.fromstring(res.text)
54-
if root.find('.//ca:model', self._namespace).get('error') == 'Success':
74+
if root.find('.//ca:model', self.xml_namespace).get('error') == 'Success':
5575
return
5676

57-
if root.find('.//ca:model', self._namespace).get('error') == 'PartialFailure':
58-
msg = root.find('.//ca:attribute', self._namespace).get('error-message')
77+
if root.find('.//ca:model', self.xml_namespace).get('error') == 'PartialFailure':
78+
msg = root.find('.//ca:attribute', self.xml_namespace).get('error-message')
5979
else:
60-
msg = root.find('.//ca:model', self._namespace).get('error-message')
80+
msg = root.find('.//ca:model', self.xml_namespace).get('error-message')
6181
raise SpectrumClientParameterError(msg)
6282

83+
@staticmethod
84+
def check_http_response(res):
85+
if res.status_code == 401:
86+
raise SpectrumClientAuthException('Authorization Failure. Invalid user name or password.')
87+
res.raise_for_status()
88+
89+
@staticmethod
90+
def xml_landscape_filter(landscape):
91+
xml = '''
92+
<greater-than>
93+
<attribute id="0x129fa">
94+
<value>{}</value>
95+
</attribute>
96+
</greater-than>
97+
<less-than>
98+
<attribute id="0x129fa">
99+
<value>{}</value>
100+
</attribute>
101+
</less-than>'''
102+
landscape_start = hex(landscape)
103+
landscape_end = hex(landscape + 0xfffff)
104+
return xml.format(landscape_start, landscape_end).strip()
105+
63106
def get_attribute(self, model_handle, attr_id):
64107
"""Get an attribute of a Spectrum model.
65108
66109
Arguments:
67110
model_handle {int} -- Model Handle of the model being queried.
68111
attr_id {int} -- Attribute ID of the attribute being queried.
69112
"""
70-
url = f'{self.url}/spectrum/restful/model/{hex(model_handle)}'
113+
url = '{}/spectrum/restful/model/{}'.format(self.url, hex(model_handle))
71114
params = {'attr': hex(attr_id)}
72115
res = requests.get(url, params=params, auth=self.auth)
73116
self._parse_get(res)
74117
root = ET.fromstring(res.text)
75-
return root.find('.//ca:attribute', self._namespace).text
118+
return root.find('.//ca:attribute', self.xml_namespace).text
119+
120+
def devices_by_name(self, regex, landscape=None):
121+
if landscape:
122+
landscape_filter = self.xml_landscape_filter(landscape)
123+
else:
124+
landscape_filter = ''
125+
126+
models_filter = '''
127+
<and>
128+
<is-derived-from>
129+
<model-type>0x1004b</model-type> <!-- Device -->
130+
</is-derived-from>
131+
{landscape_filter}
132+
<has-pcre>
133+
<attribute id="0x1006e">
134+
<value>{regex}</value>
135+
</attribute>
136+
</has-pcre>
137+
</and>'''.format(regex=regex, landscape_filter=landscape_filter)
138+
xml = self.models_search_template.format(models_filter=models_filter)
139+
root = ET.fromstring(self.search_models(xml))
140+
models = root.findall('.//ca:model', self.xml_namespace)
141+
devices = {
142+
model.get('mh'): {
143+
attr.get('id'): attr.text for attr in model.getchildren()
144+
} for model in models
145+
}
146+
return devices
147+
148+
def search_models(self, xml):
149+
url = '{}/spectrum/restful/models'.format(self.url)
150+
res = requests.post(url, xml, auth=self.auth)
151+
self.check_http_response(res)
152+
return res.text
153+
154+
def set_maintenance(self, model_handle, on=True):
155+
return self.update_attribute(model_handle, 0x1295d, str(not on))
76156

77157
def update_attribute(self, model_handle, attr_id, value):
78158
"""Update an attribute of a Spectrum model.
@@ -82,11 +162,7 @@ def update_attribute(self, model_handle, attr_id, value):
82162
attr_id {int} -- Attribute ID of the attribute being updated.
83163
value {int or str} -- Value to set the attribute to.
84164
"""
85-
86-
url = f'{self.url}/spectrum/restful/model/{hex(model_handle)}'
165+
url = '{}/spectrum/restful/model/{}'.format(self.url, hex(model_handle))
87166
params = {'attr': hex(attr_id), 'val': value}
88167
res = requests.put(url, params=params, auth=self.auth)
89168
self._parse_update(res)
90-
91-
def set_maintenance(self, model_handle, on=True):
92-
return self.update_attribute(model_handle, 0x1295d, str(not on))

0 commit comments

Comments
 (0)