readd directory

This commit is contained in:
Antoine Ouvrard
2023-03-09 17:29:50 +01:00
parent be5eb0822b
commit 3fe3b6e90a
23 changed files with 1016 additions and 0 deletions
@@ -0,0 +1,12 @@
module recup_info_zimbra
go 1.18
require (
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-ldap/ldap v3.0.3+incompatible // indirect
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
)
@@ -0,0 +1,25 @@
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -0,0 +1,78 @@
package ldap
import (
"fmt"
"log"
"github.com/go-ldap/ldap/v3"
)
type TypesearchCount struct {
ResultDest *int
SearchLdap string
LdapAttrs []string
}
type TypesearchLdapValues struct {
ResultDest *string
SearchLdap string
LdapAttrs []string
}
func SearchLdapVal(l *ldap.Conn, search TypesearchLdapValues, attrValue string) {
result, err := Search(l, search.SearchLdap, search.LdapAttrs)
if err != nil {
log.Fatal(err)
}
for _, entry := range result.Entries {
for _, cn := range entry.GetAttributeValues(attrValue) {
*search.ResultDest = cn
}
}
}
func SearchLdapCount(l *ldap.Conn, search TypesearchCount) {
result, err := Search(l, search.SearchLdap, search.LdapAttrs)
if err != nil {
log.Fatal(err)
}
*search.ResultDest = len(result.Entries)
}
// Ldap Connection without TLS
func Connect(FQDN string, BindUsername string, BindPassword string) (*ldap.Conn, error) {
// You can also use IP instead of FQDN
l, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", FQDN))
l.Bind(BindUsername, BindPassword)
if err != nil {
return nil, err
}
return l, nil
}
// Normal Search
func Search(l *ldap.Conn, filter string, ldapAttrs []string) (*ldap.SearchResult, error) {
searchReq := ldap.NewSearchRequest(
"",
ldap.ScopeWholeSubtree,
0,
0,
0,
false,
filter,
ldapAttrs,
nil,
)
result, err := l.Search(searchReq)
if err != nil {
return nil, fmt.Errorf("error: %s", err)
}
if len(result.Entries) >= 0 {
return result, nil
} else {
return nil, fmt.Errorf("couldn't fetch search entries")
}
}
@@ -0,0 +1,61 @@
package zimbra
import (
"bytes"
"encoding/xml"
"io"
"os"
"path/filepath"
)
type Localconfig struct {
XMLName xml.Name `xml:"localconfig"`
LocalconfigKeys []LocalconfigKey `xml:"key"`
}
type LocalconfigKey struct {
XMLName xml.Name `xml:"key"`
Name string `xml:"name,attr"`
Value string `xml:"value"`
}
var localConfigData map[string]string
func ReadLocalConfig(basePath string) error {
filePath := filepath.Join(basePath, "conf/localconfig.xml")
xmlFile, errOpen := os.Open(filePath)
if errOpen != nil {
return errOpen
}
defer xmlFile.Close()
var buf bytes.Buffer
_, errCopy := io.Copy(&buf, xmlFile)
if errCopy != nil {
return errCopy
}
var data Localconfig
errXml := xml.Unmarshal(buf.Bytes(), &data)
if errXml != nil {
return errXml
}
localConfigData = make(map[string]string)
for _, key := range data.LocalconfigKeys {
localConfigData[key.Name] = key.Value
}
return nil
}
func Get(param string) (string, bool) {
value, ok := localConfigData[param]
return value, ok
}
@@ -0,0 +1,22 @@
package zxsuite
import (
"log"
"os/exec"
)
func CmdZxSuite(module string, cmd string) ([]byte, error) {
outputZx, err := exec.Command("sudo", "-u", "zimbra", "/opt/zimbra/bin/zxsuite", "--json", module, cmd).Output()
if err != nil {
log.Fatal(err)
}
return outputZx, nil
}
func CmdZmcontrol(cmd string) ([]byte, error) {
outputZmcontrol, err := exec.Command("sudo", "-u", "zimbra", "/opt/zimbra/bin/zmcontrol", cmd).Output()
if err != nil {
log.Fatal(err)
}
return outputZmcontrol, nil
}
Binary file not shown.
@@ -0,0 +1,355 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"strings"
"recup_info_zimbra/pkg/ldap"
"recup_info_zimbra/pkg/zimbra"
"recup_info_zimbra/pkg/zxsuite"
)
const (
BindUsername = "uid=zimbra,cn=admins,cn=zimbra"
PathZimbra = "/opt/zimbra"
BaseDN = "uid=zimbra,cn=admins,cn=zimbra"
)
type TemplateJsonInfluxDaily struct {
FluxZimbra struct {
FluxZimbraStore TypeFluxZimbraVersion `json:"flux_zimbra_store"`
FluxZimbraZextras TypeResponse `json:"flux_zimbra_zextras"`
} `json:"flux_zimbra"`
}
type TemplateJsonInfluxHourly struct {
FluxZimbra struct {
FluxZimbraLdap TypeFluxZimbraLdap `json:"flux_zimbra_ldap"`
FluxZimbraProxy TypeFluxZimbraProxy `json:"flux_zimbra_proxy"`
FluxZimbraMta TypeFluxZimbraMta `json:"flux_zimbra_mta"`
FluxZimbraStore TypeFluxZimbraStore `json:"flux_zimbra_store"`
} `json:"flux_zimbra"`
}
// STRUCT LDAP
type TypeFluxZimbraLdap struct {
NumberCos int `json:"number_cos"`
NumberDomain int `json:"number_domain"`
NumberAliasDomain int `json:"number_alias_domain"`
NumberLdapServer int `json:"number_ldap_server"`
NameLdapServer string `json:"name_ldap_server"`
}
// STRUCT PROXY
type TypeFluxZimbraProxy struct {
NumberProxyServer int `json:"number_proxy_server"`
NameProxyServer string `json:"name_proxy_server"`
}
// STRUCT MTA
type TypeFluxZimbraMta struct {
NumberMtaServer int `json:"number_mta_server"`
NameMtaServer string `json:"name_mta_server"`
}
// STRUCT STORE
type TypeFluxZimbraStore struct {
NumberDl int `json:"number_dl"`
NumberAccount int `json:"number_account"`
NumberAdminAccount int `json:"number_admin_account"`
NumberResourceEquipment int `json:"number_resource_equipment"`
NumberResourceLocation int `json:"number_resource_location"`
NumberActiveAccount int `json:"number_active_account"`
NumberMaintenanceAccount int `json:"number_maintenance_account"`
NumberLockedAccount int `json:"number_locked_account"`
NumberClosedAccount int `json:"number_closed_account"`
NumberLockoutAccount int `json:"number_lockout_account"`
NumberPendingAccount int `json:"number_pending_account"`
NumberStoreServer int `json:"number_store_server"`
NameStoreServer string `json:"name_store_server"`
}
type TypeFluxZimbraVersion struct {
System string `json:"system"`
Version string `json:"version"`
Edition string `json:"edition"`
Patch string `json:"patch"`
}
type TypeResponse struct {
ZextrasVersion string `json:"zextras_version"`
ZextrasCommit string `json:"zextras_commit"`
ZalVersion string `json:"zal_version"`
ZalCommit string `json:"zal_commit"`
DateStart int64 `json:"dateStart"`
DateEnd int64 `json:"dateEnd"`
Expired bool `json:"expired"`
Type string `json:"type"`
Customer string `json:"customer"`
Reseller string `json:"reseller"`
Company string `json:"company"`
OrderID string `json:"order_id"`
AccountCount int `json:"accountCount"`
LicensedUsers string `json:"licensedUsers"`
NotYetValid bool `json:"notYetValid"`
IsWithinGraceInterval bool `json:"isWithinGraceInterval"`
TeamchatActiveCount int `json:"teamchatActiveCount"`
TeamchatBasicActive bool `json:"teamchatBasicActive"`
Modules struct {
ZxBackup struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
} `json:"ZxBackup"`
ZxMobile struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
} `json:"ZxMobile"`
ZxAdmin struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
} `json:"ZxAdmin"`
ZxPowerstore struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
EnabledOnNE bool `json:"enabledOnNE"`
} `json:"ZxPowerstore"`
SproxyD struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
EnabledOnNE bool `json:"enabledOnNE"`
} `json:"SproxyD"`
ZxDrive struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
} `json:"ZxDrive"`
ZxDocs struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
} `json:"ZxDocs"`
ZxChat struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
} `json:"ZxChat"`
ZxHA struct {
Quantity string `json:"quantity"`
Licensed bool `json:"licensed"`
} `json:"ZxHA"`
} `json:"modules"`
ServerID string `json:"serverID"`
CanRemoveChatBrand bool `json:"canRemoveChatBrand"`
Exists bool `json:"exists"`
Readable bool `json:"readable"`
Valid bool `json:"valid"`
Empty bool `json:"empty"`
Lockok bool `json:"lockok"`
Writable bool `json:"writable"`
AvailableSpaceForBlobs int `json:"availableSpaceForBlobs"`
AvailableSpaceForMetadata int `json:"availableSpaceForMetadata"`
CaseSensitive bool `json:"caseSensitive"`
BackupStat struct {
Scan struct {
FirstScan int `json:"firstScan"`
FirstZeXtrasBuild string `json:"firstZeXtrasBuild"`
FirstZimbraVersion string `json:"firstZimbraVersion"`
NumNewAccounts int `json:"numNewAccounts"`
NumBackupedItems int `json:"numBackupedItems"`
NumSkippedAccounts int `json:"numSkippedAccounts"`
NumCheckedItems int `json:"numCheckedItems"`
FirstZeXtrasVersion string `json:"firstZeXtrasVersion"`
LastZimbraVersion string `json:"lastZimbraVersion"`
NumActiveBackupedAccounts int `json:"numActiveBackupedAccounts"`
NumContentFiles int `json:"numContentFiles"`
NumUpAccounts int `json:"numUpAccounts"`
LastScan int `json:"lastScan"`
LastZeXtrasBuild string `json:"lastZeXtrasBuild"`
NumNewItems int `json:"numNewItems"`
LastDeepScan int `json:"lastDeepScan"`
NumDeletedAccounts int `json:"numDeletedAccounts"`
NumCheckedAccounts int `json:"numCheckedAccounts"`
ItemsPerSecond int `json:"itemsPerSecond"`
NumDeletedItems int `json:"numDeletedItems"`
LastZeXtrasVersion string `json:"lastZeXtrasVersion"`
NumUpItems int `json:"numUpItems"`
} `json:"scan"`
Purge struct {
NumTotalAccounts int `json:"numTotalAccounts"`
NumPurgedItems int `json:"numPurgedItems"`
NumPurgedAccounts int `json:"numPurgedAccounts"`
NumCheckedItems int `json:"numCheckedItems"`
NumPurgedServerConf int `json:"numPurgedServerConf"`
NumCheckedAccounts int `json:"numCheckedAccounts"`
ItemsPerSecond int `json:"itemsPerSecond"`
NumPurgedDigests int `json:"numPurgedDigests"`
} `json:"purge"`
} `json:"backupStat"`
DataPath string `json:"dataPath"`
}
type TemplateGetVersion struct {
Ok bool `json:"ok"`
Response TypeResponse `json:"response"`
}
func main() {
error := zimbra.ReadLocalConfig(PathZimbra)
if error != nil {
log.Fatal(error)
}
FQDN, _ := zimbra.Get("zimbra_server_hostname")
ldapHost, _ := zimbra.Get("ldap_host")
BindPassword, _ := zimbra.Get("zimbra_ldap_password")
var outputJsonDaily TemplateJsonInfluxDaily
var outputJsonHourly TemplateJsonInfluxHourly
SearchAccount := "(&(objectClass=zimbraAccount)(!(zimbraIsSystemAccount=TRUE))(!(zimbraIsSystemResource=TRUE))(zimbraMailHost=" + FQDN + ")"
AccountFilter := SearchAccount + ")"
SearchServer := "(&(objectClass=zimbraServer)(cn=" + FQDN + ")"
typesearchldap := []ldap.TypesearchCount{
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraLdap.NumberCos,
SearchLdap: "(&(objectClass=zimbraCos))",
LdapAttrs: []string{"cn"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraLdap.NumberDomain,
SearchLdap: "(&(objectClass=zimbraDomain)(zimbraDomainType=local))",
LdapAttrs: []string{"zimbraDomainName"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraLdap.NumberAliasDomain,
SearchLdap: "(&(objectClass=zimbraDomain)(zimbraDomainType=alias))",
LdapAttrs: []string{"zimbraDomainName"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraLdap.NumberLdapServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=ldap))",
LdapAttrs: []string{"cn"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraProxy.NumberProxyServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=proxy))",
LdapAttrs: []string{"cn"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraMta.NumberMtaServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=mta))",
LdapAttrs: []string{"cn"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberDl,
SearchLdap: "(&(objectclass=zimbradistributionlist)(zimbramailhost=" + FQDN + "))",
LdapAttrs: []string{"zimbraMailAlias"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberAccount,
SearchLdap: AccountFilter,
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberAdminAccount,
SearchLdap: "(&(objectClass=zimbraAccount)(zimbraIsAdminAccount=TRUE)(zimbraMailHost=" + FQDN + "))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberResourceEquipment,
SearchLdap: SearchAccount + "(zimbraCalResType=Equipment))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberResourceLocation,
SearchLdap: SearchAccount + "(zimbraCalResType=Location))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberActiveAccount,
SearchLdap: SearchAccount + "(zimbraAccountStatus=active))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberMaintenanceAccount,
SearchLdap: SearchAccount + "(zimbraAccountStatus=maintenance))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberLockedAccount,
SearchLdap: SearchAccount + "(zimbraAccountStatus=locked))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberClosedAccount,
SearchLdap: SearchAccount + "(zimbraAccountStatus=closed))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberLockoutAccount,
SearchLdap: SearchAccount + "(zimbraAccountStatus=lockout))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberPendingAccount,
SearchLdap: SearchAccount + "(zimbraAccountStatus=pending))",
LdapAttrs: []string{"zimbraMailDeliveryAddress"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NumberStoreServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=mailbox))",
LdapAttrs: []string{"cn"}},
}
typesearchldapval := []ldap.TypesearchLdapValues{
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraLdap.NameLdapServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=ldap))",
LdapAttrs: []string{"cn"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraProxy.NameProxyServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=proxy))",
LdapAttrs: []string{"cn"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraMta.NameMtaServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=mta))",
LdapAttrs: []string{"cn"}},
{
ResultDest: &outputJsonHourly.FluxZimbra.FluxZimbraStore.NameStoreServer,
SearchLdap: SearchServer + "(zimbraServiceEnabled=mailbox))",
LdapAttrs: []string{"cn"}},
}
// Non-TLS Connection
l, err := ldap.Connect(ldapHost, BindUsername, BindPassword)
if err != nil {
log.Fatal(err)
}
defer l.Close()
daily := flag.Bool("daily", false, "Daily excecution")
hourly := flag.Bool("hourly", false, "Hourly excecution")
flag.Parse()
if *daily {
outputZxVers, _ := zxsuite.CmdZxSuite("core", "getVersion")
outputZxLicense, _ := zxsuite.CmdZxSuite("core", "getLicenseInfo")
outputZxBackup, _ := zxsuite.CmdZxSuite("backup", "getBackupInfo")
outputZmVers, _ := zxsuite.CmdZmcontrol("-v")
rOut := strings.Split(strings.Replace(string(outputZmVers), "\n", "", -1), " ")
version := strings.Split(string(rOut[6]), "_")
patch := strings.Split(string(version[1]), ".")
outputJsonDaily.FluxZimbra.FluxZimbraStore.System = rOut[2]
outputJsonDaily.FluxZimbra.FluxZimbraStore.Version = version[0]
outputJsonDaily.FluxZimbra.FluxZimbraStore.Edition = rOut[3]
outputJsonDaily.FluxZimbra.FluxZimbraStore.Patch = patch[0]
var templategetversion TemplateGetVersion
json.Unmarshal([]byte(outputZxVers), &templategetversion)
json.Unmarshal([]byte(outputZxLicense), &templategetversion)
json.Unmarshal([]byte(outputZxBackup), &templategetversion)
outputJsonDaily.FluxZimbra.FluxZimbraZextras = templategetversion.Response
out, _ := json.Marshal(outputJsonDaily)
fmt.Println(string(out))
} else if *hourly {
for _, s := range typesearchldap {
ldap.SearchLdapCount(l, s)
}
for _, s := range typesearchldapval {
ldap.SearchLdapVal(l, s, "cn")
}
out, _ := json.Marshal(outputJsonHourly)
fmt.Println(string(out))
} else {
flag.Usage()
os.Exit(1)
}
}