<commit_before>package gitstore

import (
	"bytes"
	"context"
	"fmt"
	"sort"
	"sync"
	"time"

	"go.skia.org/infra/go/depot_tools"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/vcsinfo"
)

// btVCS implements the vcsinfo.VCS interface based on a BT-backed GitStore.
type btVCS struct {
	gitStore           GitStore
	repo               *gitiles.Repo
	defaultBranch      string
	secondaryVCS       vcsinfo.VCS
	secondaryExtractor depot_tools.DEPSExtractor

	branchInfo   *BranchPointer
	indexCommits []*vcsinfo.IndexCommit
	hashes       []string
	timestamps   map[string]time.Time           //
	detailsCache map[string]*vcsinfo.LongCommit // Details
	mutex        sync.RWMutex
}

// NewVCS returns an instance of vcsinfo.VCS that is backed by the given GitStore and uses the
// gittiles.Repo to retrieve files. Each instance provides an interface to one branch.
// If defaultBranch is "" all commits in the repository are considered.
// The instances of gitiles.Repo is only used to fetch files.
func NewVCS(gitstore GitStore, defaultBranch string, repo *gitiles.Repo) (vcsinfo.VCS, error) {
	ret := &btVCS{
		gitStore:      gitstore,
		repo:          repo,
		defaultBranch: defaultBranch,
	}
	if err := ret.Update(context.TODO(), true, false); err != nil {
		return nil, err
	}
	return ret, nil
}

// SetSecondaryRepo allows to add a secondary repository and extractor to this instance.
// It is not included in the constructor since it is currently only used by the Gold ingesters.
func (b *btVCS) SetSecondaryRepo(secVCS vcsinfo.VCS, extractor depot_tools.DEPSExtractor) {
	b.secondaryVCS = secVCS
	b.secondaryExtractor = extractor
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) Update(ctx context.Context, pull, allBranches bool) error {
	// Simulate a pull by fetching the latest head of the target branch.
	if pull {
		allBranches, err := b.gitStore.GetBranches(ctx)
		if err != nil {
			return err
		}

		var ok bool
		b.branchInfo, ok = allBranches[b.defaultBranch]
		if !ok {
			return skerr.Fmt("Unable to find branch %s in BitTable repo %s", b.defaultBranch, (b.gitStore.(*btGitStore)).repoURL)
		}
	}

	// Get all index commits for the current branch.
	return b.fetchIndexRange(ctx, 0, b.branchInfo.Index+1)
}

// From implements the vcsinfo.VCS interface
func (b *btVCS) From(start time.Time) []string {
	b.mutex.RLock()
	defer b.mutex.RUnlock()

	// Add a millisecond because we only want commits after the startTime. Timestamps in git are
	// only at second level granularity.
	found := b.timeRange(start.Add(time.Millisecond), MaxTime)
	ret := make([]string, len(found))
	for i, c := range found {
		ret[i] = c.Hash
	}
	return ret
}

// Details implements the vcsinfo.VCS interface
func (b *btVCS) Details(ctx context.Context, hash string, includeBranchInfo bool) (*vcsinfo.LongCommit, error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return b.details(ctx, hash, includeBranchInfo)
}

// TODO(stephan): includeBranchInfo currently does nothing. This needs to fixed for the few clients
// that need it.

// details returns all meta data details we care about.
func (b *btVCS) details(ctx context.Context, hash string, includeBranchInfo bool) (*vcsinfo.LongCommit, error) {
	commits, err := b.gitStore.Get(ctx, []string{hash})
	if err != nil {
		return nil, err
	}

	if len(commits) == 0 {
		return nil, skerr.Fmt("Commit %s not found", hash)
	}
	return commits[0], nil
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) LastNIndex(N int) []*vcsinfo.IndexCommit {
	b.mutex.RLock()
	defer b.mutex.RUnlock()

	if N > len(b.indexCommits) {
		N = len(b.indexCommits)
	}
	ret := make([]*vcsinfo.IndexCommit, 0, N)
	return append(ret, b.indexCommits[len(b.indexCommits)-N:]...)
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) Range(begin, end time.Time) []*vcsinfo.IndexCommit {
	return b.timeRange(begin, end)
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) IndexOf(ctx context.Context, hash string) (int, error) {
	b.mutex.RLock()
	defer b.mutex.Unlock()

	for _, c := range b.indexCommits {
		if c.Hash == hash {
			return c.Index, nil
		}
	}

	// If it was not in memory we need to fetch it
	details, err := b.gitStore.Get(ctx, []string{hash})
	if err != nil {
		return 0, err
	}

	if len(details) == 0 {
		return 0, skerr.Fmt("Hash %s does not exist in repository on branch %s", hash, b.defaultBranch)
	}

	return 0, nil
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) ByIndex(ctx context.Context, N int) (*vcsinfo.LongCommit, error) {
	// findFn returns the hash when N is within commits
	findFn := func(commits []*vcsinfo.IndexCommit) string {
		i := sort.Search(len(commits), func(i int) bool { return commits[i].Index >= N })
		return commits[i].Hash
	}

	var hash string
	b.mutex.RLock()
	if len(b.indexCommits) > 0 {
		firstIdx := b.indexCommits[0].Index
		lastIdx := b.indexCommits[len(b.indexCommits)-1].Index
		if (N >= firstIdx) && (N <= lastIdx) {
			hash = findFn(b.indexCommits)
		}
	}
	b.mutex.RUnlock()

	// Fetch the hash
	if hash == "" {
		return nil, fmt.Errorf("Hash index not found: %d", N)
	}
	return b.details(ctx, hash, false)
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) GetFile(ctx context.Context, fileName, commitHash string) (string, error) {
	var buf bytes.Buffer
	if err := b.repo.ReadFileAtRef(fileName, commitHash, &buf); err != nil {
		return "", skerr.Fmt("Error reading file %s @ %s via gitiles: %s", fileName, commitHash, err)
	}
	return buf.String(), nil
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) ResolveCommit(ctx context.Context, commitHash string) (string, error) {
	return "", skerr.Fmt("Not implemented yet")
}

// fetchIndexRange gets in the range [startIndex, endIndex).
func (b *btVCS) fetchIndexRange(ctx context.Context, startIndex, endIndex int) error {
	newIC, err := b.gitStore.RangeN(ctx, startIndex, endIndex, b.defaultBranch)
	if err != nil {
		return err
	}

	if len(newIC) == 0 {
		return nil
	}

	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.indexCommits = newIC
	return nil
}

func (b *btVCS) timeRangeNG(start time.Time, end time.Time) []*vcsinfo.IndexCommit {
	startSec := start.Unix()
	endSec := end.Unix()
	n := len(b.indexCommits)
	startIdx := 0
	for ; startIdx < n; startIdx++ {
		exp := b.indexCommits[startIdx].Timestamp.Unix() >= startSec
		if exp {
			break
		}
	}

	endIdx := startIdx
	for ; endIdx < n; endIdx++ {
		exp := b.indexCommits[endIdx].Timestamp.Unix() >= endSec
		if exp {
			break
		}
	}

	if endIdx <= startIdx {
		return []*vcsinfo.IndexCommit{}
	}
	return b.indexCommits[startIdx:endIdx]
}

func (b *btVCS) timeRange(start time.Time, end time.Time) []*vcsinfo.IndexCommit {
	n := len(b.indexCommits)
	startIdx := 0
	for ; startIdx < n; startIdx++ {
		exp := b.indexCommits[startIdx].Timestamp.After(start) || b.indexCommits[startIdx].Timestamp.Equal(start)
		if exp {
			break
		}
	}

	endIdx := startIdx
	for ; endIdx < n; endIdx++ {
		exp := b.indexCommits[endIdx].Timestamp.After(end) || b.indexCommits[endIdx].Timestamp.Equal(end)
		if exp {
			break
		}
	}

	if endIdx <= startIdx {
		return []*vcsinfo.IndexCommit{}
	}
	return b.indexCommits[startIdx:endIdx]
}
<commit_msg>gitstore - Reverse search since most requests will be for recent commits.<commit_after>package gitstore

import (
	"bytes"
	"context"
	"fmt"
	"sort"
	"sync"
	"time"

	"go.skia.org/infra/go/depot_tools"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/vcsinfo"
)

// btVCS implements the vcsinfo.VCS interface based on a BT-backed GitStore.
type btVCS struct {
	gitStore           GitStore
	repo               *gitiles.Repo
	defaultBranch      string
	secondaryVCS       vcsinfo.VCS
	secondaryExtractor depot_tools.DEPSExtractor

	branchInfo   *BranchPointer
	indexCommits []*vcsinfo.IndexCommit
	hashes       []string
	timestamps   map[string]time.Time           //
	detailsCache map[string]*vcsinfo.LongCommit // Details
	mutex        sync.RWMutex
}

// NewVCS returns an instance of vcsinfo.VCS that is backed by the given GitStore and uses the
// gittiles.Repo to retrieve files. Each instance provides an interface to one branch.
// If defaultBranch is "" all commits in the repository are considered.
// The instances of gitiles.Repo is only used to fetch files.
func NewVCS(gitstore GitStore, defaultBranch string, repo *gitiles.Repo) (vcsinfo.VCS, error) {
	ret := &btVCS{
		gitStore:      gitstore,
		repo:          repo,
		defaultBranch: defaultBranch,
	}
	if err := ret.Update(context.TODO(), true, false); err != nil {
		return nil, err
	}
	return ret, nil
}

// SetSecondaryRepo allows to add a secondary repository and extractor to this instance.
// It is not included in the constructor since it is currently only used by the Gold ingesters.
func (b *btVCS) SetSecondaryRepo(secVCS vcsinfo.VCS, extractor depot_tools.DEPSExtractor) {
	b.secondaryVCS = secVCS
	b.secondaryExtractor = extractor
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) Update(ctx context.Context, pull, allBranches bool) error {
	// Simulate a pull by fetching the latest head of the target branch.
	if pull {
		allBranches, err := b.gitStore.GetBranches(ctx)
		if err != nil {
			return err
		}

		var ok bool
		b.branchInfo, ok = allBranches[b.defaultBranch]
		if !ok {
			return skerr.Fmt("Unable to find branch %s in BitTable repo %s", b.defaultBranch, (b.gitStore.(*btGitStore)).repoURL)
		}
	}

	// Get all index commits for the current branch.
	return b.fetchIndexRange(ctx, 0, b.branchInfo.Index+1)
}

// From implements the vcsinfo.VCS interface
func (b *btVCS) From(start time.Time) []string {
	b.mutex.RLock()
	defer b.mutex.RUnlock()

	// Add a millisecond because we only want commits after the startTime. Timestamps in git are
	// only at second level granularity.
	found := b.timeRange(start.Add(time.Millisecond), MaxTime)
	ret := make([]string, len(found))
	for i, c := range found {
		ret[i] = c.Hash
	}
	return ret
}

// Details implements the vcsinfo.VCS interface
func (b *btVCS) Details(ctx context.Context, hash string, includeBranchInfo bool) (*vcsinfo.LongCommit, error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return b.details(ctx, hash, includeBranchInfo)
}

// TODO(stephan): includeBranchInfo currently does nothing. This needs to fixed for the few clients
// that need it.

// details returns all meta data details we care about.
func (b *btVCS) details(ctx context.Context, hash string, includeBranchInfo bool) (*vcsinfo.LongCommit, error) {
	commits, err := b.gitStore.Get(ctx, []string{hash})
	if err != nil {
		return nil, err
	}

	if len(commits) == 0 {
		return nil, skerr.Fmt("Commit %s not found", hash)
	}
	return commits[0], nil
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) LastNIndex(N int) []*vcsinfo.IndexCommit {
	b.mutex.RLock()
	defer b.mutex.RUnlock()

	if N > len(b.indexCommits) {
		N = len(b.indexCommits)
	}
	ret := make([]*vcsinfo.IndexCommit, 0, N)
	return append(ret, b.indexCommits[len(b.indexCommits)-N:]...)
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) Range(begin, end time.Time) []*vcsinfo.IndexCommit {
	return b.timeRange(begin, end)
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) IndexOf(ctx context.Context, hash string) (int, error) {
	b.mutex.RLock()
	defer b.mutex.Unlock()

	for i := len(b.indexCommits) - 1; i >= 0; i-- {
		if hash == b.indexCommits[i].Hash {
			return b.indexCommits[i].Index, nil
		}
	}

	// If it was not in memory we need to fetch it
	details, err := b.gitStore.Get(ctx, []string{hash})
	if err != nil {
		return 0, err
	}

	if len(details) == 0 {
		return 0, skerr.Fmt("Hash %s does not exist in repository on branch %s", hash, b.defaultBranch)
	}

	return 0, nil
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) ByIndex(ctx context.Context, N int) (*vcsinfo.LongCommit, error) {
	// findFn returns the hash when N is within commits
	findFn := func(commits []*vcsinfo.IndexCommit) string {
		i := sort.Search(len(commits), func(i int) bool { return commits[i].Index >= N })
		return commits[i].Hash
	}

	var hash string
	b.mutex.RLock()
	if len(b.indexCommits) > 0 {
		firstIdx := b.indexCommits[0].Index
		lastIdx := b.indexCommits[len(b.indexCommits)-1].Index
		if (N >= firstIdx) && (N <= lastIdx) {
			hash = findFn(b.indexCommits)
		}
	}
	b.mutex.RUnlock()

	// Fetch the hash
	if hash == "" {
		return nil, fmt.Errorf("Hash index not found: %d", N)
	}
	return b.details(ctx, hash, false)
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) GetFile(ctx context.Context, fileName, commitHash string) (string, error) {
	var buf bytes.Buffer
	if err := b.repo.ReadFileAtRef(fileName, commitHash, &buf); err != nil {
		return "", skerr.Fmt("Error reading file %s @ %s via gitiles: %s", fileName, commitHash, err)
	}
	return buf.String(), nil
}

// Update implements the vcsinfo.VCS interface
func (b *btVCS) ResolveCommit(ctx context.Context, commitHash string) (string, error) {
	return "", skerr.Fmt("Not implemented yet")
}

// fetchIndexRange gets in the range [startIndex, endIndex).
func (b *btVCS) fetchIndexRange(ctx context.Context, startIndex, endIndex int) error {
	newIC, err := b.gitStore.RangeN(ctx, startIndex, endIndex, b.defaultBranch)
	if err != nil {
		return err
	}

	if len(newIC) == 0 {
		return nil
	}

	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.indexCommits = newIC
	return nil
}

func (b *btVCS) timeRangeNG(start time.Time, end time.Time) []*vcsinfo.IndexCommit {
	startSec := start.Unix()
	endSec := end.Unix()
	n := len(b.indexCommits)
	startIdx := 0
	for ; startIdx < n; startIdx++ {
		exp := b.indexCommits[startIdx].Timestamp.Unix() >= startSec
		if exp {
			break
		}
	}

	endIdx := startIdx
	for ; endIdx < n; endIdx++ {
		exp := b.indexCommits[endIdx].Timestamp.Unix() >= endSec
		if exp {
			break
		}
	}

	if endIdx <= startIdx {
		return []*vcsinfo.IndexCommit{}
	}
	return b.indexCommits[startIdx:endIdx]
}

func (b *btVCS) timeRange(start time.Time, end time.Time) []*vcsinfo.IndexCommit {
	n := len(b.indexCommits)
	startIdx := 0
	for ; startIdx < n; startIdx++ {
		exp := b.indexCommits[startIdx].Timestamp.After(start) || b.indexCommits[startIdx].Timestamp.Equal(start)
		if exp {
			break
		}
	}

	endIdx := startIdx
	for ; endIdx < n; endIdx++ {
		exp := b.indexCommits[endIdx].Timestamp.After(end) || b.indexCommits[endIdx].Timestamp.Equal(end)
		if exp {
			break
		}
	}

	if endIdx <= startIdx {
		return []*vcsinfo.IndexCommit{}
	}
	return b.indexCommits[startIdx:endIdx]
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

    http://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 main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	resources "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
)

type AksEngineAPIModel struct {
	Location   string            `json:"location,omitempty"`
	Name       string            `json:"name,omitempty"`
	Tags       map[string]string `json:"tags,omitempty"`
	APIVersion string            `json:"APIVersion"`

	Properties *Properties `json:"properties"`
}

type Properties struct {
	OrchestratorProfile     *OrchestratorProfile     `json:"orchestratorProfile,omitempty"`
	MasterProfile           *MasterProfile           `json:"masterProfile,omitempty"`
	AgentPoolProfiles       []*AgentPoolProfile      `json:"agentPoolProfiles,omitempty"`
	LinuxProfile            *LinuxProfile            `json:"linuxProfile,omitempty"`
	WindowsProfile          *WindowsProfile          `json:"windowsProfile,omitempty"`
	ServicePrincipalProfile *ServicePrincipalProfile `json:"servicePrincipalProfile,omitempty"`
	ExtensionProfiles       []map[string]string      `json:"extensionProfiles,omitempty"`
	CustomCloudProfile      *CustomCloudProfile      `json:"customCloudProfile,omitempty"`
	FeatureFlags            *FeatureFlags            `json:"featureFlags,omitempty"`
}

type ServicePrincipalProfile struct {
	ClientID string `json:"clientId,omitempty"`
	Secret   string `json:"secret,omitempty"`
}

type LinuxProfile struct {
	AdminUsername string `json:"adminUsername"`
	SSHKeys       *SSH   `json:"ssh"`
}

type SSH struct {
	PublicKeys []PublicKey `json:"publicKeys"`
}

type PublicKey struct {
	KeyData string `json:"keyData"`
}

type WindowsProfile struct {
	AdminUsername         string `json:"adminUsername,omitempty"`
	AdminPassword         string `json:"adminPassword,omitempty"`
	ImageVersion          string `json:"imageVersion,omitempty"`
	WindowsImageSourceURL string `json:"WindowsImageSourceUrl"`
	WindowsPublisher      string `json:"WindowsPublisher"`
	WindowsOffer          string `json:"WindowsOffer"`
	WindowsSku            string `json:"WindowsSku"`
	WindowsDockerVersion  string `json:"windowsDockerVersion"`
	SSHEnabled            bool   `json:"sshEnabled,omitempty"`
}

// KubernetesContainerSpec defines configuration for a container spec
type KubernetesContainerSpec struct {
	Name           string `json:"name,omitempty"`
	Image          string `json:"image,omitempty"`
	CPURequests    string `json:"cpuRequests,omitempty"`
	MemoryRequests string `json:"memoryRequests,omitempty"`
	CPULimits      string `json:"cpuLimits,omitempty"`
	MemoryLimits   string `json:"memoryLimits,omitempty"`
}

// KubernetesAddon defines a list of addons w/ configuration to include with the cluster deployment
type KubernetesAddon struct {
	Name       string                    `json:"name,omitempty"`
	Enabled    *bool                     `json:"enabled,omitempty"`
	Containers []KubernetesContainerSpec `json:"containers,omitempty"`
	Config     map[string]string         `json:"config,omitempty"`
	Data       string                    `json:"data,omitempty"`
}

type KubernetesConfig struct {
	CustomWindowsPackageURL      string            `json:"customWindowsPackageURL,omitempty"`
	CustomHyperkubeImage         string            `json:"customHyperkubeImage,omitempty"`
	CustomCcmImage               string            `json:"customCcmImage,omitempty"` // Image for cloud-controller-manager
	UseCloudControllerManager    *bool             `json:"useCloudControllerManager,omitempty"`
	NetworkPlugin                string            `json:"networkPlugin,omitempty"`
	PrivateAzureRegistryServer   string            `json:"privateAzureRegistryServer,omitempty"`
	AzureCNIURLLinux             string            `json:"azureCNIURLLinux,omitempty"`
	AzureCNIURLWindows           string            `json:"azureCNIURLWindows,omitempty"`
	Addons                       []KubernetesAddon `json:"addons,omitempty"`
	NetworkPolicy                string            `json:"networkPolicy,omitempty"`
	CloudProviderRateLimitQPS    float64           `json:"cloudProviderRateLimitQPS,omitempty"`
	CloudProviderRateLimitBucket int               `json:"cloudProviderRateLimitBucket,omitempty"`
	APIServerConfig              map[string]string `json:"apiServerConfig,omitempty"`
	KubernetesImageBase          string            `json:"kubernetesImageBase,omitempty"`
	ControllerManagerConfig      map[string]string `json:"controllerManagerConfig,omitempty"`
	KubeletConfig                map[string]string `json:"kubeletConfig,omitempty"`
	KubeProxyMode                string            `json:"kubeProxyMode,omitempty"`
	LoadBalancerSku              string            `json:"loadBalancerSku,omitempty"`
	ExcludeMasterFromStandardLB  *bool             `json:"excludeMasterFromStandardLB,omitempty"`
	ServiceCidr                  string            `json:"serviceCidr,omitempty"`
	DNSServiceIP                 string            `json:"dnsServiceIP,omitempty"`
}

type OrchestratorProfile struct {
	OrchestratorType    string            `json:"orchestratorType"`
	OrchestratorRelease string            `json:"orchestratorRelease"`
	KubernetesConfig    *KubernetesConfig `json:"kubernetesConfig,omitempty"`
}

type MasterProfile struct {
	Count          int                 `json:"count"`
	Distro         string              `json:"distro"`
	DNSPrefix      string              `json:"dnsPrefix"`
	VMSize         string              `json:"vmSize" validate:"required"`
	IPAddressCount int                 `json:"ipAddressCount,omitempty"`
	Extensions     []map[string]string `json:"extensions,omitempty"`
	OSDiskSizeGB   int                 `json:"osDiskSizeGB,omitempty" validate:"min=0,max=1023"`
}

type AgentPoolProfile struct {
	Name                   string              `json:"name"`
	Count                  int                 `json:"count"`
	Distro                 string              `json:"distro"`
	VMSize                 string              `json:"vmSize"`
	OSType                 string              `json:"osType,omitempty"`
	AvailabilityProfile    string              `json:"availabilityProfile"`
	IPAddressCount         int                 `json:"ipAddressCount,omitempty"`
	PreProvisionExtension  map[string]string   `json:"preProvisionExtension,omitempty"`
	Extensions             []map[string]string `json:"extensions,omitempty"`
	OSDiskSizeGB           int                 `json:"osDiskSizeGB,omitempty" validate:"min=0,max=1023"`
	EnableVMSSNodePublicIP bool                `json:"enableVMSSNodePublicIP,omitempty"`
}

type AzureClient struct {
	environment       azure.Environment
	subscriptionID    string
	deploymentsClient resources.DeploymentsClient
	groupsClient      resources.GroupsClient
}

type FeatureFlags struct {
	EnableIPv6DualStack bool `json:"enableIPv6DualStack,omitempty"`
}

// CustomCloudProfile defines configuration for custom cloud profile( for ex: Azure Stack)
type CustomCloudProfile struct {
	PortalURL string `json:"portalURL,omitempty"`
}

// AzureStackMetadataEndpoints defines configuration for Azure Stack
type AzureStackMetadataEndpoints struct {
	GalleryEndpoint string                            `json:"galleryEndpoint,omitempty"`
	GraphEndpoint   string                            `json:"graphEndpoint,omitempty"`
	PortalEndpoint  string                            `json:"portalEndpoint,omitempty"`
	Authentication  *AzureStackMetadataAuthentication `json:"authentication,omitempty"`
}

// AzureStackMetadataAuthentication defines configuration for Azure Stack
type AzureStackMetadataAuthentication struct {
	LoginEndpoint string   `json:"loginEndpoint,omitempty"`
	Audiences     []string `json:"audiences,omitempty"`
}

func (az *AzureClient) ValidateDeployment(ctx context.Context, resourceGroupName, deploymentName string, template, params *map[string]interface{}) (valid resources.DeploymentValidateResult, err error) {
	return az.deploymentsClient.Validate(ctx,
		resourceGroupName,
		deploymentName,
		resources.Deployment{
			Properties: &resources.DeploymentProperties{
				Template:   template,
				Parameters: params,
				Mode:       resources.Incremental,
			},
		})
}

func (az *AzureClient) DeployTemplate(ctx context.Context, resourceGroupName, deploymentName string, template, parameters *map[string]interface{}) (de resources.DeploymentExtended, err error) {
	future, err := az.deploymentsClient.CreateOrUpdate(
		ctx,
		resourceGroupName,
		deploymentName,
		resources.Deployment{
			Properties: &resources.DeploymentProperties{
				Template:   template,
				Parameters: parameters,
				Mode:       resources.Incremental,
			},
		})
	if err != nil {
		return de, fmt.Errorf("cannot create deployment: %v", err)
	}

	err = future.WaitForCompletionRef(ctx, az.deploymentsClient.Client)
	if err != nil {
		return de, fmt.Errorf("cannot get the create deployment future response: %v", err)
	}

	return future.Result(az.deploymentsClient)
}

func (az *AzureClient) EnsureResourceGroup(ctx context.Context, name, location string, managedBy *string) (resourceGroup *resources.Group, err error) {
	var tags map[string]*string
	group, err := az.groupsClient.Get(ctx, name)
	if err == nil && group.Tags != nil {
		tags = group.Tags
	} else {
		tags = make(map[string]*string)
	}
	// Tags for correlating resource groups with prow jobs on testgrid
	tags["buildID"] = stringPointer(os.Getenv("BUILD_ID"))
	tags["jobName"] = stringPointer(os.Getenv("JOB_NAME"))
	tags["creationTimestamp"] = stringPointer(time.Now().UTC().Format(time.RFC3339))

	response, err := az.groupsClient.CreateOrUpdate(ctx, name, resources.Group{
		Name:      &name,
		Location:  &location,
		ManagedBy: managedBy,
		Tags:      tags,
	})
	if err != nil {
		return &response, err
	}

	return &response, nil
}

func (az *AzureClient) DeleteResourceGroup(ctx context.Context, groupName string) error {
	_, err := az.groupsClient.Get(ctx, groupName)
	if err == nil {
		future, err := az.groupsClient.Delete(ctx, groupName)
		if err != nil {
			return fmt.Errorf("cannot delete resource group %v: %v", groupName, err)
		}
		err = future.WaitForCompletionRef(ctx, az.groupsClient.Client)
		if err != nil {
			// Skip the teardown errors because of https://github.com/Azure/go-autorest/issues/357
			// TODO(feiskyer): fix the issue by upgrading go-autorest version >= v11.3.2.
			log.Printf("Warning: failed to delete resource group %q with error %v", groupName, err)
		}
	}
	return nil
}

func getOAuthConfig(env azure.Environment, subscriptionID, tenantID string) (*adal.OAuthConfig, error) {

	oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID)
	if err != nil {
		return nil, err
	}

	return oauthConfig, nil
}

func getAzureClient(env azure.Environment, subscriptionID, clientID, tenantID, clientSecret string) (*AzureClient, error) {
	oauthConfig, err := getOAuthConfig(env, subscriptionID, tenantID)
	if err != nil {
		return nil, err
	}

	armSpt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, tenantID, armSpt), nil
}

func getClient(env azure.Environment, subscriptionID, tenantID string, armSpt *adal.ServicePrincipalToken) *AzureClient {
	c := &AzureClient{
		environment:    env,
		subscriptionID: subscriptionID,

		deploymentsClient: resources.NewDeploymentsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		groupsClient:      resources.NewGroupsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
	}

	authorizer := autorest.NewBearerAuthorizer(armSpt)
	c.deploymentsClient.Authorizer = authorizer
	c.deploymentsClient.PollingDuration = 60 * time.Minute
	c.groupsClient.Authorizer = authorizer

	return c
}

func stringPointer(s string) *string {
	return &s
}
<commit_msg>Add additional JSON fields for aks-engine API model<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

    http://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 main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	resources "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
)

type AksEngineAPIModel struct {
	Location   string            `json:"location,omitempty"`
	Name       string            `json:"name,omitempty"`
	Tags       map[string]string `json:"tags,omitempty"`
	APIVersion string            `json:"apiVersion"`
	Properties *Properties       `json:"properties"`
}

type Properties struct {
	OrchestratorProfile     *OrchestratorProfile     `json:"orchestratorProfile,omitempty"`
	MasterProfile           *MasterProfile           `json:"masterProfile,omitempty"`
	AgentPoolProfiles       []*AgentPoolProfile      `json:"agentPoolProfiles,omitempty"`
	LinuxProfile            *LinuxProfile            `json:"linuxProfile,omitempty"`
	WindowsProfile          *WindowsProfile          `json:"windowsProfile,omitempty"`
	ServicePrincipalProfile *ServicePrincipalProfile `json:"servicePrincipalProfile,omitempty"`
	ExtensionProfiles       []map[string]string      `json:"extensionProfiles,omitempty"`
	CustomCloudProfile      *CustomCloudProfile      `json:"customCloudProfile,omitempty"`
	FeatureFlags            *FeatureFlags            `json:"featureFlags,omitempty"`
}

type ServicePrincipalProfile struct {
	ClientID string `json:"clientId,omitempty"`
	Secret   string `json:"secret,omitempty"`
}

type LinuxProfile struct {
	AdminUsername string `json:"adminUsername"`
	SSHKeys       *SSH   `json:"ssh"`
}

type SSH struct {
	PublicKeys []PublicKey `json:"publicKeys"`
}

type PublicKey struct {
	KeyData string `json:"keyData"`
}

type WindowsProfile struct {
	AdminUsername         string `json:"adminUsername,omitempty"`
	AdminPassword         string `json:"adminPassword,omitempty"`
	ImageVersion          string `json:"imageVersion,omitempty"`
	WindowsImageSourceURL string `json:"WindowsImageSourceUrl"`
	WindowsPublisher      string `json:"WindowsPublisher"`
	WindowsOffer          string `json:"WindowsOffer"`
	WindowsSku            string `json:"WindowsSku"`
	WindowsDockerVersion  string `json:"windowsDockerVersion"`
	SSHEnabled            bool   `json:"sshEnabled,omitempty"`
}

// KubernetesContainerSpec defines configuration for a container spec
type KubernetesContainerSpec struct {
	Name           string `json:"name,omitempty"`
	Image          string `json:"image,omitempty"`
	CPURequests    string `json:"cpuRequests,omitempty"`
	MemoryRequests string `json:"memoryRequests,omitempty"`
	CPULimits      string `json:"cpuLimits,omitempty"`
	MemoryLimits   string `json:"memoryLimits,omitempty"`
}

// KubernetesAddon defines a list of addons w/ configuration to include with the cluster deployment
type KubernetesAddon struct {
	Name       string                    `json:"name,omitempty"`
	Enabled    *bool                     `json:"enabled,omitempty"`
	Containers []KubernetesContainerSpec `json:"containers,omitempty"`
	Config     map[string]string         `json:"config,omitempty"`
	Data       string                    `json:"data,omitempty"`
}

type KubernetesConfig struct {
	CustomWindowsPackageURL          string            `json:"customWindowsPackageURL,omitempty"`
	CustomHyperkubeImage             string            `json:"customHyperkubeImage,omitempty"`
	CustomCcmImage                   string            `json:"customCcmImage,omitempty"` // Image for cloud-controller-manager
	UseCloudControllerManager        *bool             `json:"useCloudControllerManager,omitempty"`
	NetworkPlugin                    string            `json:"networkPlugin,omitempty"`
	PrivateAzureRegistryServer       string            `json:"privateAzureRegistryServer,omitempty"`
	AzureCNIURLLinux                 string            `json:"azureCNIURLLinux,omitempty"`
	AzureCNIURLWindows               string            `json:"azureCNIURLWindows,omitempty"`
	Addons                           []KubernetesAddon `json:"addons,omitempty"`
	NetworkPolicy                    string            `json:"networkPolicy,omitempty"`
	CloudProviderRateLimitQPS        float64           `json:"cloudProviderRateLimitQPS,omitempty"`
	CloudProviderRateLimitBucket     int               `json:"cloudProviderRateLimitBucket,omitempty"`
	APIServerConfig                  map[string]string `json:"apiServerConfig,omitempty"`
	KubernetesImageBase              string            `json:"kubernetesImageBase,omitempty"`
	ControllerManagerConfig          map[string]string `json:"controllerManagerConfig,omitempty"`
	KubeletConfig                    map[string]string `json:"kubeletConfig,omitempty"`
	KubeProxyMode                    string            `json:"kubeProxyMode,omitempty"`
	LoadBalancerSku                  string            `json:"loadBalancerSku,omitempty"`
	ExcludeMasterFromStandardLB      *bool             `json:"excludeMasterFromStandardLB,omitempty"`
	ServiceCidr                      string            `json:"serviceCidr,omitempty"`
	DNSServiceIP                     string            `json:"dnsServiceIP,omitempty"`
	OutboundRuleIdleTimeoutInMinutes int32             `json:"outboundRuleIdleTimeoutInMinutes,omitempty"`
}

type OrchestratorProfile struct {
	OrchestratorType    string            `json:"orchestratorType"`
	OrchestratorRelease string            `json:"orchestratorRelease"`
	KubernetesConfig    *KubernetesConfig `json:"kubernetesConfig,omitempty"`
}

type MasterProfile struct {
	Count               int                 `json:"count"`
	Distro              string              `json:"distro"`
	DNSPrefix           string              `json:"dnsPrefix"`
	VMSize              string              `json:"vmSize" validate:"required"`
	IPAddressCount      int                 `json:"ipAddressCount,omitempty"`
	Extensions          []map[string]string `json:"extensions,omitempty"`
	OSDiskSizeGB        int                 `json:"osDiskSizeGB,omitempty" validate:"min=0,max=1023"`
	AvailabilityProfile string              `json:"availabilityProfile,omitempty"`
	AvailabilityZones   []string            `json:"availabilityZones,omitempty"`
}

type AgentPoolProfile struct {
	Name                   string              `json:"name"`
	Count                  int                 `json:"count"`
	Distro                 string              `json:"distro"`
	VMSize                 string              `json:"vmSize"`
	OSType                 string              `json:"osType,omitempty"`
	AvailabilityProfile    string              `json:"availabilityProfile"`
	AvailabilityZones      []string            `json:"availabilityZones,omitempty"`
	IPAddressCount         int                 `json:"ipAddressCount,omitempty"`
	PreProvisionExtension  map[string]string   `json:"preProvisionExtension,omitempty"`
	Extensions             []map[string]string `json:"extensions,omitempty"`
	OSDiskSizeGB           int                 `json:"osDiskSizeGB,omitempty" validate:"min=0,max=1023"`
	EnableVMSSNodePublicIP bool                `json:"enableVMSSNodePublicIP,omitempty"`
}

type AzureClient struct {
	environment       azure.Environment
	subscriptionID    string
	deploymentsClient resources.DeploymentsClient
	groupsClient      resources.GroupsClient
}

type FeatureFlags struct {
	EnableIPv6DualStack bool `json:"enableIPv6DualStack,omitempty"`
}

// CustomCloudProfile defines configuration for custom cloud profile( for ex: Azure Stack)
type CustomCloudProfile struct {
	PortalURL string `json:"portalURL,omitempty"`
}

// AzureStackMetadataEndpoints defines configuration for Azure Stack
type AzureStackMetadataEndpoints struct {
	GalleryEndpoint string                            `json:"galleryEndpoint,omitempty"`
	GraphEndpoint   string                            `json:"graphEndpoint,omitempty"`
	PortalEndpoint  string                            `json:"portalEndpoint,omitempty"`
	Authentication  *AzureStackMetadataAuthentication `json:"authentication,omitempty"`
}

// AzureStackMetadataAuthentication defines configuration for Azure Stack
type AzureStackMetadataAuthentication struct {
	LoginEndpoint string   `json:"loginEndpoint,omitempty"`
	Audiences     []string `json:"audiences,omitempty"`
}

func (az *AzureClient) ValidateDeployment(ctx context.Context, resourceGroupName, deploymentName string, template, params *map[string]interface{}) (valid resources.DeploymentValidateResult, err error) {
	return az.deploymentsClient.Validate(ctx,
		resourceGroupName,
		deploymentName,
		resources.Deployment{
			Properties: &resources.DeploymentProperties{
				Template:   template,
				Parameters: params,
				Mode:       resources.Incremental,
			},
		})
}

func (az *AzureClient) DeployTemplate(ctx context.Context, resourceGroupName, deploymentName string, template, parameters *map[string]interface{}) (de resources.DeploymentExtended, err error) {
	future, err := az.deploymentsClient.CreateOrUpdate(
		ctx,
		resourceGroupName,
		deploymentName,
		resources.Deployment{
			Properties: &resources.DeploymentProperties{
				Template:   template,
				Parameters: parameters,
				Mode:       resources.Incremental,
			},
		})
	if err != nil {
		return de, fmt.Errorf("cannot create deployment: %v", err)
	}

	err = future.WaitForCompletionRef(ctx, az.deploymentsClient.Client)
	if err != nil {
		return de, fmt.Errorf("cannot get the create deployment future response: %v", err)
	}

	return future.Result(az.deploymentsClient)
}

func (az *AzureClient) EnsureResourceGroup(ctx context.Context, name, location string, managedBy *string) (resourceGroup *resources.Group, err error) {
	var tags map[string]*string
	group, err := az.groupsClient.Get(ctx, name)
	if err == nil && group.Tags != nil {
		tags = group.Tags
	} else {
		tags = make(map[string]*string)
	}
	// Tags for correlating resource groups with prow jobs on testgrid
	tags["buildID"] = stringPointer(os.Getenv("BUILD_ID"))
	tags["jobName"] = stringPointer(os.Getenv("JOB_NAME"))
	tags["creationTimestamp"] = stringPointer(time.Now().UTC().Format(time.RFC3339))

	response, err := az.groupsClient.CreateOrUpdate(ctx, name, resources.Group{
		Name:      &name,
		Location:  &location,
		ManagedBy: managedBy,
		Tags:      tags,
	})
	if err != nil {
		return &response, err
	}

	return &response, nil
}

func (az *AzureClient) DeleteResourceGroup(ctx context.Context, groupName string) error {
	_, err := az.groupsClient.Get(ctx, groupName)
	if err == nil {
		future, err := az.groupsClient.Delete(ctx, groupName)
		if err != nil {
			return fmt.Errorf("cannot delete resource group %v: %v", groupName, err)
		}
		err = future.WaitForCompletionRef(ctx, az.groupsClient.Client)
		if err != nil {
			// Skip the teardown errors because of https://github.com/Azure/go-autorest/issues/357
			// TODO(feiskyer): fix the issue by upgrading go-autorest version >= v11.3.2.
			log.Printf("Warning: failed to delete resource group %q with error %v", groupName, err)
		}
	}
	return nil
}

func getOAuthConfig(env azure.Environment, subscriptionID, tenantID string) (*adal.OAuthConfig, error) {

	oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID)
	if err != nil {
		return nil, err
	}

	return oauthConfig, nil
}

func getAzureClient(env azure.Environment, subscriptionID, clientID, tenantID, clientSecret string) (*AzureClient, error) {
	oauthConfig, err := getOAuthConfig(env, subscriptionID, tenantID)
	if err != nil {
		return nil, err
	}

	armSpt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, tenantID, armSpt), nil
}

func getClient(env azure.Environment, subscriptionID, tenantID string, armSpt *adal.ServicePrincipalToken) *AzureClient {
	c := &AzureClient{
		environment:    env,
		subscriptionID: subscriptionID,

		deploymentsClient: resources.NewDeploymentsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		groupsClient:      resources.NewGroupsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
	}

	authorizer := autorest.NewBearerAuthorizer(armSpt)
	c.deploymentsClient.Authorizer = authorizer
	c.deploymentsClient.PollingDuration = 60 * time.Minute
	c.groupsClient.Authorizer = authorizer

	return c
}

func stringPointer(s string) *string {
	return &s
}
<|endoftext|>


<commit_before>package ipmi

import (
	"net"
	"log"
	"bytes"
	"github.com/rmxymh/infra-ecosphere/bmc"
	"github.com/rmxymh/infra-ecosphere/utils"
	"encoding/binary"
)

// Chassis Network Function
const (
	IPMI_CMD_GET_CHASSIS_CAPABILITIES =	0x00
	IPMI_CMD_GET_CHASSIS_STATUS =		0x01
	IPMI_CMD_CHASSIS_CONTROL =		0x02
	IPMI_CMD_CHASSIS_RESET =		0x03
	IPMI_CMD_CHASSIS_IDENTIFY =		0x04
	IPMI_CMD_SET_CHASSIS_CAPABILITIES =	0x05
	IPMI_CMD_SET_POWER_RESTORE_POLICY =	0x06
	IPMI_CMD_GET_SYSTEM_RESTART_CAUSE =	0x07
	IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS =	0x08
	IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS =	0x09
	IPMI_CMD_GET_POH_COUNTER =		0x0f
)

type IPMI_Chassis_Handler func(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage)

type IPMIChassisHandlerSet struct {
	GetChassisCapabilities	IPMI_Chassis_Handler
	GetChassisStatus	IPMI_Chassis_Handler
	ChassisControl		IPMI_Chassis_Handler
	ChassisReset		IPMI_Chassis_Handler
	ChassisIdentify		IPMI_Chassis_Handler
	SetChassisCapabilities	IPMI_Chassis_Handler
	SetPowerRestorePolicy	IPMI_Chassis_Handler
	GetSystemRestartCause	IPMI_Chassis_Handler
	SetSystemBootOptions	IPMI_Chassis_Handler
	GetSystemBootOptions	IPMI_Chassis_Handler
	GetPOHCounter		IPMI_Chassis_Handler
	Unsupported		IPMI_Chassis_Handler
}

var IPMIChassisHandler IPMIChassisHandlerSet = IPMIChassisHandlerSet{}

func IPMI_CHASSIS_SetHandler(command int, handler IPMI_Chassis_Handler) {
	switch command {
	case IPMI_CMD_GET_CHASSIS_CAPABILITIES:
		IPMIChassisHandler.GetChassisCapabilities = handler
	case IPMI_CMD_GET_CHASSIS_STATUS:
		IPMIChassisHandler.GetChassisStatus = handler
	case IPMI_CMD_CHASSIS_CONTROL:
		IPMIChassisHandler.ChassisControl = handler
	case IPMI_CMD_CHASSIS_RESET:
		IPMIChassisHandler.ChassisReset = handler
	case IPMI_CMD_CHASSIS_IDENTIFY:
		IPMIChassisHandler.ChassisIdentify = handler
	case IPMI_CMD_SET_CHASSIS_CAPABILITIES:
		IPMIChassisHandler.SetChassisCapabilities = handler
	case IPMI_CMD_SET_POWER_RESTORE_POLICY:
		IPMIChassisHandler.SetPowerRestorePolicy = handler
	case IPMI_CMD_GET_SYSTEM_RESTART_CAUSE:
		IPMIChassisHandler.GetSystemRestartCause = handler
	case IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS:
		IPMIChassisHandler.SetSystemBootOptions = handler
	case IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS:
		IPMIChassisHandler.GetSystemBootOptions = handler
	case IPMI_CMD_GET_POH_COUNTER:
		IPMIChassisHandler.GetPOHCounter = handler
	}
}

func init() {
	IPMIChassisHandler.Unsupported = HandleIPMIUnsupportedChassisCommand

	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_CHASSIS_STATUS, HandleIPMIGetChassisStatus)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_CHASSIS_CONTROL, HandleIPMIChassisControl)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS, IPMI_CHASSIS_SetBootOption_DeserializeAndExecute)

	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_CHASSIS_CAPABILITIES, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_CHASSIS_RESET, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_CHASSIS_IDENTIFY, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_SET_CHASSIS_CAPABILITIES, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_SET_POWER_RESTORE_POLICY, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_SYSTEM_RESTART_CAUSE, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_POH_COUNTER, HandleIPMIUnsupportedChassisCommand)
}

// Default Handler Implementation
func HandleIPMIUnsupportedChassisCommand(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	log.Println("      IPMI Chassis: This command is not supported currently, ignore.")
}

type IPMIGetChassisStatusResponse struct {
	CurrentPowerState uint8
	LastPowerEvent uint8
	MiscChassisState uint8
	FrontPanelButtonCapabilities uint8
}

const (
	CHASSIS_POWER_STATE_BITMASK_POWER_ON = 			0x01
	CHASSIS_POWER_STATE_BITMASK_POWER_OVERLOAD =		0x02
	CHASSIS_POWER_STATE_BITMASK_INTERLOCK = 		0x04
	CHASSIS_POWER_STATE_BITMASK_POWER_FAULT =		0x08
	CHASSIS_POWER_STATE_BITMASK_POWER_CONTROL_FAULT =	0x10

	// Bit 5 ~ 6
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_POWER_OFF =	0x00
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_RESTORE =	0x20
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_POWER_UP =	0x40
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_UNKNOWN =	0x60
)

const (
	CHASSIS_LAST_POWER_AC_FAILED =		0x01
	CHASSIS_LAST_POWER_DOWN_OVERLOAD =	0x02
	CHASSIS_LAST_POWER_DOWN_INTERLOCK =	0x04
	CHASSIS_LAST_PWOER_DOWN_FAULT =		0x08
	CHASSIS_LAST_POWER_ON_VIA_IPMI =	0x10
)

const (
	CHASSIS_MISC_INTRUCTION_ACTIVE =	0x01
	CHASSIS_MISC_FRONT_PANEL_LOCKOUT =	0x02
	CHASSIS_MISC_DRIVE_FAULT =		0x04
	CHASSIS_MISC_COOLING_FAULT =		0x08

	// Bit 4 ~ 5
	CHASSIS_MISC_IDENTIFY_OFF =		0x00
	CHASSIS_MISC_IDENTIFY_TEMPERARY =	0x10
	CHASSIS_MISC_IDENTIFY_INDEFINITE_ON =	0x20

	CHASSIS_MISC_IDENTIFY_SUPPORTED =	0x40
)

func HandleIPMIGetChassisStatus(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	session, ok := GetSession(wrapper.SessionId)
	if ! ok {
		log.Printf("Unable to find session 0x%08x\n", wrapper.SessionId)
	} else {
		bmcUser := session.User
		code := GetAuthenticationCode(wrapper.AuthenticationType, bmcUser.Password, wrapper.SessionId, message, wrapper.SequenceNumber)
		if bytes.Compare(wrapper.AuthenticationCode[:], code[:]) == 0 {
			log.Println("      IPMI Authentication Pass.")
		} else {
			log.Println("      IPMI Authentication Failed.")
		}

		localIP := utils.GetLocalIP(server)
		bmc, ok := bmc.GetBMC(net.ParseIP(localIP))
		if ! ok {
			log.Printf("BMC %s is not found\n", localIP)
		} else {
			session.Inc()

			response := IPMIGetChassisStatusResponse{}
			if bmc.VM.IsRunning() {
				response.CurrentPowerState |= CHASSIS_POWER_STATE_BITMASK_POWER_ON
			}
			response.LastPowerEvent = 0
			response.MiscChassisState = 0
			response.FrontPanelButtonCapabilities = 0

			dataBuf := bytes.Buffer{}
			binary.Write(&dataBuf, binary.LittleEndian, response)

			responseWrapper, responseMessage := BuildResponseMessageTemplate(wrapper, message, (IPMI_NETFN_CHASSIS | IPMI_NETFN_RESPONSE), IPMI_CMD_GET_CHASSIS_STATUS)
			responseMessage.Data = dataBuf.Bytes()

			responseWrapper.SessionId = wrapper.SessionId
			responseWrapper.SequenceNumber = session.RemoteSessionSequenceNumber
			rmcp := BuildUpRMCPForIPMI()

			obuf := bytes.Buffer{}
			SerializeRMCP(&obuf, rmcp)
			SerializeIPMI(&obuf, responseWrapper, responseMessage, bmcUser.Password)
			server.WriteToUDP(obuf.Bytes(), addr)
		}
	}
}

type IPMIChassisControlRequest struct {
	ChassisControl uint8
}

const (
	CHASSIS_CONTROL_POWER_DOWN =	0x00
	CHASSIS_CONTROL_POWER_UP =	0x01
	CHASSIS_CONTROL_POWER_CYCLE =	0x02
	CHASSIS_CONTROL_HARD_RESET =	0x03
	CHASSIS_CONTROL_PULSE = 	0x04
	CHASSIS_CONTROL_POWER_SOFT =	0x05
)

func HandleIPMIChassisControl(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	buf := bytes.NewBuffer(message.Data)
	request := IPMIChassisControlRequest{}
	binary.Read(buf, binary.LittleEndian, &request)

	session, ok := GetSession(wrapper.SessionId)
	if ! ok {
		log.Printf("Unable to find session 0x%08x\n", wrapper.SessionId)
	} else {
		bmcUser := session.User
		code := GetAuthenticationCode(wrapper.AuthenticationType, bmcUser.Password, wrapper.SessionId, message, wrapper.SequenceNumber)
		if bytes.Compare(wrapper.AuthenticationCode[:], code[:]) == 0 {
			log.Println("      IPMI Authentication Pass.")
		} else {
			log.Println("      IPMI Authentication Failed.")
		}

		localIP := utils.GetLocalIP(server)
		bmc, ok := bmc.GetBMC(net.ParseIP(localIP))
		if ! ok {
			log.Printf("BMC %s is not found\n", localIP)
		} else {
			switch request.ChassisControl {
			case CHASSIS_CONTROL_POWER_DOWN:
				bmc.PowerOff()
			case CHASSIS_CONTROL_POWER_UP:
				bmc.PowerOn()
			case CHASSIS_CONTROL_POWER_CYCLE:
				bmc.PowerOff()
				bmc.PowerOn()
			case CHASSIS_CONTROL_HARD_RESET:
				bmc.PowerOff()
				bmc.PowerOn()
			case CHASSIS_CONTROL_PULSE:
				// do nothing
			case CHASSIS_CONTROL_POWER_SOFT:
				bmc.PowerSoft()
			}

			session.Inc()

			responseWrapper, responseMessage := BuildResponseMessageTemplate(wrapper, message, (IPMI_NETFN_APP | IPMI_NETFN_RESPONSE), IPMI_CMD_CHASSIS_CONTROL)

			responseWrapper.SessionId = wrapper.SessionId
			responseWrapper.SequenceNumber = session.RemoteSessionSequenceNumber
			rmcp := BuildUpRMCPForIPMI()

			obuf := bytes.Buffer{}
			SerializeRMCP(&obuf, rmcp)
			SerializeIPMI(&obuf, responseWrapper, responseMessage, bmcUser.Password)
			server.WriteToUDP(obuf.Bytes(), addr)
		}
	}
}

func IPMI_CHASSIS_DeserializeAndExecute(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	switch message.Command {
	case IPMI_CMD_GET_CHASSIS_CAPABILITIES:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_CHASSIS_CAPABILITIES")
		IPMIChassisHandler.GetChassisCapabilities(addr, server, wrapper, message)

	case IPMI_CMD_GET_CHASSIS_STATUS:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_CHASSIS_STATUS")
		IPMIChassisHandler.GetChassisStatus(addr, server, wrapper, message)

	case IPMI_CMD_CHASSIS_CONTROL:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_CHASSIS_CONTROL")
		IPMIChassisHandler.ChassisControl(addr, server, wrapper, message)

	case IPMI_CMD_CHASSIS_RESET:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_CHASSIS_RESET")
		IPMIChassisHandler.ChassisReset(addr, server, wrapper, message)

	case IPMI_CMD_CHASSIS_IDENTIFY:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_CHASSIS_IDENTIFY")
		IPMIChassisHandler.ChassisIdentify(addr, server, wrapper, message)

	case IPMI_CMD_SET_CHASSIS_CAPABILITIES:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_SET_CHASSIS_CAPABILITIES")
		IPMIChassisHandler.SetChassisCapabilities(addr, server, wrapper, message)

	case IPMI_CMD_SET_POWER_RESTORE_POLICY:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_SET_POWER_RESTORE_POLICY")
		IPMIChassisHandler.SetPowerRestorePolicy(addr, server, wrapper, message)

	case IPMI_CMD_GET_SYSTEM_RESTART_CAUSE:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_SYSTEM_RESTART_CAUSE")
		IPMIChassisHandler.GetSystemRestartCause(addr, server, wrapper, message)

	case IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS")
		IPMIChassisHandler.SetSystemBootOptions(addr, server, wrapper, message)

	case IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS")
		IPMIChassisHandler.GetSystemBootOptions(addr, server, wrapper, message)

	case IPMI_CMD_GET_POH_COUNTER:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_POH_COUNTER")
		IPMIChassisHandler.GetPOHCounter(addr, server, wrapper, message)

	}
}

<commit_msg>Add missed handler.<commit_after>package ipmi

import (
	"net"
	"log"
	"bytes"
	"github.com/rmxymh/infra-ecosphere/bmc"
	"github.com/rmxymh/infra-ecosphere/utils"
	"encoding/binary"
)

// Chassis Network Function
const (
	IPMI_CMD_GET_CHASSIS_CAPABILITIES =	0x00
	IPMI_CMD_GET_CHASSIS_STATUS =		0x01
	IPMI_CMD_CHASSIS_CONTROL =		0x02
	IPMI_CMD_CHASSIS_RESET =		0x03
	IPMI_CMD_CHASSIS_IDENTIFY =		0x04
	IPMI_CMD_SET_CHASSIS_CAPABILITIES =	0x05
	IPMI_CMD_SET_POWER_RESTORE_POLICY =	0x06
	IPMI_CMD_GET_SYSTEM_RESTART_CAUSE =	0x07
	IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS =	0x08
	IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS =	0x09
	IPMI_CMD_GET_POH_COUNTER =		0x0f
)

type IPMI_Chassis_Handler func(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage)

type IPMIChassisHandlerSet struct {
	GetChassisCapabilities	IPMI_Chassis_Handler
	GetChassisStatus	IPMI_Chassis_Handler
	ChassisControl		IPMI_Chassis_Handler
	ChassisReset		IPMI_Chassis_Handler
	ChassisIdentify		IPMI_Chassis_Handler
	SetChassisCapabilities	IPMI_Chassis_Handler
	SetPowerRestorePolicy	IPMI_Chassis_Handler
	GetSystemRestartCause	IPMI_Chassis_Handler
	SetSystemBootOptions	IPMI_Chassis_Handler
	GetSystemBootOptions	IPMI_Chassis_Handler
	GetPOHCounter		IPMI_Chassis_Handler
	Unsupported		IPMI_Chassis_Handler
}

var IPMIChassisHandler IPMIChassisHandlerSet = IPMIChassisHandlerSet{}

func IPMI_CHASSIS_SetHandler(command int, handler IPMI_Chassis_Handler) {
	switch command {
	case IPMI_CMD_GET_CHASSIS_CAPABILITIES:
		IPMIChassisHandler.GetChassisCapabilities = handler
	case IPMI_CMD_GET_CHASSIS_STATUS:
		IPMIChassisHandler.GetChassisStatus = handler
	case IPMI_CMD_CHASSIS_CONTROL:
		IPMIChassisHandler.ChassisControl = handler
	case IPMI_CMD_CHASSIS_RESET:
		IPMIChassisHandler.ChassisReset = handler
	case IPMI_CMD_CHASSIS_IDENTIFY:
		IPMIChassisHandler.ChassisIdentify = handler
	case IPMI_CMD_SET_CHASSIS_CAPABILITIES:
		IPMIChassisHandler.SetChassisCapabilities = handler
	case IPMI_CMD_SET_POWER_RESTORE_POLICY:
		IPMIChassisHandler.SetPowerRestorePolicy = handler
	case IPMI_CMD_GET_SYSTEM_RESTART_CAUSE:
		IPMIChassisHandler.GetSystemRestartCause = handler
	case IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS:
		IPMIChassisHandler.SetSystemBootOptions = handler
	case IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS:
		IPMIChassisHandler.GetSystemBootOptions = handler
	case IPMI_CMD_GET_POH_COUNTER:
		IPMIChassisHandler.GetPOHCounter = handler
	}
}

func init() {
	IPMIChassisHandler.Unsupported = HandleIPMIUnsupportedChassisCommand

	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_CHASSIS_STATUS, HandleIPMIGetChassisStatus)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_CHASSIS_CONTROL, HandleIPMIChassisControl)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS, IPMI_CHASSIS_SetBootOption_DeserializeAndExecute)

	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_CHASSIS_CAPABILITIES, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_CHASSIS_RESET, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_CHASSIS_IDENTIFY, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_SET_CHASSIS_CAPABILITIES, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_SET_POWER_RESTORE_POLICY, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_SYSTEM_RESTART_CAUSE, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS, HandleIPMIUnsupportedChassisCommand)
	IPMI_CHASSIS_SetHandler(IPMI_CMD_GET_POH_COUNTER, HandleIPMIUnsupportedChassisCommand)
}

// Default Handler Implementation
func HandleIPMIUnsupportedChassisCommand(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	log.Println("      IPMI Chassis: This command is not supported currently, ignore.")
}

type IPMIGetChassisStatusResponse struct {
	CurrentPowerState uint8
	LastPowerEvent uint8
	MiscChassisState uint8
	FrontPanelButtonCapabilities uint8
}

const (
	CHASSIS_POWER_STATE_BITMASK_POWER_ON = 			0x01
	CHASSIS_POWER_STATE_BITMASK_POWER_OVERLOAD =		0x02
	CHASSIS_POWER_STATE_BITMASK_INTERLOCK = 		0x04
	CHASSIS_POWER_STATE_BITMASK_POWER_FAULT =		0x08
	CHASSIS_POWER_STATE_BITMASK_POWER_CONTROL_FAULT =	0x10

	// Bit 5 ~ 6
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_POWER_OFF =	0x00
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_RESTORE =	0x20
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_POWER_UP =	0x40
	CHASSIS_POWER_STATE_BITMASK_POWER_RESTORE_UNKNOWN =	0x60
)

const (
	CHASSIS_LAST_POWER_AC_FAILED =		0x01
	CHASSIS_LAST_POWER_DOWN_OVERLOAD =	0x02
	CHASSIS_LAST_POWER_DOWN_INTERLOCK =	0x04
	CHASSIS_LAST_PWOER_DOWN_FAULT =		0x08
	CHASSIS_LAST_POWER_ON_VIA_IPMI =	0x10
)

const (
	CHASSIS_MISC_INTRUCTION_ACTIVE =	0x01
	CHASSIS_MISC_FRONT_PANEL_LOCKOUT =	0x02
	CHASSIS_MISC_DRIVE_FAULT =		0x04
	CHASSIS_MISC_COOLING_FAULT =		0x08

	// Bit 4 ~ 5
	CHASSIS_MISC_IDENTIFY_OFF =		0x00
	CHASSIS_MISC_IDENTIFY_TEMPERARY =	0x10
	CHASSIS_MISC_IDENTIFY_INDEFINITE_ON =	0x20

	CHASSIS_MISC_IDENTIFY_SUPPORTED =	0x40
)

func HandleIPMIGetChassisStatus(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	session, ok := GetSession(wrapper.SessionId)
	if ! ok {
		log.Printf("Unable to find session 0x%08x\n", wrapper.SessionId)
	} else {
		bmcUser := session.User
		code := GetAuthenticationCode(wrapper.AuthenticationType, bmcUser.Password, wrapper.SessionId, message, wrapper.SequenceNumber)
		if bytes.Compare(wrapper.AuthenticationCode[:], code[:]) == 0 {
			log.Println("      IPMI Authentication Pass.")
		} else {
			log.Println("      IPMI Authentication Failed.")
		}

		localIP := utils.GetLocalIP(server)
		bmc, ok := bmc.GetBMC(net.ParseIP(localIP))
		if ! ok {
			log.Printf("BMC %s is not found\n", localIP)
		} else {
			session.Inc()

			response := IPMIGetChassisStatusResponse{}
			if bmc.VM.IsRunning() {
				response.CurrentPowerState |= CHASSIS_POWER_STATE_BITMASK_POWER_ON
			}
			response.LastPowerEvent = 0
			response.MiscChassisState = 0
			response.FrontPanelButtonCapabilities = 0

			dataBuf := bytes.Buffer{}
			binary.Write(&dataBuf, binary.LittleEndian, response)

			responseWrapper, responseMessage := BuildResponseMessageTemplate(wrapper, message, (IPMI_NETFN_CHASSIS | IPMI_NETFN_RESPONSE), IPMI_CMD_GET_CHASSIS_STATUS)
			responseMessage.Data = dataBuf.Bytes()

			responseWrapper.SessionId = wrapper.SessionId
			responseWrapper.SequenceNumber = session.RemoteSessionSequenceNumber
			rmcp := BuildUpRMCPForIPMI()

			obuf := bytes.Buffer{}
			SerializeRMCP(&obuf, rmcp)
			SerializeIPMI(&obuf, responseWrapper, responseMessage, bmcUser.Password)
			server.WriteToUDP(obuf.Bytes(), addr)
		}
	}
}

type IPMIChassisControlRequest struct {
	ChassisControl uint8
}

const (
	CHASSIS_CONTROL_POWER_DOWN =	0x00
	CHASSIS_CONTROL_POWER_UP =	0x01
	CHASSIS_CONTROL_POWER_CYCLE =	0x02
	CHASSIS_CONTROL_HARD_RESET =	0x03
	CHASSIS_CONTROL_PULSE = 	0x04
	CHASSIS_CONTROL_POWER_SOFT =	0x05
)

func HandleIPMIChassisControl(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	buf := bytes.NewBuffer(message.Data)
	request := IPMIChassisControlRequest{}
	binary.Read(buf, binary.LittleEndian, &request)

	session, ok := GetSession(wrapper.SessionId)
	if ! ok {
		log.Printf("Unable to find session 0x%08x\n", wrapper.SessionId)
	} else {
		bmcUser := session.User
		code := GetAuthenticationCode(wrapper.AuthenticationType, bmcUser.Password, wrapper.SessionId, message, wrapper.SequenceNumber)
		if bytes.Compare(wrapper.AuthenticationCode[:], code[:]) == 0 {
			log.Println("      IPMI Authentication Pass.")
		} else {
			log.Println("      IPMI Authentication Failed.")
		}

		localIP := utils.GetLocalIP(server)
		bmc, ok := bmc.GetBMC(net.ParseIP(localIP))
		if ! ok {
			log.Printf("BMC %s is not found\n", localIP)
		} else {
			switch request.ChassisControl {
			case CHASSIS_CONTROL_POWER_DOWN:
				bmc.PowerOff()
			case CHASSIS_CONTROL_POWER_UP:
				bmc.PowerOn()
			case CHASSIS_CONTROL_POWER_CYCLE:
				bmc.PowerOff()
				bmc.PowerOn()
			case CHASSIS_CONTROL_HARD_RESET:
				bmc.PowerOff()
				bmc.PowerOn()
			case CHASSIS_CONTROL_PULSE:
				// do nothing
			case CHASSIS_CONTROL_POWER_SOFT:
				bmc.PowerSoft()
			}

			session.Inc()

			responseWrapper, responseMessage := BuildResponseMessageTemplate(wrapper, message, (IPMI_NETFN_APP | IPMI_NETFN_RESPONSE), IPMI_CMD_CHASSIS_CONTROL)

			responseWrapper.SessionId = wrapper.SessionId
			responseWrapper.SequenceNumber = session.RemoteSessionSequenceNumber
			rmcp := BuildUpRMCPForIPMI()

			obuf := bytes.Buffer{}
			SerializeRMCP(&obuf, rmcp)
			SerializeIPMI(&obuf, responseWrapper, responseMessage, bmcUser.Password)
			server.WriteToUDP(obuf.Bytes(), addr)
		}
	}
}

func IPMI_CHASSIS_DeserializeAndExecute(addr *net.UDPAddr, server *net.UDPConn, wrapper IPMISessionWrapper, message IPMIMessage) {
	switch message.Command {
	case IPMI_CMD_GET_CHASSIS_CAPABILITIES:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_CHASSIS_CAPABILITIES")
		IPMIChassisHandler.GetChassisCapabilities(addr, server, wrapper, message)

	case IPMI_CMD_GET_CHASSIS_STATUS:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_CHASSIS_STATUS")
		IPMIChassisHandler.GetChassisStatus(addr, server, wrapper, message)

	case IPMI_CMD_CHASSIS_CONTROL:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_CHASSIS_CONTROL")
		IPMIChassisHandler.ChassisControl(addr, server, wrapper, message)

	case IPMI_CMD_CHASSIS_RESET:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_CHASSIS_RESET")
		IPMIChassisHandler.ChassisReset(addr, server, wrapper, message)

	case IPMI_CMD_CHASSIS_IDENTIFY:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_CHASSIS_IDENTIFY")
		IPMIChassisHandler.ChassisIdentify(addr, server, wrapper, message)

	case IPMI_CMD_SET_CHASSIS_CAPABILITIES:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_SET_CHASSIS_CAPABILITIES")
		IPMIChassisHandler.SetChassisCapabilities(addr, server, wrapper, message)

	case IPMI_CMD_SET_POWER_RESTORE_POLICY:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_SET_POWER_RESTORE_POLICY")
		IPMIChassisHandler.SetPowerRestorePolicy(addr, server, wrapper, message)

	case IPMI_CMD_GET_SYSTEM_RESTART_CAUSE:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_SYSTEM_RESTART_CAUSE")
		IPMIChassisHandler.GetSystemRestartCause(addr, server, wrapper, message)

	case IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_SET_SYSTEM_BOOT_OPTIONS")
		IPMIChassisHandler.SetSystemBootOptions(addr, server, wrapper, message)

	case IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_SYSTEM_BOOT_OPTIONS")
		IPMIChassisHandler.GetSystemBootOptions(addr, server, wrapper, message)

	case IPMI_CMD_GET_POH_COUNTER:
		log.Println("      IPMI CHASSIS: Command = IPMI_CMD_GET_POH_COUNTER")
		IPMIChassisHandler.GetPOHCounter(addr, server, wrapper, message)

	}
}

<|endoftext|>


<commit_before>// Copyright (C) 2016 Space Monkey, Inc.
//
// 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
//
//   http://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 xform

import (
	"fmt"
	"strings"

	"gopkg.in/spacemonkeygo/dbx.v1/ir"
)

func DefaultIndexName(i *ir.Index) string {
	parts := []string{i.Model.Table}
	for _, field := range i.Fields {
		parts = append(parts, field.Column)
	}
	if i.Unique {
		parts = append(parts, "unique")
	}
	parts = append(parts, "index")
	return strings.Join(parts, "_")
}

func DefaultCreateSuffix(cre *ir.Create) []string {
	var parts []string
	parts = append(parts, cre.Model.Name)
	return parts
}

func DefaultReadSuffix(read *ir.Read) []string {
	var parts []string
	for _, selectable := range read.Selectables {
		switch obj := selectable.(type) {
		case *ir.Model:
			parts = append(parts, obj.Name)
		case *ir.Field:
			parts = append(parts, obj.Model.Name)
			parts = append(parts, obj.Name)
		default:
			panic(fmt.Sprintf("unhandled selectable %T", selectable))
		}
	}
	parts = append(parts, whereSuffix(read.Where, len(read.Joins) > 0)...)
	return parts
}

func DefaultUpdateSuffix(upd *ir.Update) []string {
	var parts []string
	parts = append(parts, upd.Model.Name)
	parts = append(parts, whereSuffix(upd.Where, len(upd.Joins) > 0)...)
	return parts
}

func DefaultDeleteSuffix(del *ir.Delete) []string {
	var parts []string
	parts = append(parts, del.Model.Name)
	parts = append(parts, whereSuffix(del.Where, len(del.Joins) > 0)...)
	return parts
}

func whereSuffix(wheres []*ir.Where, full bool) (parts []string) {
	if len(wheres) == 0 {
		return nil
	}
	parts = append(parts, "by")
	for i, where := range wheres {
		if where.Right != nil {
			continue
		}
		if i > 0 {
			parts = append(parts, "and")
		}
		if full {
			parts = append(parts, where.Left.Model.Name)
		}
		parts = append(parts, where.Left.Name)
		if suffix := where.Op.Suffix(); suffix != "" {
			parts = append(parts, suffix)
		}
	}
	return parts
}
<commit_msg>include orderby in suffix<commit_after>// Copyright (C) 2016 Space Monkey, Inc.
//
// 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
//
//   http://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 xform

import (
	"fmt"
	"strings"

	"gopkg.in/spacemonkeygo/dbx.v1/ir"
)

func DefaultIndexName(i *ir.Index) string {
	parts := []string{i.Model.Table}
	for _, field := range i.Fields {
		parts = append(parts, field.Column)
	}
	if i.Unique {
		parts = append(parts, "unique")
	}
	parts = append(parts, "index")
	return strings.Join(parts, "_")
}

func DefaultCreateSuffix(cre *ir.Create) []string {
	var parts []string
	parts = append(parts, cre.Model.Name)
	return parts
}

func DefaultReadSuffix(read *ir.Read) []string {
	var parts []string
	for _, selectable := range read.Selectables {
		switch obj := selectable.(type) {
		case *ir.Model:
			parts = append(parts, obj.Name)
		case *ir.Field:
			parts = append(parts, obj.Model.Name)
			parts = append(parts, obj.Name)
		default:
			panic(fmt.Sprintf("unhandled selectable %T", selectable))
		}
	}
	full := len(read.Joins) > 0
	parts = append(parts, whereSuffix(read.Where, full)...)
	if read.OrderBy != nil {
		parts = append(parts, "order_by")
		if read.OrderBy.Descending {
			parts = append(parts, "desc")
		} else {
			parts = append(parts, "asc")
		}
		for _, field := range read.OrderBy.Fields {
			if full {
				parts = append(parts, field.Model.Name)
			}
			parts = append(parts, field.Name)
		}
	}
	return parts
}

func DefaultUpdateSuffix(upd *ir.Update) []string {
	var parts []string
	parts = append(parts, upd.Model.Name)
	parts = append(parts, whereSuffix(upd.Where, len(upd.Joins) > 0)...)
	return parts
}

func DefaultDeleteSuffix(del *ir.Delete) []string {
	var parts []string
	parts = append(parts, del.Model.Name)
	parts = append(parts, whereSuffix(del.Where, len(del.Joins) > 0)...)
	return parts
}

func whereSuffix(wheres []*ir.Where, full bool) (parts []string) {
	if len(wheres) == 0 {
		return nil
	}
	parts = append(parts, "by")
	for i, where := range wheres {
		if where.Right != nil {
			continue
		}
		if i > 0 {
			parts = append(parts, "and")
		}
		if full {
			parts = append(parts, where.Left.Model.Name)
		}
		parts = append(parts, where.Left.Name)
		if suffix := where.Op.Suffix(); suffix != "" {
			parts = append(parts, suffix)
		}
	}
	return parts
}
<|endoftext|>


<commit_before>package ircstats

import (
	"encoding/json"
	"fmt"
	"sort"
	"strconv"
	"time"
)

//
// This view data struct will contain all the data that will be injected into the view template. Ideally this will be
// done as a JSON export so that JavaScript within the view can transform it in any way it sees fit.
//
type ViewData struct {
	PageTitle       string   // Page title from configuration
	PageDescription string   // Page description from configuration
	JsonData        JsonData // Json data for exporting to page
}

type UserData struct {
	Username           string
	Url                string
	Avatar             string
	FirstSpoke         int64
	LastSpoke          int64
	TotalWords         int64            // Count of words
	Averages           Averages         // Used for words/day
	Vocabulary         int64            // Number of different words used
	Words              map[string]int64 // World cloud
	DaysActiveInPeriod int64
	TotalWordsInPeriod int64
	WordsByDayInPeriod float64
	ActivityPercentage float64 // Overall % contribution to Channel.WordCount
}

type TimeZone struct {
	Name   string
	Offset int
}

func (tz TimeZone) Format() string {
	var output string

	if tz.Offset > 0 {
		output = "GMT +" + strconv.Itoa(tz.Offset)
	} else {
		output = "GMT -" + strconv.Itoa(tz.Offset)
	}
	return output
}

type JsonData struct {
	// Configurable options
	HeatMapInterval uint // HeatMap Interval from configuration
	ActivityPeriod  uint // Activity Period from configuration

	// Dates
	GeneratedAt   int64 // Timestamp of last generated at
	FirstSeen     int64 // Timestamp of first message
	LastSeen      int64 // Timestamp of last message
	TotalDaysSeen int64 // Number of days between FirstSeen and LastSeen
	TimeZone      TimeZone

	// Averages
	Averages Averages // Calculated Averages

	// Counters
	MaxDay           MaxDay  // Calculated Max Day
	MaxHour          MaxHour // Calculated Max Hour
	MaxWeek          MaxWeek // Calculated Max Week
	TotalLines       int64   // Lines parsed in total
	TotalWords       int64   // Total Words (all words multiplied by times used)
	TotalUsers       int64   // Number of unique users
	TotalActiveUsers int64   // Number of active users within activity period (default 30 days)

	// Misc
	Users             map[string]UserData // Users list
	SortedActiveUsers []string            // Sorted Users map by "activity"
	SortedTopUsers    []string            // Sorted Users map by words
}

type sortedMap struct {
	m map[string]int
	s []string
}

func (sm *sortedMap) Len() int {
	return len(sm.m)
}

func (sm *sortedMap) Less(i, j int) bool {
	return sm.m[sm.s[i]] > sm.m[sm.s[j]]
}

func (sm *sortedMap) Swap(i, j int) {
	sm.s[i], sm.s[j] = sm.s[j], sm.s[i]
}

func sortedKeys(m map[string]int) []string {
	sm := new(sortedMap)
	sm.m = m
	sm.s = make([]string, len(m))
	i := 0
	for key, _ := range m {
		sm.s[i] = key
		i++
	}
	sort.Sort(sm)
	return sm.s
}

func NewViewData(c Config) *ViewData {
	j := JsonData{
		HeatMapInterval: c.HeatMapInterval,
		ActivityPeriod:  c.ActivityPeriod,
		GeneratedAt:     time.Now().Unix(),
	}

	// Set timezone data for frontend
	j.TimeZone.Name, j.TimeZone.Offset = time.Now().Zone()
	j.TimeZone.Offset = (j.TimeZone.Offset / 60) / 60 // We want the zone offset in hours

	return &ViewData{
		PageTitle:       c.PageTitle,
		PageDescription: c.PageDescription,
		JsonData:        j,
	}
}

func (j JsonData) Debug() {
	fmt.Println("==================================================")
	fmt.Println("Json Data Debug:")
	fmt.Println("= [ Dates ] ======================================")
	fmt.Printf("First line date\t\t\t\t\t%d\n", j.FirstSeen)
	fmt.Printf("Last line date\t\t\t\t\t%d\n", j.LastSeen)
	fmt.Printf("Total Days Seen:\t\t\t\t%d\n", j.TotalDaysSeen)
	fmt.Println("= [ Averages ] ===================================")
	fmt.Printf("Mean Lines/Hr:\t\t\t\t\t%f\n", j.Averages.Hour)
	fmt.Printf("Mean Lines/Day:\t\t\t\t\t%f\n", j.Averages.Day)
	fmt.Printf("Mean Lines/Week:\t\t\t\t%f\n", j.Averages.Week)
	fmt.Printf("Mean Lines/Week Day:\t\t\t%f\n", j.Averages.WeekDay)
	fmt.Println("= [ Counters ] ===================================")
	fmt.Printf("Total Lines Parsed:\t\t\t\t%d\n", j.TotalLines)
	fmt.Printf("Total Unique Users:\t\t\t\t%d\n", j.TotalUsers)
	fmt.Printf("Users Active in past %d days:\t%d\n", j.ActivityPeriod, j.TotalActiveUsers)

	fmt.Printf("Peak Day Date:\t\t\t\t\t%s\n", j.MaxDay.Day)
	fmt.Printf("Peak Day Lines:\t\t\t\t\t%d\n", j.MaxDay.Lines)

	fmt.Printf("Peak Hour:\t\t\t\t\t\t%d\n", j.MaxHour.Hour)
	fmt.Printf("Peak Hour Lines:\t\t\t\t%d\n", j.MaxHour.Lines)

	fmt.Printf("Peak Week:\t\t\t\t\t\t%d\n", j.MaxWeek.Week)
	fmt.Printf("Peak Week Lines:\t\t\t\t%d\n", j.MaxWeek.Lines)

	fmt.Println("==================================================")
}

// Calculate stats for View
func (vd *ViewData) Calculate(db Database) {
	// Dates
	vd.JsonData.FirstSeen = db.Channel.FirstSeen
	vd.JsonData.LastSeen = db.Channel.LastSeen
	vd.JsonData.TotalDaysSeen = db.Channel.TotalDaysSeen()

	// Calculate Counters
	vd.JsonData.TotalUsers = db.CountUsers()
	vd.JsonData.MaxDay.Day, vd.JsonData.MaxDay.Lines = db.Channel.FindPeakDay()
	vd.JsonData.MaxHour.Hour, vd.JsonData.MaxHour.Lines = db.Channel.FindPeakHour()
	vd.JsonData.MaxWeek.Week, vd.JsonData.MaxWeek.Lines = db.Channel.FindPeakWeek()
	vd.JsonData.TotalLines = db.Channel.LineCount
	vd.JsonData.TotalWords = db.Channel.WordCount

	// Calculate Averages
	vd.JsonData.Averages.Hour = db.Channel.FindHourAverage()
	vd.JsonData.Averages.Week = db.Channel.FindWeekAverage()
	vd.JsonData.Averages.Day = db.Channel.FindDayAverage()

	// Calculate Users
	vd.calculateUsers(db)
}

func (vd *ViewData) calculateUsers(db Database) {
	var (
		timePeriod  map[string]bool
		users       map[string]UserData
		activeUsers map[string]int
		topUsers    map[string]int

		userWordCount  int64
		userDaysActive int64
	)

	timePeriod = make(map[string]bool)
	activeUsers = make(map[string]int)
	topUsers = make(map[string]int)
	users = make(map[string]UserData)

	for i := 1; i < int(vd.JsonData.ActivityPeriod); i++ {
		timePeriod[time.Now().AddDate(0, 0, -i).Format("2006-02-01")] = true
	}

	for nick, u := range db.Users {
		userWordCount = 0
		userDaysActive = 0

		for timePeriodDate := range timePeriod {
			if _, ok := u.Days[timePeriodDate]; ok {
				userDaysActive++
				userWordCount += u.Days[timePeriodDate]
			}
		}

		viewUserData := UserData{
			Username:           u.Username,
			Url:                u.Url,
			Avatar:             u.Avatar,
			FirstSpoke:         u.FirstSeen,
			LastSpoke:          u.LastSeen,
			TotalWords:         u.WordCount,
			Vocabulary:         int64(len(u.Words)),
			Words:              u.Words,
			DaysActiveInPeriod: userDaysActive,
			TotalWordsInPeriod: userWordCount,
			ActivityPercentage: (float64(u.WordCount) / float64(db.Channel.WordCount)) * 100,
		}

		viewUserData.Averages.Hour = u.FindHourAverage()
		viewUserData.Averages.Week = u.FindWeekAverage()
		viewUserData.Averages.Day = u.FindDayAverage()

		if userDaysActive > 0 {
			viewUserData.WordsByDayInPeriod = float64(userWordCount) / float64(userDaysActive)
			activeUsers[nick] = int(userDaysActive)
		}
		topUsers[nick] = int(viewUserData.TotalWords)
		users[nick] = viewUserData
	}

	vd.JsonData.Users = users
	vd.JsonData.SortedTopUsers = sortedKeys(topUsers)
	vd.JsonData.SortedActiveUsers = sortedKeys(activeUsers)
	vd.JsonData.TotalActiveUsers = int64(len(vd.JsonData.SortedActiveUsers))
}

// Returns a json string of the JsonData, good for debugging.
func (vd ViewData) GetJsonString() (j []byte, err error) {
	j, err = json.Marshal(vd.JsonData)
	return
}
<commit_msg>Attempting to make data ready for graphs<commit_after>package ircstats

import (
	"encoding/json"
	"fmt"
	"sort"
	"strconv"
	"time"
)

//
// This view data struct will contain all the data that will be injected into the view template. Ideally this will be
// done as a JSON export so that JavaScript within the view can transform it in any way it sees fit.
//
type ViewData struct {
	PageTitle       string   // Page title from configuration
	PageDescription string   // Page description from configuration
	JsonData        JsonData // Json data for exporting to page
}

// Data mapping for front end JSON
type UserData struct {
	Username           string
	Url                string
	Avatar             string
	FirstSpoke         int64
	LastSpoke          int64
	TotalWords         int64            // Count of words
	Averages           Averages         // Used for words/day
	Vocabulary         int64            // Number of different words used
	Words              map[string]int64 // World cloud
	DaysActiveInPeriod int64
	TotalWordsInPeriod int64
	WordsByDayInPeriod float64
	ActivityPercentage float64          // Overall % contribution to Channel.WordCount
}

// Data mapping for passing timezone data to front end JSON
type TimeZone struct {
	Name   string
	Offset int
}

// Data mapping for passing date data to front end JSON
type GraphDay struct {
	Date  string
	Value int64
}

func (tz TimeZone) Format() string {
	var output string

	if tz.Offset > 0 {
		output = "GMT +" + strconv.Itoa(tz.Offset)
	} else {
		output = "GMT -" + strconv.Itoa(tz.Offset)
	}
	return output
}

type JsonData struct {
					      // Configurable options
	HeatMapInterval   uint                // HeatMap Interval from configuration
	ActivityPeriod    uint                // Activity Period from configuration

					      // Dates
	GeneratedAt       int64               // Timestamp of last generated at
	FirstSeen         int64               // Timestamp of first message
	LastSeen          int64               // Timestamp of last message
	TotalDaysSeen     int64               // Number of days between FirstSeen and LastSeen
	TimeZone          TimeZone

					      // Averages
	Averages          Averages            // Calculated Averages

					      // Counters
	MaxDay            MaxDay              // Calculated Max Day
	MaxHour           MaxHour             // Calculated Max Hour
	MaxWeek           MaxWeek             // Calculated Max Week
	TotalLines        int64               // Lines parsed in total
	TotalWords        int64               // Total Words (all words multiplied by times used)
	TotalUsers        int64               // Number of unique users
	TotalActiveUsers  int64               // Number of active users within activity period (default 30 days)

					      // Graph Data
	Days              []GraphDay

					      // Misc
	Users             map[string]UserData // Users list
	SortedActiveUsers []string            // Sorted Users map by "activity"
	SortedTopUsers    []string            // Sorted Users map by words
}

type sortedMap struct {
	m map[string]int
	s []string
}

func (sm *sortedMap) Len() int {
	return len(sm.m)
}

func (sm *sortedMap) Less(i, j int) bool {
	return sm.m[sm.s[i]] > sm.m[sm.s[j]]
}

func (sm *sortedMap) Swap(i, j int) {
	sm.s[i], sm.s[j] = sm.s[j], sm.s[i]
}

func sortedKeys(m map[string]int) []string {
	sm := new(sortedMap)
	sm.m = m
	sm.s = make([]string, len(m))
	i := 0
	for key, _ := range m {
		sm.s[i] = key
		i++
	}
	sort.Sort(sm)
	return sm.s
}

func NewViewData(c Config) *ViewData {
	j := JsonData{
		HeatMapInterval: c.HeatMapInterval,
		ActivityPeriod:  c.ActivityPeriod,
		GeneratedAt:     time.Now().Unix(),
	}

	// Set timezone data for frontend
	j.TimeZone.Name, j.TimeZone.Offset = time.Now().Zone()
	j.TimeZone.Offset = (j.TimeZone.Offset / 60) / 60 // We want the zone offset in hours

	return &ViewData{
		PageTitle:       c.PageTitle,
		PageDescription: c.PageDescription,
		JsonData:        j,
	}
}

func (j JsonData) Debug() {
	fmt.Println("==================================================")
	fmt.Println("Json Data Debug:")
	fmt.Println("= [ Dates ] ======================================")
	fmt.Printf("First line date\t\t\t\t\t%d\n", j.FirstSeen)
	fmt.Printf("Last line date\t\t\t\t\t%d\n", j.LastSeen)
	fmt.Printf("Total Days Seen:\t\t\t\t%d\n", j.TotalDaysSeen)
	fmt.Println("= [ Averages ] ===================================")
	fmt.Printf("Mean Lines/Hr:\t\t\t\t\t%f\n", j.Averages.Hour)
	fmt.Printf("Mean Lines/Day:\t\t\t\t\t%f\n", j.Averages.Day)
	fmt.Printf("Mean Lines/Week:\t\t\t\t%f\n", j.Averages.Week)
	fmt.Printf("Mean Lines/Week Day:\t\t\t%f\n", j.Averages.WeekDay)
	fmt.Println("= [ Counters ] ===================================")
	fmt.Printf("Total Lines Parsed:\t\t\t\t%d\n", j.TotalLines)
	fmt.Printf("Total Unique Users:\t\t\t\t%d\n", j.TotalUsers)
	fmt.Printf("Users Active in past %d days:\t%d\n", j.ActivityPeriod, j.TotalActiveUsers)

	fmt.Printf("Peak Day Date:\t\t\t\t\t%s\n", j.MaxDay.Day)
	fmt.Printf("Peak Day Lines:\t\t\t\t\t%d\n", j.MaxDay.Lines)

	fmt.Printf("Peak Hour:\t\t\t\t\t\t%d\n", j.MaxHour.Hour)
	fmt.Printf("Peak Hour Lines:\t\t\t\t%d\n", j.MaxHour.Lines)

	fmt.Printf("Peak Week:\t\t\t\t\t\t%d\n", j.MaxWeek.Week)
	fmt.Printf("Peak Week Lines:\t\t\t\t%d\n", j.MaxWeek.Lines)

	fmt.Println("==================================================")
}

// Calculate stats for View
func (vd *ViewData) Calculate(db Database) {
	// Dates
	vd.JsonData.FirstSeen = db.Channel.FirstSeen
	vd.JsonData.LastSeen = db.Channel.LastSeen
	vd.JsonData.TotalDaysSeen = db.Channel.TotalDaysSeen()

	// Calculate Counters
	vd.JsonData.TotalUsers = db.CountUsers()
	vd.JsonData.MaxDay.Day, vd.JsonData.MaxDay.Lines = db.Channel.FindPeakDay()
	vd.JsonData.MaxHour.Hour, vd.JsonData.MaxHour.Lines = db.Channel.FindPeakHour()
	vd.JsonData.MaxWeek.Week, vd.JsonData.MaxWeek.Lines = db.Channel.FindPeakWeek()
	vd.JsonData.TotalLines = db.Channel.LineCount
	vd.JsonData.TotalWords = db.Channel.WordCount

	// Calculate Averages
	vd.JsonData.Averages.Hour = db.Channel.FindHourAverage()
	vd.JsonData.Averages.Week = db.Channel.FindWeekAverage()
	vd.JsonData.Averages.Day = db.Channel.FindDayAverage()

	// Calculate Users
	vd.calculateUsers(db)

	// @todo Format data for Graph Usage
	// loop one day each between vd.JsonData.FirstSeen and vd.JsonData.LastSeen
	//vd.JsonData.Days = append(vd.JsonData.Days, GraphDay{Date: dt, Value: 0})

}

func (vd *ViewData) calculateUsers(db Database) {
	var (
		timePeriod  map[string]bool
		users       map[string]UserData
		activeUsers map[string]int
		topUsers    map[string]int

		userWordCount int64
		userDaysActive int64
	)

	timePeriod = make(map[string]bool)
	activeUsers = make(map[string]int)
	topUsers = make(map[string]int)
	users = make(map[string]UserData)

	for i := 1; i < int(vd.JsonData.ActivityPeriod); i++ {
		timePeriod[time.Now().AddDate(0, 0, -i).Format("2006-02-01")] = true
	}

	for nick, u := range db.Users {
		userWordCount = 0
		userDaysActive = 0

		for timePeriodDate := range timePeriod {
			if _, ok := u.Days[timePeriodDate]; ok {
				userDaysActive++
				userWordCount += u.Days[timePeriodDate]
			}
		}

		viewUserData := UserData{
			Username:           u.Username,
			Url:                u.Url,
			Avatar:             u.Avatar,
			FirstSpoke:         u.FirstSeen,
			LastSpoke:          u.LastSeen,
			TotalWords:         u.WordCount,
			Vocabulary:         int64(len(u.Words)),
			Words:              u.Words,
			DaysActiveInPeriod: userDaysActive,
			TotalWordsInPeriod: userWordCount,
			ActivityPercentage: (float64(u.WordCount) / float64(db.Channel.WordCount)) * 100,
		}

		viewUserData.Averages.Hour = u.FindHourAverage()
		viewUserData.Averages.Week = u.FindWeekAverage()
		viewUserData.Averages.Day = u.FindDayAverage()

		if userDaysActive > 0 {
			viewUserData.WordsByDayInPeriod = float64(userWordCount) / float64(userDaysActive)
			activeUsers[nick] = int(userDaysActive)
		}
		topUsers[nick] = int(viewUserData.TotalWords)
		users[nick] = viewUserData
	}

	vd.JsonData.Users = users
	vd.JsonData.SortedTopUsers = sortedKeys(topUsers)
	vd.JsonData.SortedActiveUsers = sortedKeys(activeUsers)
	vd.JsonData.TotalActiveUsers = int64(len(vd.JsonData.SortedActiveUsers))
}

// Returns a json string of the JsonData, good for debugging.
func (vd ViewData) GetJsonString() (j []byte, err error) {
	j, err = json.Marshal(vd.JsonData)
	return
}
<|endoftext|>


<commit_before>/*
 * MinIO Cloud Storage, (C) 2018 MinIO, Inc.
 *
 * 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
 *
 *     http://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 event

import (
	"encoding/xml"
	"errors"
	"io"
	"reflect"
	"strings"
	"unicode/utf8"

	"github.com/minio/minio-go/v6/pkg/set"
)

// ValidateFilterRuleValue - checks if given value is filter rule value or not.
func ValidateFilterRuleValue(value string) error {
	for _, segment := range strings.Split(value, "/") {
		if segment == "." || segment == ".." {
			return &ErrInvalidFilterValue{value}
		}
	}

	if len(value) <= 1024 && utf8.ValidString(value) && !strings.Contains(value, `\`) {
		return nil
	}

	return &ErrInvalidFilterValue{value}
}

// FilterRule - represents elements inside <FilterRule>...</FilterRule>
type FilterRule struct {
	Name  string `xml:"Name"`
	Value string `xml:"Value"`
}

// UnmarshalXML - decodes XML data.
func (filter *FilterRule) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type filterRule FilterRule
	rule := filterRule{}
	if err := d.DecodeElement(&rule, &start); err != nil {
		return err
	}

	if rule.Name != "prefix" && rule.Name != "suffix" {
		return &ErrInvalidFilterName{rule.Name}
	}

	if err := ValidateFilterRuleValue(filter.Value); err != nil {
		return err
	}

	*filter = FilterRule(rule)

	return nil
}

// FilterRuleList - represents multiple <FilterRule>...</FilterRule>
type FilterRuleList struct {
	Rules []FilterRule `xml:"FilterRule,omitempty"`
}

// UnmarshalXML - decodes XML data.
func (ruleList *FilterRuleList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type filterRuleList FilterRuleList
	rules := filterRuleList{}
	if err := d.DecodeElement(&rules, &start); err != nil {
		return err
	}

	// FilterRuleList must have only one prefix and/or suffix.
	nameSet := set.NewStringSet()
	for _, rule := range rules.Rules {
		if nameSet.Contains(rule.Name) {
			if rule.Name == "prefix" {
				return &ErrFilterNamePrefix{}
			}

			return &ErrFilterNameSuffix{}
		}

		nameSet.Add(rule.Name)
	}

	*ruleList = FilterRuleList(rules)
	return nil
}

// Pattern - returns pattern using prefix and suffix values.
func (ruleList FilterRuleList) Pattern() string {
	var prefix string
	var suffix string

	for _, rule := range ruleList.Rules {
		switch rule.Name {
		case "prefix":
			prefix = rule.Value
		case "suffix":
			suffix = rule.Value
		}
	}

	return NewPattern(prefix, suffix)
}

// S3Key - represents elements inside <S3Key>...</S3Key>
type S3Key struct {
	RuleList FilterRuleList `xml:"S3Key,omitempty" json:"S3Key,omitempty"`
}

// common - represents common elements inside <QueueConfiguration>, <CloudFunctionConfiguration>
// and <TopicConfiguration>
type common struct {
	ID     string `xml:"Id" json:"Id"`
	Filter S3Key  `xml:"Filter" json:"Filter"`
	Events []Name `xml:"Event" json:"Event"`
}

// Queue - represents elements inside <QueueConfiguration>
type Queue struct {
	common
	ARN ARN `xml:"Queue"`
}

// UnmarshalXML - decodes XML data.
func (q *Queue) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type queue Queue
	parsedQueue := queue{}
	if err := d.DecodeElement(&parsedQueue, &start); err != nil {
		return err
	}

	if len(parsedQueue.Events) == 0 {
		return errors.New("missing event name(s)")
	}

	eventStringSet := set.NewStringSet()
	for _, eventName := range parsedQueue.Events {
		if eventStringSet.Contains(eventName.String()) {
			return &ErrDuplicateEventName{eventName}
		}

		eventStringSet.Add(eventName.String())
	}

	*q = Queue(parsedQueue)

	return nil
}

// Validate - checks whether queue has valid values or not.
func (q Queue) Validate(region string, targetList *TargetList) error {
	if region != "" && q.ARN.region != region {
		return &ErrUnknownRegion{q.ARN.region}
	}

	if !targetList.Exists(q.ARN.TargetID) {
		return &ErrARNNotFound{q.ARN}
	}

	return nil
}

// SetRegion - sets region value to queue's ARN.
func (q *Queue) SetRegion(region string) {
	q.ARN.region = region
}

// ToRulesMap - converts Queue to RulesMap
func (q Queue) ToRulesMap() RulesMap {
	pattern := q.Filter.RuleList.Pattern()
	return NewRulesMap(q.Events, pattern, q.ARN.TargetID)
}

// Unused.  Available for completion.
type lambda struct {
	ARN string `xml:"CloudFunction"`
}

// Unused. Available for completion.
type topic struct {
	ARN string `xml:"Topic" json:"Topic"`
}

// Config - notification configuration described in
// http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html
type Config struct {
	XMLNS      string   `xml:"xmlns,attr,omitempty"`
	XMLName    xml.Name `xml:"NotificationConfiguration"`
	QueueList  []Queue  `xml:"QueueConfiguration,omitempty"`
	LambdaList []lambda `xml:"CloudFunctionConfiguration,omitempty"`
	TopicList  []topic  `xml:"TopicConfiguration,omitempty"`
}

// UnmarshalXML - decodes XML data.
func (conf *Config) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type config Config
	parsedConfig := config{}
	if err := d.DecodeElement(&parsedConfig, &start); err != nil {
		return err
	}

	// Empty queue list means user wants to delete the notification configuration.
	if len(parsedConfig.QueueList) > 0 {
		for i, q1 := range parsedConfig.QueueList[:len(parsedConfig.QueueList)-1] {
			for _, q2 := range parsedConfig.QueueList[i+1:] {
				if reflect.DeepEqual(q1, q2) {
					return &ErrDuplicateQueueConfiguration{q1}
				}
			}
		}
	}

	if len(parsedConfig.LambdaList) > 0 || len(parsedConfig.TopicList) > 0 {
		return &ErrUnsupportedConfiguration{}
	}

	*conf = Config(parsedConfig)

	return nil
}

// Validate - checks whether config has valid values or not.
func (conf Config) Validate(region string, targetList *TargetList) error {
	for _, queue := range conf.QueueList {
		if err := queue.Validate(region, targetList); err != nil {
			return err
		}

		// TODO: Need to discuss/check why same ARN cannot be used in another queue configuration.
	}

	return nil
}

// SetRegion - sets region to all queue configuration.
func (conf *Config) SetRegion(region string) {
	for i := range conf.QueueList {
		conf.QueueList[i].SetRegion(region)
	}
}

// ToRulesMap - converts all queue configuration to RulesMap.
func (conf *Config) ToRulesMap() RulesMap {
	rulesMap := make(RulesMap)

	for _, queue := range conf.QueueList {
		rulesMap.Add(queue.ToRulesMap())
	}

	return rulesMap
}

// ParseConfig - parses data in reader to notification configuration.
func ParseConfig(reader io.Reader, region string, targetList *TargetList) (*Config, error) {
	var config Config
	if err := xml.NewDecoder(reader).Decode(&config); err != nil {
		return nil, err
	}

	if err := config.Validate(region, targetList); err != nil {
		return nil, err
	}

	config.SetRegion(region)

	return &config, nil
}
<commit_msg>Stop duplicate entry in Notification.xml (#7690)<commit_after>/*
 * MinIO Cloud Storage, (C) 2018 MinIO, Inc.
 *
 * 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
 *
 *     http://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 event

import (
	"encoding/xml"
	"errors"
	"io"
	"reflect"
	"strings"
	"unicode/utf8"

	"github.com/minio/minio-go/v6/pkg/set"
)

// ValidateFilterRuleValue - checks if given value is filter rule value or not.
func ValidateFilterRuleValue(value string) error {
	for _, segment := range strings.Split(value, "/") {
		if segment == "." || segment == ".." {
			return &ErrInvalidFilterValue{value}
		}
	}

	if len(value) <= 1024 && utf8.ValidString(value) && !strings.Contains(value, `\`) {
		return nil
	}

	return &ErrInvalidFilterValue{value}
}

// FilterRule - represents elements inside <FilterRule>...</FilterRule>
type FilterRule struct {
	Name  string `xml:"Name"`
	Value string `xml:"Value"`
}

// UnmarshalXML - decodes XML data.
func (filter *FilterRule) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type filterRule FilterRule
	rule := filterRule{}
	if err := d.DecodeElement(&rule, &start); err != nil {
		return err
	}

	if rule.Name != "prefix" && rule.Name != "suffix" {
		return &ErrInvalidFilterName{rule.Name}
	}

	if err := ValidateFilterRuleValue(filter.Value); err != nil {
		return err
	}

	*filter = FilterRule(rule)

	return nil
}

// FilterRuleList - represents multiple <FilterRule>...</FilterRule>
type FilterRuleList struct {
	Rules []FilterRule `xml:"FilterRule,omitempty"`
}

// UnmarshalXML - decodes XML data.
func (ruleList *FilterRuleList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type filterRuleList FilterRuleList
	rules := filterRuleList{}
	if err := d.DecodeElement(&rules, &start); err != nil {
		return err
	}

	// FilterRuleList must have only one prefix and/or suffix.
	nameSet := set.NewStringSet()
	for _, rule := range rules.Rules {
		if nameSet.Contains(rule.Name) {
			if rule.Name == "prefix" {
				return &ErrFilterNamePrefix{}
			}

			return &ErrFilterNameSuffix{}
		}

		nameSet.Add(rule.Name)
	}

	*ruleList = FilterRuleList(rules)
	return nil
}

// Pattern - returns pattern using prefix and suffix values.
func (ruleList FilterRuleList) Pattern() string {
	var prefix string
	var suffix string

	for _, rule := range ruleList.Rules {
		switch rule.Name {
		case "prefix":
			prefix = rule.Value
		case "suffix":
			suffix = rule.Value
		}
	}

	return NewPattern(prefix, suffix)
}

// S3Key - represents elements inside <S3Key>...</S3Key>
type S3Key struct {
	RuleList FilterRuleList `xml:"S3Key,omitempty" json:"S3Key,omitempty"`
}

// common - represents common elements inside <QueueConfiguration>, <CloudFunctionConfiguration>
// and <TopicConfiguration>
type common struct {
	ID     string `xml:"Id" json:"Id"`
	Filter S3Key  `xml:"Filter" json:"Filter"`
	Events []Name `xml:"Event" json:"Event"`
}

// Queue - represents elements inside <QueueConfiguration>
type Queue struct {
	common
	ARN ARN `xml:"Queue"`
}

// UnmarshalXML - decodes XML data.
func (q *Queue) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type queue Queue
	parsedQueue := queue{}
	if err := d.DecodeElement(&parsedQueue, &start); err != nil {
		return err
	}

	if len(parsedQueue.Events) == 0 {
		return errors.New("missing event name(s)")
	}

	eventStringSet := set.NewStringSet()
	for _, eventName := range parsedQueue.Events {
		if eventStringSet.Contains(eventName.String()) {
			return &ErrDuplicateEventName{eventName}
		}

		eventStringSet.Add(eventName.String())
	}

	*q = Queue(parsedQueue)

	return nil
}

// Validate - checks whether queue has valid values or not.
func (q Queue) Validate(region string, targetList *TargetList) error {
	if region != "" && q.ARN.region != region {
		return &ErrUnknownRegion{q.ARN.region}
	}

	if !targetList.Exists(q.ARN.TargetID) {
		return &ErrARNNotFound{q.ARN}
	}

	return nil
}

// SetRegion - sets region value to queue's ARN.
func (q *Queue) SetRegion(region string) {
	q.ARN.region = region
}

// ToRulesMap - converts Queue to RulesMap
func (q Queue) ToRulesMap() RulesMap {
	pattern := q.Filter.RuleList.Pattern()
	return NewRulesMap(q.Events, pattern, q.ARN.TargetID)
}

// Unused.  Available for completion.
type lambda struct {
	ARN string `xml:"CloudFunction"`
}

// Unused. Available for completion.
type topic struct {
	ARN string `xml:"Topic" json:"Topic"`
}

// Config - notification configuration described in
// http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html
type Config struct {
	XMLNS      string   `xml:"xmlns,attr,omitempty"`
	XMLName    xml.Name `xml:"NotificationConfiguration"`
	QueueList  []Queue  `xml:"QueueConfiguration,omitempty"`
	LambdaList []lambda `xml:"CloudFunctionConfiguration,omitempty"`
	TopicList  []topic  `xml:"TopicConfiguration,omitempty"`
}

// UnmarshalXML - decodes XML data.
func (conf *Config) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Make subtype to avoid recursive UnmarshalXML().
	type config Config
	parsedConfig := config{}
	if err := d.DecodeElement(&parsedConfig, &start); err != nil {
		return err
	}

	// Empty queue list means user wants to delete the notification configuration.
	if len(parsedConfig.QueueList) > 0 {
		for i, q1 := range parsedConfig.QueueList[:len(parsedConfig.QueueList)-1] {
			for _, q2 := range parsedConfig.QueueList[i+1:] {
				// Removes the region from ARN if server region is not set
				if q2.ARN.region != "" && q1.ARN.region == "" {
					q2.ARN.region = ""
				}
				if reflect.DeepEqual(q1, q2) {
					return &ErrDuplicateQueueConfiguration{q1}
				}
			}
		}
	}

	if len(parsedConfig.LambdaList) > 0 || len(parsedConfig.TopicList) > 0 {
		return &ErrUnsupportedConfiguration{}
	}

	*conf = Config(parsedConfig)

	return nil
}

// Validate - checks whether config has valid values or not.
func (conf Config) Validate(region string, targetList *TargetList) error {
	for _, queue := range conf.QueueList {
		if err := queue.Validate(region, targetList); err != nil {
			return err
		}

		// TODO: Need to discuss/check why same ARN cannot be used in another queue configuration.
	}

	return nil
}

// SetRegion - sets region to all queue configuration.
func (conf *Config) SetRegion(region string) {
	for i := range conf.QueueList {
		conf.QueueList[i].SetRegion(region)
	}
}

// ToRulesMap - converts all queue configuration to RulesMap.
func (conf *Config) ToRulesMap() RulesMap {
	rulesMap := make(RulesMap)

	for _, queue := range conf.QueueList {
		rulesMap.Add(queue.ToRulesMap())
	}

	return rulesMap
}

// ParseConfig - parses data in reader to notification configuration.
func ParseConfig(reader io.Reader, region string, targetList *TargetList) (*Config, error) {
	var config Config
	if err := xml.NewDecoder(reader).Decode(&config); err != nil {
		return nil, err
	}

	if err := config.Validate(region, targetList); err != nil {
		return nil, err
	}

	config.SetRegion(region)

	return &config, nil
}
<|endoftext|>