internal/sqlservermetrics/sqlservermetrics_windows.go (199 lines of code) (raw):
/*
Copyright 2023 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sqlservermetrics
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
"github.com/GoogleCloudPlatform/sql-server-agent/internal/agentstatus"
"github.com/GoogleCloudPlatform/sql-server-agent/internal/guestcollector"
configpb "github.com/GoogleCloudPlatform/sql-server-agent/protos/sqlserveragentconfig"
"github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries/log"
)
// LogPrefix is the log prefix for windows.
func LogPrefix() string {
return filepath.Join(
os.Getenv("ProgramData"),
"Google",
"google-cloud-sql-server-agent",
"logs",
"google-cloud-sql-server-agent")
}
// ConfigPath is the config path for windows.
func ConfigPath() string {
p, err := os.Executable()
if err != nil {
log.Logger.Fatalw("Failed to get the path of executable", "error", err)
}
return p
}
// AgentFilePath is the agent file path for windows.
func AgentFilePath() string {
p, err := os.Executable()
if err != nil {
log.Logger.Fatalw("Failed to get the path of executable", "error", err)
}
return p
}
// OSCollection is the windows implementation of OSCollection.
func OSCollection(ctx context.Context, path, logPrefix string, cfg *configpb.Configuration, onetime bool) error {
if !cfg.GetCollectionConfiguration().GetCollectGuestOsMetrics() {
return nil
}
if cfg.GetCredentialConfiguration() == nil || len(cfg.GetCredentialConfiguration()) == 0 {
return fmt.Errorf("empty credentials")
}
wlm, err := initCollection(ctx)
if err != nil {
return err
}
if !onetime {
if err := checkAgentStatus(wlm, path); err != nil {
return err
}
}
sourceInstanceProps := SIP
timeout := time.Duration(cfg.GetCollectionTimeoutSeconds()) * time.Second
interval := time.Duration(cfg.GetRetryIntervalInSeconds()) * time.Second
log.Logger.Info("Guest rules collection starts.")
for _, credentialCfg := range cfg.GetCredentialConfiguration() {
guestCfg := guestConfigFromCredential(credentialCfg)
if err := validateCredCfgGuest(cfg.GetRemoteCollection(), !guestCfg.LinuxRemote, guestCfg, credentialCfg.GetInstanceId(), credentialCfg.GetInstanceName()); err != nil {
log.Logger.Errorw("Invalid credential configuration", "error", err)
UsageMetricsLogger.Error(agentstatus.InvalidConfigurationsError)
if !cfg.GetRemoteCollection() {
break
}
continue
}
targetInstanceProps := sourceInstanceProps
var c guestcollector.GuestCollector
if cfg.GetRemoteCollection() {
// remote collection
targetInstanceProps = InstanceProperties{
InstanceID: credentialCfg.GetInstanceId(),
Instance: credentialCfg.GetInstanceName(),
}
host := guestCfg.ServerName
username := guestCfg.GuestUserName
if !guestCfg.LinuxRemote {
log.Logger.Debug("Starting remote win guest collection for ip " + host)
pswd, err := secretValue(ctx, sourceInstanceProps.ProjectID, guestCfg.GuestSecretName)
if err != nil {
log.Logger.Errorw("Collection failed", "target", guestCfg.ServerName, "error", fmt.Errorf("failed to get secret value: %v", err))
UsageMetricsLogger.Error(agentstatus.SecretValueError)
if !cfg.GetRemoteCollection() {
break
}
continue
}
c = guestcollector.NewWindowsCollector(host, username, pswd, UsageMetricsLogger)
} else {
// on local windows vm collecting on remote linux vm's, we use ssh, otherwise we use wmi
log.Logger.Debug("Starting remote linux guest collection for ip " + host)
// disks only used for local linux collection
c = guestcollector.NewLinuxCollector(nil, host, username, guestCfg.LinuxSSHPrivateKeyPath, true, guestCfg.GuestPortNumber, UsageMetricsLogger)
}
} else {
// local win collection
log.Logger.Debug("Starting local win guest collection")
c = guestcollector.NewWindowsCollector(nil, nil, nil, UsageMetricsLogger)
}
details := runOSCollection(ctx, c, timeout)
updateCollectedData(wlm, sourceInstanceProps, targetInstanceProps, details)
log.Logger.Debug("Finished guest collection")
if onetime {
target := "localhost"
if cfg.GetRemoteCollection() {
target = credentialCfg.GetInstanceName()
}
persistCollectedData(wlm, filepath.Join(filepath.Dir(logPrefix), fmt.Sprintf("%s-%s.json", target, "guest")))
} else {
log.Logger.Debugf("Source vm %s is sending os collected data on target machine, %s, to workload manager.", sourceInstanceProps.Instance, targetInstanceProps.Instance)
sendRequestToWLM(wlm, sourceInstanceProps.Name, cfg.GetMaxRetries(), interval)
}
// Local collection.
// Exit the loop. Only take the first credential in the credentialconfiguration array.
if !cfg.GetRemoteCollection() {
break
}
}
log.Logger.Info("Guest rules collection ends.")
return nil
}
// SQLCollection is the windows implementation of SQLCollection.
func SQLCollection(ctx context.Context, path, logPrefix string, cfg *configpb.Configuration, onetime bool) error {
if !cfg.GetCollectionConfiguration().GetCollectSqlMetrics() {
return nil
}
if cfg.GetCredentialConfiguration() == nil || len(cfg.GetCredentialConfiguration()) == 0 {
return fmt.Errorf("empty credentials")
}
wlm, err := initCollection(ctx)
if err != nil {
return err
}
if !onetime {
if err := checkAgentStatus(wlm, path); err != nil {
return err
}
}
sourceInstanceProps := SIP
timeout := time.Duration(cfg.GetCollectionTimeoutSeconds()) * time.Second
interval := time.Duration(cfg.GetRetryIntervalInSeconds()) * time.Second
log.Logger.Info("SQL rules collection starts.")
for _, credentialCfg := range cfg.GetCredentialConfiguration() {
validationDetails := initDetails()
guestCfg := guestConfigFromCredential(credentialCfg)
for _, sqlCfg := range sqlConfigFromCredential(credentialCfg) {
if err := validateCredCfgSQL(cfg.GetRemoteCollection(), !guestCfg.LinuxRemote, sqlCfg, guestCfg, credentialCfg.GetInstanceId(), credentialCfg.GetInstanceName()); err != nil {
log.Logger.Errorw("Invalid credential configuration", "error", err)
UsageMetricsLogger.Error(agentstatus.InvalidConfigurationsError)
continue
}
pswd, err := secretValue(ctx, sourceInstanceProps.ProjectID, sqlCfg.SecretName)
if err != nil {
log.Logger.Errorw("Failed to get secret value", "error", err)
UsageMetricsLogger.Error(agentstatus.SecretValueError)
continue
}
conn := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;", sqlCfg.Host, sqlCfg.Username, pswd, sqlCfg.PortNumber)
details, err := runSQLCollection(ctx, conn, timeout, !guestCfg.LinuxRemote)
if err != nil {
log.Logger.Errorw("Failed to run sql collection", "error", err)
UsageMetricsLogger.Error(agentstatus.SQLCollectionFailure)
continue
}
for _, detail := range details {
for _, field := range detail.Fields {
field["host_name"] = sqlCfg.Host
field["port_number"] = fmt.Sprintf("%d", sqlCfg.PortNumber)
}
}
// getting physical drive if on local windows collecting sql on linux remote
if cfg.GetRemoteCollection() && guestCfg.LinuxRemote {
addPhysicalDriveRemoteLinux(details, guestCfg)
} else {
addPhysicalDriveLocal(ctx, details, true)
}
for i, detail := range details {
for _, vd := range validationDetails {
if detail.Name == vd.Name {
detail.Fields = append(vd.Fields, detail.Fields...)
details[i] = detail
break
}
}
}
validationDetails = details
}
targetInstanceProps := sourceInstanceProps
// update targetInstanceProps value for remote collections.
if cfg.GetRemoteCollection() {
// remote collection
targetInstanceProps = InstanceProperties{
InstanceID: credentialCfg.GetInstanceId(),
Instance: credentialCfg.GetInstanceName(),
}
}
updateCollectedData(wlm, sourceInstanceProps, targetInstanceProps, validationDetails)
if onetime {
target := "localhost"
if cfg.GetRemoteCollection() {
target = targetInstanceProps.Instance
}
persistCollectedData(wlm, filepath.Join(filepath.Dir(logPrefix), fmt.Sprintf("%s-%s.json", target, "sql")))
} else {
log.Logger.Debugf("Source vm %s is sending collected sql data on target machine, %s, to workload manager.", sourceInstanceProps.Instance, targetInstanceProps.Instance)
sendRequestToWLM(wlm, sourceInstanceProps.Name, cfg.GetMaxRetries(), interval)
}
}
log.Logger.Info("SQL rules collection ends.")
return nil
}