-
Notifications
You must be signed in to change notification settings - Fork 12
/
panorama.go
346 lines (278 loc) · 12.1 KB
/
panorama.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
package panos
import (
"encoding/xml"
"errors"
"fmt"
"strings"
"time"
)
// Devices lists all of the devices in Panorama.
type Devices struct {
XMLName xml.Name `xml:"response"`
Status string `xml:"status,attr"`
Code string `xml:"code,attr"`
Devices []Serial `xml:"result>devices>entry"`
}
// DeviceGroups lists all of the device-group's in Panorama.
type DeviceGroups struct {
XMLName xml.Name `xml:"response"`
Status string `xml:"status,attr"`
Code string `xml:"code,attr"`
DeviceGroup []DeviceGroup `xml:"result>devicegroups>entry"`
}
// DeviceGroup contains information about each individual device-group.
type DeviceGroup struct {
Name string `xml:"name,attr"`
Devices []Serial `xml:"devices>entry"`
}
// Serial contains the information of each device managed by Panorama.
type Serial struct {
Serial string `xml:"name,attr"`
Connected string `xml:"connected"`
UnsupportedVersion string `xml:"unsupported-version"`
Hostname string `xml:"hostname"`
IPAddress string `xml:"ip-address"`
MacAddress string `xml:"mac-addr"`
Uptime string `xml:"uptime"`
Family string `xml:"family"`
Model string `xml:"model"`
SoftwareVersion string `xml:"sw-version"`
AppVersion string `xml:"app-version"`
AntiVirusVersion string `xml:"av-version"`
WildfireVersion string `xml:"wildfire-version"`
ThreatVersion string `xml:"threat-version"`
URLDB string `xml:"url-db"`
URLFilteringVersion string `xml:"url-filtering-version"`
LogDBVersion string `xml:"logdb-version"`
VpnClientPackageVersion string `xml:"vpnclient-package-version"`
GlobalProtectClientPackageVersion string `xml:"global-protect-client-package-version"`
Domain string `xml:"domain"`
HAState string `xml:"ha>state"`
HAPeer string `xml:"ha>peer>serial"`
VpnDisableMode string `xml:"vpn-disable-mode"`
OperationalMode string `xml:"operational-mode"`
CertificateStatus string `xml:"certificate-status"`
CertificateSubjectName string `xml:"certificate-subject-name"`
CertificateExpiry string `xml:"certificate-expiry"`
ConnectedAt string `xml:"connected-at"`
CustomCertificateUsage string `xml:"custom-certificate-usage"`
MultiVsys string `xml:"multi-vsys"`
Vsys []VsysEntry `xml:"vsys>entry"`
}
// VsysEntry contains information about each vsys.
type VsysEntry struct {
Name string `xml:"name,attr"`
DisplayName string `xml:"display-name"`
SharedPolicyStatus string `xml:"shared-policy-status"`
SharedPolicyMD5Sum string `xml:"shared-policy-md5sum"`
}
// SetShared will set Panorama's device-group to shared for all subsequent configuration changes. For example, if you set this
// to "true" and then create address or service objects, they will all be shared objects. Set this back to "false" to return to normal mode.
func (p *PaloAlto) SetShared(shared bool) {
if p.DeviceType == "panos" {
panic(errors.New("you can only set the shared option on a Panorama device"))
}
p.Shared = shared
}
// Devices returns information about all of the devices that are managed by Panorama.
func (p *PaloAlto) Devices() (*Devices, error) {
var devices Devices
if p.DeviceType != "panorama" {
return nil, errors.New("devices can only be listed from a Panorama device")
}
_, devData, errs := r.Get(p.URI).Query(fmt.Sprintf("type=op&cmd=<show><devices><all></all></devices></show>&key=%s", p.Key)).End()
if errs != nil {
return nil, errs[0]
}
if err := xml.Unmarshal([]byte(devData), &devices); err != nil {
return nil, err
}
if devices.Status != "success" {
return nil, fmt.Errorf("error code %s: %s", devices.Code, errorCodes[devices.Code])
}
return &devices, nil
}
// DeviceGroups returns information about all of the device-groups in Panorama, and what devices are
// linked to them, along with detailed information about each device. You can (optionally) specify a specific device-group
// if you wish.
func (p *PaloAlto) DeviceGroups(devicegroup ...string) (*DeviceGroups, error) {
var devices DeviceGroups
// xpath := "/config/devices/entry//device-group"
command := "<show><devicegroups></devicegroups></show>"
if p.DeviceType != "panorama" {
return nil, errors.New("device-groups can only be listed from a Panorama device")
}
if len(devicegroup) > 0 {
command = fmt.Sprintf("<show><devicegroups><name>%s</name></devicegroups></show>", devicegroup[0])
}
// _, devData, errs := r.Get(p.URI).Query(fmt.Sprintf("type=config&action=get&xpath=%s&key=%s", xpath, p.Key)).End()
_, devData, errs := r.Get(p.URI).Query(fmt.Sprintf("type=op&cmd=%s&key=%s", command, p.Key)).End()
if errs != nil {
return nil, errs[0]
}
if err := xml.Unmarshal([]byte(devData), &devices); err != nil {
return nil, err
}
if devices.Status != "success" {
return nil, fmt.Errorf("error code %s: %s", devices.Code, errorCodes[devices.Code])
}
return &devices, nil
}
// CreateDeviceGroup will create a new device-group on a Panorama device. You can add devices as well by
// specifying the serial numbers in a string slice ([]string). Specify "nil" if you do not wish to add any.
func (p *PaloAlto) CreateDeviceGroup(name, description string, devices []string) error {
var xmlBody string
var xpath string
var reqError requestError
if p.DeviceType == "panos" || p.DeviceType != "panorama" {
return errors.New("you must be connected to a Panorama device when creating a device-group")
}
if p.DeviceType == "panorama" {
xpath = "/config/devices/entry[@name='localhost.localdomain']/device-group"
xmlBody = fmt.Sprintf("<entry name=\"%s\">", name)
}
if devices != nil {
xmlBody += "<devices>"
for _, s := range devices {
xmlBody += fmt.Sprintf("<entry name=\"%s\"/>", strings.TrimSpace(s))
}
xmlBody += "</devices>"
}
if description != "" {
xmlBody += fmt.Sprintf("<description>%s</description>", description)
}
xmlBody += "</entry>"
_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
if errs != nil {
return errs[0]
}
if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
return err
}
if reqError.Status != "success" {
return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
}
return nil
}
// DeleteDeviceGroup will delete the given device-group from Panorama.
func (p *PaloAlto) DeleteDeviceGroup(name string) error {
var xpath string
var reqError requestError
if p.DeviceType == "panos" || p.DeviceType != "panorama" {
return errors.New("you must be connected to a Panorama device when deleting a device-group")
}
if p.DeviceType == "panorama" {
xpath = fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/device-group/entry[@name='%s']", name)
}
_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
if errs != nil {
return errs[0]
}
if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
return err
}
if reqError.Status != "success" {
return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
}
return nil
}
// AddDevice will add a new device to a Panorama. If you specify the optional devicegroup parameter,
// it will also add the device to the given device-group.
func (p *PaloAlto) AddDevice(serial string, devicegroup ...string) error {
var reqError requestError
if p.DeviceType == "panos" || p.DeviceType != "panorama" {
return errors.New("you must be connected to Panorama when adding devices")
}
if p.DeviceType == "panorama" && len(devicegroup) <= 0 {
xpath := "/config/mgt-config/devices"
xmlBody := fmt.Sprintf("<entry name=\"%s\"/>", serial)
_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
if errs != nil {
return errs[0]
}
if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
return err
}
if reqError.Status != "success" {
return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
}
}
if p.DeviceType == "panorama" && len(devicegroup) > 0 {
deviceXpath := "/config/mgt-config/devices"
deviceXMLBody := fmt.Sprintf("<entry name=\"%s\"/>", serial)
xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/device-group/entry[@name='%s']", devicegroup[0])
xmlBody := fmt.Sprintf("<devices><entry name=\"%s\"/></devices>", serial)
_, addResp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", deviceXpath, deviceXMLBody, p.Key)).End()
if errs != nil {
return errs[0]
}
if err := xml.Unmarshal([]byte(addResp), &reqError); err != nil {
return err
}
if reqError.Status != "success" {
return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
}
time.Sleep(200 * time.Millisecond)
_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
if errs != nil {
return errs[0]
}
if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
return err
}
if reqError.Status != "success" {
return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
}
}
return nil
}
// SetPanoramaServer will configure a device to be managed by the given Panorama server's primary IP address.
// You can optionally add a second Panorama server by specifying an IP address for the "secondary" parameter.
func (p *PaloAlto) SetPanoramaServer(primary string, secondary ...string) error {
var reqError requestError
xpath := "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system"
xmlBody := fmt.Sprintf("<panorama-server>%s</panorama-server>", primary)
if len(secondary) > 0 {
xmlBody = fmt.Sprintf("<panorama-server>%s</panorama-server><panorama-server-2>%s</panorama-server-2>", primary, secondary[0])
}
if p.DeviceType == "panorama" && p.Panorama == true {
return errors.New("you must be connected to a non-Panorama device in order to configure a Panorama server")
}
_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
if errs != nil {
return errs[0]
}
if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
return err
}
if reqError.Status != "success" {
return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
}
return nil
}
// RemoveDevice will remove a device from Panorama. If you specify the optional devicegroup parameter,
// it will only remove the device from the given device-group.
func (p *PaloAlto) RemoveDevice(serial string, devicegroup ...string) error {
var xpath string
var reqError requestError
if p.DeviceType == "panos" || p.DeviceType != "panorama" {
return errors.New("you must be connected to Panorama when removing devices")
}
if p.DeviceType == "panorama" && len(devicegroup) <= 0 {
xpath = fmt.Sprintf("/config/mgt-config/devices/entry[@name='%s']", serial)
}
if p.DeviceType == "panorama" && len(devicegroup) > 0 {
xpath = fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/device-group/entry[@name='%s']/devices/entry[@name='%s']", devicegroup[0], serial)
}
_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
if errs != nil {
return errs[0]
}
if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
return err
}
if reqError.Status != "success" {
return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
}
return nil
}