agent/inventory/gatherers/role/dataProvider.go (256 lines of code) (raw):

package role import ( "encoding/json" "encoding/xml" "fmt" "path/filepath" "strings" "github.com/aliyun/aliyun_assist_client/agent/inventory/appconfig" "github.com/aliyun/aliyun_assist_client/agent/inventory/model" "github.com/aliyun/aliyun_assist_client/agent/util" "github.com/aliyun/aliyun_assist_client/agent/util/osutil" "github.com/aliyun/aliyun_assist_client/agent/util/stringutil" "github.com/aliyun/aliyun_assist_client/common/executil" "github.com/aliyun/aliyun_assist_client/agent/log" "github.com/google/uuid" ) var ( startMarker = "<start" + randomString(8) + ">" endMarker = "<end" + randomString(8) + ">" roleInfoScript = ` [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 import-module ServerManager $roleInfo = Get-WindowsFeature | Select-Object Name, DisplayName, Description, Installed, InstallState, FeatureType, Path, SubFeatures, ServerComponentDescriptor, DependsOn, Parent $jsonObj = @() foreach($r in $roleInfo) { $Name = $r.Name $DisplayName = $r.DisplayName $Description = $r.Description $Installed = $r.Installed $InstalledState = $r.InstallState $FeatureType = $r.FeatureType $Path = $r.Path $SubFeatures = $r.SubFeatures $ServerComponentDescriptor = $r.ServerComponentDescriptor $DependsOn = $r.DependsOn $Parent = $r.Parent $jsonObj += @" {"Name": "` + mark(`$Name`) + `", "DisplayName": "` + mark(`$DisplayName`) + `", "Description": "` + mark(`$Description`) + `", "Installed": "$Installed", "InstalledState": "$InstalledState", "FeatureType": "$FeatureType", "Path": "` + mark(`$Path`) + `", "SubFeatures": "` + mark(`$SubFeatures`) + `", "ServerComponentDescriptor": "` + mark(`$ServerComponentDescriptor`) + `", "DependsOn": "` + mark(`$DependsOn`) + `", "Parent": "` + mark(`$Parent`) + `"} "@ } $result = $jsonObj -join "," $result = "[" + $result + "]" [Console]::WriteLine($result) ` roleInfoScriptUsingRegistry = ` [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $keyExists = Test-Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OC Manager\Subcomponents" $jsonObj = @() if ($keyExists) { $key = Get-Item "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OC Manager\Subcomponents" $valueNames = $key.GetValueNames(); foreach ($valueName in $valueNames) { $value = $key.GetValue($valueName); if ($value -gt 0) { $installed = "True" } else { $installed = "False" } $jsonObj += @" {"Name": "$valueName", "Installed": "$installed"} "@ } } $result = $jsonObj -join "," $result = "[" + $result + "]" [Console]::WriteLine($result) ` ) const ( // Use powershell to get role info PowershellCmd = "powershell" QueryFileName = "roleInfo.xml" ) type RoleService struct { RoleService []RoleService DisplayName string `xml:"DisplayName,attr"` Installed string `xml:"Installed,attr"` Id string `xml:"Id,attr"` Default string `xml:"Default,attr"` } type Role struct { RoleService []RoleService DisplayName string `xml:"DisplayName,attr"` Installed string `xml:"Installed,attr"` Id string `xml:"Id,attr"` Default string `xml:"Default,attr"` } type Feature struct { Feature []Feature DisplayName string `xml:"DisplayName,attr"` Installed string `xml:"Installed,attr"` Id string `xml:"Id,attr"` Default string `xml:"Default,attr"` } type Result struct { Role []Role Feature []Feature } func randomString(length int) string { return uuid.New().String()[:length] } func mark(s string) string { return startMarker + s + endMarker } // LogError is a wrapper on log.Error for easy testability func LogError(err error) { // To debug unit test, please uncomment following line // fmt.Println(err) log.GetLogger().Error(err) } var cmdExecutor = executeCommand var readFile = readAllText var resultPath = getResultFilePath func executeCommand(command string, args ...string) ([]byte, error) { return executil.Command(command, args...).CombinedOutput() } func readAllText(path string) (xmlData string, err error) { xmlData, err = osutil.ReadFile(path) return } func getResultFilePath() (path string, err error) { path = filepath.Join(appconfig.DefaultDataStorePath, util.GetInstanceId(), appconfig.InventoryRootDirName, appconfig.RoleInventoryRootDirName, QueryFileName) return } func readServiceData(roleService RoleService, roleInfo *[]model.RoleData) { roleData := model.RoleData{ Name: roleService.Id, DisplayName: roleService.DisplayName, Installed: strings.Title(roleService.Installed), FeatureType: "Role Service", } *roleInfo = append(*roleInfo, roleData) for i := 0; i < len(roleService.RoleService); i++ { readServiceData(roleService.RoleService[i], roleInfo) } } func readRoleData(role Role, roleInfo *[]model.RoleData) { roleData := model.RoleData{ Name: role.Id, DisplayName: role.DisplayName, Installed: strings.Title(role.Installed), FeatureType: "Role", } *roleInfo = append(*roleInfo, roleData) for i := 0; i < len(role.RoleService); i++ { readServiceData(role.RoleService[i], roleInfo) } } func readFeatureData(feature Feature, roleInfo *[]model.RoleData) { roleData := model.RoleData{ Name: feature.Id, DisplayName: feature.DisplayName, Installed: strings.Title(feature.Installed), FeatureType: "Feature", } *roleInfo = append(*roleInfo, roleData) for i := 0; i < len(feature.Feature); i++ { readFeatureData(feature.Feature[i], roleInfo) } } func readAllData(result Result, roleInfo *[]model.RoleData) { roles := result.Role features := result.Feature for i := 0; i < len(roles); i++ { readRoleData(roles[i], roleInfo) } for i := 0; i < len(features); i++ { readFeatureData(features[i], roleInfo) } } // executePowershellCommands executes commands in Powershell to get all windows processes. func executePowershellCommands(command, args string) (output []byte, err error) { if output, err = cmdExecutor(PowershellCmd, command+" "+args); err != nil { log.GetLogger().Debugf("Failed to execute command : %v %v with error - %v", command, args, err.Error()) log.GetLogger().Debugf("Command Stderr: %v", string(output)) err = fmt.Errorf("Command failed with error: %v", string(output)) } return } func collectDataFromPowershell(powershellCommand string, roleInfo *[]model.RoleData) (err error) { log.GetLogger().Debugf("Executing command: %v", powershellCommand) var output []byte var cleanOutput string output, err = executePowershellCommands(powershellCommand, "") if err != nil { log.GetLogger().Errorf("Error executing command - %v", err.Error()) return } log.GetLogger().Debugf("Command output before clean up: %v", string(output)) cleanOutput, err = stringutil.ReplaceMarkedFields(stringutil.CleanupNewLines(string(output)), startMarker, endMarker, stringutil.CleanupJSONField) if err != nil { LogError(err) return } log.GetLogger().Debugf("Command output: %v", string(cleanOutput)) if err = json.Unmarshal([]byte(cleanOutput), roleInfo); err != nil { err = fmt.Errorf("Unable to parse command output - %v", err.Error()) log.GetLogger().Error(err.Error()) log.GetLogger().Debugf("Error parsing command output - no data to return") } return } // Some early 2008 versions use ServerManager for role management, so use that for collecting data. func collectDataUsingServerManager(roleInfo *[]model.RoleData) (err error) { var xmlData, path string var output []byte path, err = resultPath() if err != nil { log.GetLogger().Errorf("Error getting path of file") return } powershellCommand := "Servermanagercmd.exe -q " + path output, err = executePowershellCommands(powershellCommand, "") log.GetLogger().Debugf("Command output: %v", string(output)) if err != nil { log.GetLogger().Errorf("Error executing command - %v", err.Error()) return } xmlData, err = readFile(path) if err != nil { log.GetLogger().Errorf("Error reading role info file - %v", err.Error()) return } v := Result{} err = xml.Unmarshal([]byte(xmlData), &v) if err != nil { log.GetLogger().Errorf("Error unmarshalling xml: %v", err.Error()) return } readAllData(v, roleInfo) osutil.DeleteFile(path) return } func collectRoleData(config model.Config) (data []model.RoleData, err error) { log.GetLogger().Debugf("collectRoleData called") err = collectDataFromPowershell(roleInfoScript, &data) // Some early 2008 releases uses server manager for getting role information if err != nil { log.GetLogger().Debugf("Trying collecting role data using server manager") err = collectDataUsingServerManager(&data) } // In some versions of 2003, roles information is stored as subcomponents in registry. if err != nil { log.GetLogger().Debugf("Trying collecting role data using registry") err = collectDataFromPowershell(roleInfoScriptUsingRegistry, &data) } if err == nil && data != nil && len(data) > RoleCountLimit { err = fmt.Errorf(RoleCountLimitExceeded+", got %d", len(data)) log.GetLogger().WithError(err).Error("collect role data failed") return []model.RoleData{}, err } if err != nil { log.GetLogger().Errorf("Failed to collect role data using possible mechanisms") } return }