<commit_before>/*
Copyright 2016 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 spotinstmodel

import (
	"fmt"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/model"
	"k8s.io/kops/pkg/model/awsmodel"
	"k8s.io/kops/pkg/model/defaults"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
	"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
	"k8s.io/kops/upup/pkg/fi/cloudup/spotinsttasks"
)

const (
	// InstanceGroupLabelOrientation is the metadata label used on the
	// instance group to specify which orientation should be used.
	InstanceGroupLabelOrientation = "spotinst.io/orientation"

	// InstanceGroupLabelUtilizeReservedInstances is the metadata label used
	// on the instance group to specify whether reserved instances should be
	// utilized.
	InstanceGroupLabelUtilizeReservedInstances = "spotinst.io/utilize-reserved-instances"

	// InstanceGroupLabelFallbackToOnDemand is the metadata label used on the
	// instance group to specify whether fallback to on-demand instances should
	// be enabled.
	InstanceGroupLabelFallbackToOnDemand = "spotinst.io/fallback-to-ondemand"

	// InstanceGroupLabelAutoScalerDisabled is the metadata label used on the
	// instance group to specify whether the auto-scaler should be enabled.
	InstanceGroupLabelAutoScalerDisabled = "spotinst.io/autoscaler-disabled"

	// InstanceGroupLabelAutoScalerNodeLabels is the metadata label used on the
	// instance group to specify whether default node labels should be set for
	// the auto-scaler.
	InstanceGroupLabelAutoScalerNodeLabels = "spotinst.io/autoscaler-node-labels"
)

// ElastigroupModelBuilder configures Elastigroup objects
type ElastigroupModelBuilder struct {
	*awsmodel.AWSModelContext

	BootstrapScript   *model.BootstrapScript
	Lifecycle         *fi.Lifecycle
	SecurityLifecycle *fi.Lifecycle
}

var _ fi.ModelBuilder = &ElastigroupModelBuilder{}

func (b *ElastigroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
	for _, ig := range b.InstanceGroups {
		glog.V(2).Infof("Building instance group %q", b.AutoscalingGroupName(ig))

		group := &spotinsttasks.Elastigroup{
			Lifecycle:            b.Lifecycle,
			Name:                 fi.String(b.AutoscalingGroupName(ig)),
			ImageID:              fi.String(ig.Spec.Image),
			Monitoring:           fi.Bool(false),
			OnDemandInstanceType: fi.String(strings.Split(ig.Spec.MachineType, ",")[0]),
			SpotInstanceTypes:    strings.Split(ig.Spec.MachineType, ","),
			SecurityGroups: []*awstasks.SecurityGroup{
				b.LinkToSecurityGroup(ig.Spec.Role),
			},
		}

		// Cloud config.
		{
			if cfg := b.Cluster.Spec.CloudConfig; cfg != nil {
				// Product.
				if cfg.SpotinstProduct != nil {
					group.Product = cfg.SpotinstProduct
				}

				// Orientation.
				if cfg.SpotinstOrientation != nil {
					group.Orientation = cfg.SpotinstOrientation
				}
			}
		}

		// Strategy.
		{
			for k, v := range ig.ObjectMeta.Labels {
				switch k {
				case InstanceGroupLabelOrientation:
					group.Orientation = fi.String(v)
					break

				case InstanceGroupLabelUtilizeReservedInstances:
					if v == "true" {
						group.UtilizeReservedInstances = fi.Bool(true)
					} else if v == "false" {
						group.UtilizeReservedInstances = fi.Bool(false)
					}
					break

				case InstanceGroupLabelFallbackToOnDemand:
					if v == "true" {
						group.FallbackToOnDemand = fi.Bool(true)
					} else if v == "false" {
						group.FallbackToOnDemand = fi.Bool(false)
					}
					break
				}
			}
		}

		// Instance profile.
		{
			iprof, err := b.LinkToIAMInstanceProfile(ig)
			if err != nil {
				return err
			}
			group.IAMInstanceProfile = iprof
		}

		// Root volume.
		{
			volumeSize := fi.Int32Value(ig.Spec.RootVolumeSize)
			if volumeSize == 0 {
				var err error
				volumeSize, err = defaults.DefaultInstanceGroupVolumeSize(ig.Spec.Role)
				if err != nil {
					return err
				}
			}

			volumeType := fi.StringValue(ig.Spec.RootVolumeType)
			if volumeType == "" {
				volumeType = awsmodel.DefaultVolumeType
			}

			group.RootVolumeSize = fi.Int64(int64(volumeSize))
			group.RootVolumeType = fi.String(volumeType)
			group.RootVolumeOptimization = ig.Spec.RootVolumeOptimization
		}

		// Tenancy.
		{
			if ig.Spec.Tenancy != "" {
				group.Tenancy = fi.String(ig.Spec.Tenancy)
			}
		}

		// Risk.
		{
			var risk float64
			switch ig.Spec.Role {
			case kops.InstanceGroupRoleMaster:
				risk = 0
			case kops.InstanceGroupRoleNode:
				risk = 100
			case kops.InstanceGroupRoleBastion:
				risk = 0
			default:
				return fmt.Errorf("spotinst: kops.Role not found %s", ig.Spec.Role)
			}
			group.Risk = &risk
		}

		// Security groups.
		{
			for _, id := range ig.Spec.AdditionalSecurityGroups {
				sgTask := &awstasks.SecurityGroup{
					Name:   fi.String(id),
					ID:     fi.String(id),
					Shared: fi.Bool(true),
				}
				if err := c.EnsureTask(sgTask); err != nil {
					return err
				}
				group.SecurityGroups = append(group.SecurityGroups, sgTask)
			}
		}

		// SSH Key.
		{
			sshKey, err := b.LinkToSSHKey()
			if err != nil {
				return err
			}
			group.SSHKey = sshKey
		}

		// Load balancer.
		{
			var lb *awstasks.LoadBalancer
			switch ig.Spec.Role {
			case kops.InstanceGroupRoleMaster:
				if b.UseLoadBalancerForAPI() {
					lb = b.LinkToELB("api")
				}
			case kops.InstanceGroupRoleBastion:
				lb = b.LinkToELB(model.BastionELBSecurityGroupPrefix)
			}
			if lb != nil {
				group.LoadBalancer = lb
			}
		}

		// User data.
		{
			userData, err := b.BootstrapScript.ResourceNodeUp(ig, b.Cluster)
			if err != nil {
				return err
			}
			group.UserData = userData
		}

		// Public IP.
		{
			subnetMap := make(map[string]*kops.ClusterSubnetSpec)
			for i := range b.Cluster.Spec.Subnets {
				subnet := &b.Cluster.Spec.Subnets[i]
				subnetMap[subnet.Name] = subnet
			}

			var subnetType kops.SubnetType
			for _, subnetName := range ig.Spec.Subnets {
				subnet := subnetMap[subnetName]
				if subnet == nil {
					return fmt.Errorf("spotinst: InstanceGroup %q uses subnet %q that does not exist", ig.ObjectMeta.Name, subnetName)
				}
				if subnetType != "" && subnetType != subnet.Type {
					return fmt.Errorf("spotinst: InstanceGroup %q cannot be in subnets of different Type", ig.ObjectMeta.Name)
				}
				subnetType = subnet.Type
			}

			associatePublicIP := true
			switch subnetType {
			case kops.SubnetTypePublic, kops.SubnetTypeUtility:
				associatePublicIP = true
				if ig.Spec.AssociatePublicIP != nil {
					associatePublicIP = *ig.Spec.AssociatePublicIP
				}
			case kops.SubnetTypePrivate:
				associatePublicIP = false
				if ig.Spec.AssociatePublicIP != nil {
					if *ig.Spec.AssociatePublicIP {
						glog.Warningf("Ignoring AssociatePublicIP=true for private InstanceGroup %q", ig.ObjectMeta.Name)
					}
				}
			default:
				return fmt.Errorf("spotinst: unknown subnet type %q", subnetType)
			}
			group.AssociatePublicIP = &associatePublicIP
		}

		// Subnets.
		{
			subnets, err := b.GatherSubnets(ig)
			if err != nil {
				return err
			}
			if len(subnets) == 0 {
				return fmt.Errorf("spotinst: could not determine any subnets for InstanceGroup %q; subnets was %s", ig.ObjectMeta.Name, ig.Spec.Subnets)
			}
			for _, subnet := range subnets {
				group.Subnets = append(group.Subnets, b.LinkToSubnet(subnet))
			}
		}

		// Capacity.
		{
			minSize := int32(1)
			if ig.Spec.MinSize != nil {
				minSize = fi.Int32Value(ig.Spec.MinSize)
			} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
				minSize = 2
			}

			maxSize := int32(1)
			if ig.Spec.MaxSize != nil {
				maxSize = *ig.Spec.MaxSize
			} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
				maxSize = 10
			}

			group.MinSize = fi.Int64(int64(minSize))
			group.MaxSize = fi.Int64(int64(maxSize))
		}

		// Tags.
		{
			tags, err := b.CloudTagsForInstanceGroup(ig)
			if err != nil {
				return fmt.Errorf("spotinst: error building cloud tags: %v", err)
			}
			tags[awsup.TagClusterName] = b.ClusterName()
			tags["Name"] = b.AutoscalingGroupName(ig)
			group.Tags = tags
		}

		// Auto Scaler.
		{
			if ig.Spec.Role != kops.InstanceGroupRoleBastion {
				group.ClusterIdentifier = fi.String(b.ClusterName())

				// Toggle auto scaler's features.
				var autoScalerDisabled bool
				var autoScalerNodeLabels bool
				{
					for k, v := range ig.ObjectMeta.Labels {
						switch k {
						case InstanceGroupLabelAutoScalerDisabled:
							if v == "true" {
								autoScalerDisabled = true
							} else if v == "false" {
								autoScalerDisabled = false
							}
							break

						case InstanceGroupLabelAutoScalerNodeLabels:
							if v == "true" {
								autoScalerNodeLabels = true
							} else if v == "false" {
								autoScalerNodeLabels = false
							}
							break
						}
					}
				}

				// Toggle the auto scaler.
				group.AutoScalerEnabled = fi.Bool(!autoScalerDisabled)

				// Set the node labels.
				if ig.Spec.Role == kops.InstanceGroupRoleNode {
					nodeLabels := make(map[string]string)
					for k, v := range ig.Spec.NodeLabels {
						if strings.HasPrefix(k, kops.NodeLabelInstanceGroup) && !autoScalerNodeLabels {
							continue
						}
						nodeLabels[k] = v
					}
					if len(nodeLabels) > 0 {
						group.AutoScalerNodeLabels = nodeLabels
					}
				}
			}
		}

		c.AddTask(group)
	}

	return nil
}
<commit_msg>fix: don't use curly brackets for additional scoping<commit_after>/*
Copyright 2016 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 spotinstmodel

import (
	"fmt"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/model"
	"k8s.io/kops/pkg/model/awsmodel"
	"k8s.io/kops/pkg/model/defaults"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
	"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
	"k8s.io/kops/upup/pkg/fi/cloudup/spotinsttasks"
)

const (
	// InstanceGroupLabelOrientation is the metadata label used on the
	// instance group to specify which orientation should be used.
	InstanceGroupLabelOrientation = "spotinst.io/orientation"

	// InstanceGroupLabelUtilizeReservedInstances is the metadata label used
	// on the instance group to specify whether reserved instances should be
	// utilized.
	InstanceGroupLabelUtilizeReservedInstances = "spotinst.io/utilize-reserved-instances"

	// InstanceGroupLabelFallbackToOnDemand is the metadata label used on the
	// instance group to specify whether fallback to on-demand instances should
	// be enabled.
	InstanceGroupLabelFallbackToOnDemand = "spotinst.io/fallback-to-ondemand"

	// InstanceGroupLabelAutoScalerDisabled is the metadata label used on the
	// instance group to specify whether the auto-scaler should be enabled.
	InstanceGroupLabelAutoScalerDisabled = "spotinst.io/autoscaler-disabled"

	// InstanceGroupLabelAutoScalerNodeLabels is the metadata label used on the
	// instance group to specify whether default node labels should be set for
	// the auto-scaler.
	InstanceGroupLabelAutoScalerNodeLabels = "spotinst.io/autoscaler-node-labels"
)

// ElastigroupModelBuilder configures Elastigroup objects
type ElastigroupModelBuilder struct {
	*awsmodel.AWSModelContext

	BootstrapScript   *model.BootstrapScript
	Lifecycle         *fi.Lifecycle
	SecurityLifecycle *fi.Lifecycle
}

var _ fi.ModelBuilder = &ElastigroupModelBuilder{}

func (b *ElastigroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
	for _, ig := range b.InstanceGroups {
		glog.V(2).Infof("Building instance group %q", b.AutoscalingGroupName(ig))

		group := &spotinsttasks.Elastigroup{
			Lifecycle:            b.Lifecycle,
			Name:                 fi.String(b.AutoscalingGroupName(ig)),
			ImageID:              fi.String(ig.Spec.Image),
			Monitoring:           fi.Bool(false),
			OnDemandInstanceType: fi.String(strings.Split(ig.Spec.MachineType, ",")[0]),
			SpotInstanceTypes:    strings.Split(ig.Spec.MachineType, ","),
			SecurityGroups: []*awstasks.SecurityGroup{
				b.LinkToSecurityGroup(ig.Spec.Role),
			},
		}

		// Cloud config.
		if cfg := b.Cluster.Spec.CloudConfig; cfg != nil {
			// Product.
			if cfg.SpotinstProduct != nil {
				group.Product = cfg.SpotinstProduct
			}

			// Orientation.
			if cfg.SpotinstOrientation != nil {
				group.Orientation = cfg.SpotinstOrientation
			}
		}

		// Strategy.
		for k, v := range ig.ObjectMeta.Labels {
			switch k {
			case InstanceGroupLabelOrientation:
				group.Orientation = fi.String(v)
				break

			case InstanceGroupLabelUtilizeReservedInstances:
				if v == "true" {
					group.UtilizeReservedInstances = fi.Bool(true)
				} else if v == "false" {
					group.UtilizeReservedInstances = fi.Bool(false)
				}
				break

			case InstanceGroupLabelFallbackToOnDemand:
				if v == "true" {
					group.FallbackToOnDemand = fi.Bool(true)
				} else if v == "false" {
					group.FallbackToOnDemand = fi.Bool(false)
				}
				break
			}
		}

		// Instance profile.
		iprof, err := b.LinkToIAMInstanceProfile(ig)
		if err != nil {
			return err
		}
		group.IAMInstanceProfile = iprof

		// Root volume.
		volumeSize := fi.Int32Value(ig.Spec.RootVolumeSize)
		if volumeSize == 0 {
			var err error
			volumeSize, err = defaults.DefaultInstanceGroupVolumeSize(ig.Spec.Role)
			if err != nil {
				return err
			}
		}

		volumeType := fi.StringValue(ig.Spec.RootVolumeType)
		if volumeType == "" {
			volumeType = awsmodel.DefaultVolumeType
		}

		group.RootVolumeSize = fi.Int64(int64(volumeSize))
		group.RootVolumeType = fi.String(volumeType)
		group.RootVolumeOptimization = ig.Spec.RootVolumeOptimization

		// Tenancy.
		if ig.Spec.Tenancy != "" {
			group.Tenancy = fi.String(ig.Spec.Tenancy)
		}

		// Risk.
		var risk float64
		switch ig.Spec.Role {
		case kops.InstanceGroupRoleMaster:
			risk = 0
		case kops.InstanceGroupRoleNode:
			risk = 100
		case kops.InstanceGroupRoleBastion:
			risk = 0
		default:
			return fmt.Errorf("spotinst: kops.Role not found %s", ig.Spec.Role)
		}
		group.Risk = &risk

		// Security groups.
		for _, id := range ig.Spec.AdditionalSecurityGroups {
			sgTask := &awstasks.SecurityGroup{
				Name:   fi.String(id),
				ID:     fi.String(id),
				Shared: fi.Bool(true),
			}
			if err := c.EnsureTask(sgTask); err != nil {
				return err
			}
			group.SecurityGroups = append(group.SecurityGroups, sgTask)
		}

		// SSH Key.
		sshKey, err := b.LinkToSSHKey()
		if err != nil {
			return err
		}
		group.SSHKey = sshKey

		// Load balancer.
		var lb *awstasks.LoadBalancer
		switch ig.Spec.Role {
		case kops.InstanceGroupRoleMaster:
			if b.UseLoadBalancerForAPI() {
				lb = b.LinkToELB("api")
			}
		case kops.InstanceGroupRoleBastion:
			lb = b.LinkToELB(model.BastionELBSecurityGroupPrefix)
		}
		if lb != nil {
			group.LoadBalancer = lb
		}

		// User data.
		userData, err := b.BootstrapScript.ResourceNodeUp(ig, b.Cluster)
		if err != nil {
			return err
		}
		group.UserData = userData

		// Public IP.
		subnetMap := make(map[string]*kops.ClusterSubnetSpec)
		for i := range b.Cluster.Spec.Subnets {
			subnet := &b.Cluster.Spec.Subnets[i]
			subnetMap[subnet.Name] = subnet
		}

		var subnetType kops.SubnetType
		for _, subnetName := range ig.Spec.Subnets {
			subnet := subnetMap[subnetName]
			if subnet == nil {
				return fmt.Errorf("spotinst: InstanceGroup %q uses subnet %q that does not exist", ig.ObjectMeta.Name, subnetName)
			}
			if subnetType != "" && subnetType != subnet.Type {
				return fmt.Errorf("spotinst: InstanceGroup %q cannot be in subnets of different Type", ig.ObjectMeta.Name)
			}
			subnetType = subnet.Type
		}

		associatePublicIP := true
		switch subnetType {
		case kops.SubnetTypePublic, kops.SubnetTypeUtility:
			associatePublicIP = true
			if ig.Spec.AssociatePublicIP != nil {
				associatePublicIP = *ig.Spec.AssociatePublicIP
			}
		case kops.SubnetTypePrivate:
			associatePublicIP = false
			if ig.Spec.AssociatePublicIP != nil {
				if *ig.Spec.AssociatePublicIP {
					glog.Warningf("Ignoring AssociatePublicIP=true for private InstanceGroup %q", ig.ObjectMeta.Name)
				}
			}
		default:
			return fmt.Errorf("spotinst: unknown subnet type %q", subnetType)
		}
		group.AssociatePublicIP = &associatePublicIP

		// Subnets.
		subnets, err := b.GatherSubnets(ig)
		if err != nil {
			return err
		}
		if len(subnets) == 0 {
			return fmt.Errorf("spotinst: could not determine any subnets for InstanceGroup %q; subnets was %s", ig.ObjectMeta.Name, ig.Spec.Subnets)
		}
		for _, subnet := range subnets {
			group.Subnets = append(group.Subnets, b.LinkToSubnet(subnet))
		}

		// Capacity.
		minSize := int32(1)
		if ig.Spec.MinSize != nil {
			minSize = fi.Int32Value(ig.Spec.MinSize)
		} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
			minSize = 2
		}

		maxSize := int32(1)
		if ig.Spec.MaxSize != nil {
			maxSize = *ig.Spec.MaxSize
		} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
			maxSize = 10
		}

		group.MinSize = fi.Int64(int64(minSize))
		group.MaxSize = fi.Int64(int64(maxSize))

		// Tags.
		tags, err := b.CloudTagsForInstanceGroup(ig)
		if err != nil {
			return fmt.Errorf("spotinst: error building cloud tags: %v", err)
		}
		tags[awsup.TagClusterName] = b.ClusterName()
		tags["Name"] = b.AutoscalingGroupName(ig)
		group.Tags = tags

		// Auto Scaler.
		if ig.Spec.Role != kops.InstanceGroupRoleBastion {
			group.ClusterIdentifier = fi.String(b.ClusterName())

			// Toggle auto scaler's features.
			var autoScalerDisabled bool
			var autoScalerNodeLabels bool
			{
				for k, v := range ig.ObjectMeta.Labels {
					switch k {
					case InstanceGroupLabelAutoScalerDisabled:
						if v == "true" {
							autoScalerDisabled = true
						} else if v == "false" {
							autoScalerDisabled = false
						}
						break

					case InstanceGroupLabelAutoScalerNodeLabels:
						if v == "true" {
							autoScalerNodeLabels = true
						} else if v == "false" {
							autoScalerNodeLabels = false
						}
						break
					}
				}
			}

			// Toggle the auto scaler.
			group.AutoScalerEnabled = fi.Bool(!autoScalerDisabled)

			// Set the node labels.
			if ig.Spec.Role == kops.InstanceGroupRoleNode {
				nodeLabels := make(map[string]string)
				for k, v := range ig.Spec.NodeLabels {
					if strings.HasPrefix(k, kops.NodeLabelInstanceGroup) && !autoScalerNodeLabels {
						continue
					}
					nodeLabels[k] = v
				}
				if len(nodeLabels) > 0 {
					group.AutoScalerNodeLabels = nodeLabels
				}
			}
		}

		c.AddTask(group)
	}

	return nil
}
<|endoftext|>


<commit_before>package session

import (
	"math"
	"time"

	"github.com/cenkalti/rain/internal/addrlist"
)

// Stats contains statistics about Torrent.
type Stats struct {
	// Status of the torrent.
	Status TorrentStatus
	// Contains the error message if torrent is stopped unexpectedly.
	Error  error
	Pieces struct {
		// Number of pieces that are checked when torrent is in "Verifying" state.
		Checked uint32
		// Number of pieces that we are downloaded successfully and verivied by hash check.
		Have uint32
		// Number of pieces that need to be downloaded. Some of them may be being downloaded.
		// Pieces that are being downloaded may counted as missing until they are downloaded and passed hash check.
		Missing uint32
		// Number of unique pieces available on swarm.
		// If this number is less then the number of total pieces, the download may never finish.
		Available uint32
		// Number of total pieces in torrent.
		Total uint32
	}
	Bytes struct {
		// Bytes that are downloaded and passed hash check.
		Complete int64
		// The number of bytes that is needed to complete all missing pieces.
		Incomplete int64
		// The number of total bytes of files in torrent.  Total = Complete + Incomplete
		Total int64
		// Downloaded is the number of bytes downloaded from swarm.
		// Because some pieces may be downloaded more than once, this number may be greater than completed bytes.
		Downloaded int64
		// BytesUploaded is the number of bytes uploaded to the swarm.
		Uploaded int64
		// Bytes downloaded due to duplicate/non-requested pieces.
		Wasted int64
		// Bytes allocated on storage.
		Allocated int64
	}
	Peers struct {
		// Number of peers that are connected, handshaked and ready to send and receive messages.
		Total int
		// Number of peers that have connected to us.
		Incoming int
		// Number of peers that we have connected to.
		Outgoing int
	}
	Handshakes struct {
		// Number of peers that are not handshaked yet.
		Total int
		// Number of incoming peers in handshake state.
		Incoming int
		// Number of outgoing peers in handshake state.
		Outgoing int
	}
	Addresses struct {
		// Total number of peer addresses that are ready to be connected.
		Total int
		// Peers found via trackers.
		Tracker int
		// Peers found via DHT node.
		DHT int
		// Peers found via peer exchange.
		PEX int
	}
	Downloads struct {
		// Number of active piece downloads.
		Total int
		// Number of pieces that are being downloaded normally.
		Running int
		// Number of pieces that are being downloaded too slow.
		Snubbed int
		// Number of piece downloads in choked state.
		Choked int
	}
	MetadataDownloads struct {
		// Number of active metadata downloads.
		Total int
		// Number of peers that uploading too slow.
		Snubbed int
		// Number of peers that are being downloaded normally.
		Running int
	}
	// Name can change after metadata is downloaded.
	Name string
	// Is private torrent?
	Private bool
	// Length of a single piece.
	PieceLength uint32
	// Number of seconds that torrent has seeded.
	SecondsSeeded int
}

func (t *torrent) stats() Stats {
	var s Stats
	s.Status = t.status()
	s.Error = t.lastError
	s.Addresses.Total = t.addrList.Len()
	s.Addresses.Tracker = t.addrList.LenSource(addrlist.Tracker)
	s.Addresses.DHT = t.addrList.LenSource(addrlist.DHT)
	s.Addresses.PEX = t.addrList.LenSource(addrlist.PEX)
	s.Handshakes.Incoming = len(t.incomingHandshakers)
	s.Handshakes.Outgoing = len(t.outgoingHandshakers)
	s.Handshakes.Total = len(t.incomingHandshakers) + len(t.outgoingHandshakers)
	s.Peers.Total = len(t.peers)
	s.Peers.Incoming = len(t.incomingPeers)
	s.Peers.Outgoing = len(t.outgoingPeers)
	s.MetadataDownloads.Total = len(t.infoDownloaders)
	s.MetadataDownloads.Snubbed = len(t.infoDownloadersSnubbed)
	s.MetadataDownloads.Running = len(t.infoDownloaders) - len(t.infoDownloadersSnubbed)
	s.Downloads.Total = len(t.pieceDownloaders)
	s.Downloads.Snubbed = len(t.pieceDownloadersSnubbed)
	s.Downloads.Choked = len(t.pieceDownloadersChoked)
	s.Downloads.Running = len(t.pieceDownloaders) - len(t.pieceDownloadersChoked) - len(t.pieceDownloadersSnubbed)
	s.Pieces.Available = t.avaliablePieceCount()
	s.Bytes.Downloaded = t.byteStats.BytesDownloaded
	s.Bytes.Uploaded = t.byteStats.BytesUploaded
	s.Bytes.Wasted = t.byteStats.BytesWasted
	s.SecondsSeeded = int(t.byteStats.SecondsSeeded)
	s.Bytes.Allocated = t.bytesAllocated
	s.Pieces.Checked = t.checkedPieces

	if t.info != nil {
		s.Bytes.Total = t.info.TotalLength
		s.Bytes.Complete = t.bytesComplete()
		s.Bytes.Incomplete = s.Bytes.Total - s.Bytes.Complete

		s.Name = t.info.Name
		s.Private = (t.info.Private == 1)
		s.PieceLength = t.info.PieceLength
	} else {
		// Some trackers don't send any peer address if don't tell we have missing bytes.
		s.Bytes.Incomplete = math.MaxUint32

		s.Name = t.name
	}
	if t.bitfield != nil {
		s.Pieces.Total = t.bitfield.Len()
		s.Pieces.Have = t.bitfield.Count()
		s.Pieces.Missing = s.Pieces.Total - s.Pieces.Have
	}
	return s
}

func (t *torrent) avaliablePieceCount() uint32 {
	if t.piecePicker == nil {
		return 0
	}
	return t.piecePicker.Available()
}

func (t *torrent) bytesComplete() int64 {
	if t.bitfield == nil || len(t.pieces) == 0 {
		return 0
	}
	n := int64(t.info.PieceLength) * int64(t.bitfield.Count())
	if t.bitfield.Test(t.bitfield.Len() - 1) {
		n -= int64(t.info.PieceLength)
		n += int64(t.pieces[t.bitfield.Len()-1].Length)
	}
	return n
}

func (t *torrent) getTrackers() []Tracker {
	var trackers []Tracker
	for _, an := range t.announcers {
		st := an.Stats()
		t := Tracker{
			URL:      an.Tracker.URL(),
			Status:   TrackerStatus(st.Status),
			Seeders:  st.Seeders,
			Leechers: st.Leechers,
			Error:    st.Error,
		}
		trackers = append(trackers, t)
	}
	return trackers
}

func (t *torrent) getPeers() []Peer {
	var peers []Peer
	for pe := range t.peers {
		p := Peer{
			Addr: pe.Addr(),
		}
		peers = append(peers, p)
	}
	return peers
}

func (t *torrent) updateSecondsSeeded() {
	if t.status() != Seeding {
		t.secondsSeededUpdatedAt = time.Time{}
		return
	}
	if t.secondsSeededUpdatedAt.IsZero() {
		t.secondsSeededUpdatedAt = time.Now()
		return
	}
	now := time.Now()
	passed := now.Sub(t.secondsSeededUpdatedAt)
	t.byteStats.SecondsSeeded += uint32(passed / time.Second)
	t.secondsSeededUpdatedAt = now
}
<commit_msg>update seconds seeded before getting stats<commit_after>package session

import (
	"math"
	"time"

	"github.com/cenkalti/rain/internal/addrlist"
)

// Stats contains statistics about Torrent.
type Stats struct {
	// Status of the torrent.
	Status TorrentStatus
	// Contains the error message if torrent is stopped unexpectedly.
	Error  error
	Pieces struct {
		// Number of pieces that are checked when torrent is in "Verifying" state.
		Checked uint32
		// Number of pieces that we are downloaded successfully and verivied by hash check.
		Have uint32
		// Number of pieces that need to be downloaded. Some of them may be being downloaded.
		// Pieces that are being downloaded may counted as missing until they are downloaded and passed hash check.
		Missing uint32
		// Number of unique pieces available on swarm.
		// If this number is less then the number of total pieces, the download may never finish.
		Available uint32
		// Number of total pieces in torrent.
		Total uint32
	}
	Bytes struct {
		// Bytes that are downloaded and passed hash check.
		Complete int64
		// The number of bytes that is needed to complete all missing pieces.
		Incomplete int64
		// The number of total bytes of files in torrent.  Total = Complete + Incomplete
		Total int64
		// Downloaded is the number of bytes downloaded from swarm.
		// Because some pieces may be downloaded more than once, this number may be greater than completed bytes.
		Downloaded int64
		// BytesUploaded is the number of bytes uploaded to the swarm.
		Uploaded int64
		// Bytes downloaded due to duplicate/non-requested pieces.
		Wasted int64
		// Bytes allocated on storage.
		Allocated int64
	}
	Peers struct {
		// Number of peers that are connected, handshaked and ready to send and receive messages.
		Total int
		// Number of peers that have connected to us.
		Incoming int
		// Number of peers that we have connected to.
		Outgoing int
	}
	Handshakes struct {
		// Number of peers that are not handshaked yet.
		Total int
		// Number of incoming peers in handshake state.
		Incoming int
		// Number of outgoing peers in handshake state.
		Outgoing int
	}
	Addresses struct {
		// Total number of peer addresses that are ready to be connected.
		Total int
		// Peers found via trackers.
		Tracker int
		// Peers found via DHT node.
		DHT int
		// Peers found via peer exchange.
		PEX int
	}
	Downloads struct {
		// Number of active piece downloads.
		Total int
		// Number of pieces that are being downloaded normally.
		Running int
		// Number of pieces that are being downloaded too slow.
		Snubbed int
		// Number of piece downloads in choked state.
		Choked int
	}
	MetadataDownloads struct {
		// Number of active metadata downloads.
		Total int
		// Number of peers that uploading too slow.
		Snubbed int
		// Number of peers that are being downloaded normally.
		Running int
	}
	// Name can change after metadata is downloaded.
	Name string
	// Is private torrent?
	Private bool
	// Length of a single piece.
	PieceLength uint32
	// Number of seconds that torrent has seeded.
	SecondsSeeded int
}

func (t *torrent) stats() Stats {
	t.updateSecondsSeeded()

	var s Stats
	s.Status = t.status()
	s.Error = t.lastError
	s.Addresses.Total = t.addrList.Len()
	s.Addresses.Tracker = t.addrList.LenSource(addrlist.Tracker)
	s.Addresses.DHT = t.addrList.LenSource(addrlist.DHT)
	s.Addresses.PEX = t.addrList.LenSource(addrlist.PEX)
	s.Handshakes.Incoming = len(t.incomingHandshakers)
	s.Handshakes.Outgoing = len(t.outgoingHandshakers)
	s.Handshakes.Total = len(t.incomingHandshakers) + len(t.outgoingHandshakers)
	s.Peers.Total = len(t.peers)
	s.Peers.Incoming = len(t.incomingPeers)
	s.Peers.Outgoing = len(t.outgoingPeers)
	s.MetadataDownloads.Total = len(t.infoDownloaders)
	s.MetadataDownloads.Snubbed = len(t.infoDownloadersSnubbed)
	s.MetadataDownloads.Running = len(t.infoDownloaders) - len(t.infoDownloadersSnubbed)
	s.Downloads.Total = len(t.pieceDownloaders)
	s.Downloads.Snubbed = len(t.pieceDownloadersSnubbed)
	s.Downloads.Choked = len(t.pieceDownloadersChoked)
	s.Downloads.Running = len(t.pieceDownloaders) - len(t.pieceDownloadersChoked) - len(t.pieceDownloadersSnubbed)
	s.Pieces.Available = t.avaliablePieceCount()
	s.Bytes.Downloaded = t.byteStats.BytesDownloaded
	s.Bytes.Uploaded = t.byteStats.BytesUploaded
	s.Bytes.Wasted = t.byteStats.BytesWasted
	s.SecondsSeeded = int(t.byteStats.SecondsSeeded)
	s.Bytes.Allocated = t.bytesAllocated
	s.Pieces.Checked = t.checkedPieces

	if t.info != nil {
		s.Bytes.Total = t.info.TotalLength
		s.Bytes.Complete = t.bytesComplete()
		s.Bytes.Incomplete = s.Bytes.Total - s.Bytes.Complete

		s.Name = t.info.Name
		s.Private = (t.info.Private == 1)
		s.PieceLength = t.info.PieceLength
	} else {
		// Some trackers don't send any peer address if don't tell we have missing bytes.
		s.Bytes.Incomplete = math.MaxUint32

		s.Name = t.name
	}
	if t.bitfield != nil {
		s.Pieces.Total = t.bitfield.Len()
		s.Pieces.Have = t.bitfield.Count()
		s.Pieces.Missing = s.Pieces.Total - s.Pieces.Have
	}
	return s
}

func (t *torrent) avaliablePieceCount() uint32 {
	if t.piecePicker == nil {
		return 0
	}
	return t.piecePicker.Available()
}

func (t *torrent) bytesComplete() int64 {
	if t.bitfield == nil || len(t.pieces) == 0 {
		return 0
	}
	n := int64(t.info.PieceLength) * int64(t.bitfield.Count())
	if t.bitfield.Test(t.bitfield.Len() - 1) {
		n -= int64(t.info.PieceLength)
		n += int64(t.pieces[t.bitfield.Len()-1].Length)
	}
	return n
}

func (t *torrent) getTrackers() []Tracker {
	var trackers []Tracker
	for _, an := range t.announcers {
		st := an.Stats()
		t := Tracker{
			URL:      an.Tracker.URL(),
			Status:   TrackerStatus(st.Status),
			Seeders:  st.Seeders,
			Leechers: st.Leechers,
			Error:    st.Error,
		}
		trackers = append(trackers, t)
	}
	return trackers
}

func (t *torrent) getPeers() []Peer {
	var peers []Peer
	for pe := range t.peers {
		p := Peer{
			Addr: pe.Addr(),
		}
		peers = append(peers, p)
	}
	return peers
}

func (t *torrent) updateSecondsSeeded() {
	if t.status() != Seeding {
		t.secondsSeededUpdatedAt = time.Time{}
		return
	}
	if t.secondsSeededUpdatedAt.IsZero() {
		t.secondsSeededUpdatedAt = time.Now()
		return
	}
	now := time.Now()
	passed := now.Sub(t.secondsSeededUpdatedAt)
	t.byteStats.SecondsSeeded += uint32(passed / time.Second)
	t.secondsSeededUpdatedAt = now
}
<|endoftext|>


<commit_before>package sesstype

import (
	"fmt"
	"os"
)

var (
	cfsmStateCount = make(map[string]int)    // Contains next state number for each CFSM.
	cfsmByName     = make(map[string]int)    // Converts role names to CFSM state number.
	labelJumpState = make(map[string]string) // Convert label names to state names to jump to.
	totalCFSMs     = 0                       // Number of CFSMs.
	chanCFSMs      = 0                       // Number of CFSMs for channels.
)

func genNewState(roleName string) string {
	stateIdx := cfsmStateCount[roleName]
	cfsmStateCount[roleName]++
	return fmt.Sprintf("%sZZ%d", roleName, stateIdx)
}

func isAlphanum(r rune) bool {
	return ('0' <= r && r <= '9') || ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z')
}

// Encode non-alphanum symbols to empty.
func encodeSymbols(name string) string {
	outstr := ""
	for _, runeVal := range name {
		if isAlphanum(runeVal) {
			outstr += string(runeVal)
		} else {
			switch runeVal {
			case '{':
				outstr += "LBRACE"
			case '}':
				outstr += "RBRACE"
			case '.':
				outstr += "DOT"
			case '(':
				outstr += "LPAREN"
			case ')':
				outstr += "RPAREN"
			case '/':
				outstr += "SLASH"
			}
		}
		// Ignore other non alphanum
	}
	return outstr
}

// Create CFSM for channel.
func genChanCFSM(name string, typ string, begin int, end int) string {
	q0 := fmt.Sprintf("Chan%s", encodeSymbols(genNewState(name)))
	qTerm := fmt.Sprintf("Close%s", encodeSymbols(genNewState(name)))
	cfsm := ""
	for i := begin; i < end; i++ {
		q1 := encodeSymbols(genNewState(name))
		cfsm += fmt.Sprintf("%s %d ? %s %s\n", q0, i, encodeSymbols(typ), q1)
		for j := begin; j < end; j++ {
			if i != j {
				cfsm += fmt.Sprintf("%s %d ! %s %s\n", q1, j, encodeSymbols(typ), q0)
			}
		}
		cfsm += fmt.Sprintf("%s %d ? STOP %s\n", q0, i, qTerm)
		for j := begin; j < end; j++ {
			if i != j {
				cfsm += fmt.Sprintf("%s %d ! STOP %s\n", qTerm, j, qTerm)
			}
		}
	}

	return fmt.Sprintf(".outputs\n.state graph\n%s.marking %s\n.end\n\n", cfsm, q0)
}

// nodeToCFSM creates CFSM states from sesstype.Node. q0 is already written.
func nodeToCFSM(root Node, role Role, q0 string, initial bool) string {
	switch node := root.(type) {
	case *SendNode:
		toCFSM, ok := cfsmByName[node.dest.Name()]
		if !ok {
			panic(fmt.Sprintf("Sending to unknown channel: %s", node.dest.Name()))
		}

		sendType := encodeSymbols(node.dest.Type().String())
		qSend := encodeSymbols(genNewState(role.Name()))
		cfsm := fmt.Sprintf("%s %d ! %s ", q0, toCFSM, sendType)
		if !initial {
			cfsm = fmt.Sprintf("%s\n%s", q0, cfsm)
		}
		childCfsm := ""
		for _, child := range node.Children() {
			childCfsm += nodeToCFSM(child, role, qSend, false)
		}
		if childCfsm == "" {
			return fmt.Sprintf("%s %s\n", cfsm, qSend)
		}
		return fmt.Sprintf("%s %s", cfsm, childCfsm)

	case *RecvNode:
		fromCFSM, ok := cfsmByName[node.orig.Name()]
		if !ok {
			panic(fmt.Sprintf("Receiving from unknown channel: %s", node.orig.Name()))
		}

		recvType := encodeSymbols(node.orig.Type().String())
		qRecv := encodeSymbols(genNewState(role.Name()))
		cfsm := fmt.Sprintf("%s %d ? %s ", q0, fromCFSM, recvType)
		if !initial {
			cfsm = fmt.Sprintf("%s\n%s", q0, cfsm)
		}
		childCfsm := ""
		for _, child := range node.Children() {
			childCfsm += nodeToCFSM(child, role, qRecv, false)
		}
		if childCfsm == "" {
			return fmt.Sprintf("%s %s\n", cfsm, qRecv)
		}
		return fmt.Sprintf("%s %s", cfsm, childCfsm)

	case *EndNode:
		endCFSM, ok := cfsmByName[node.ch.Name()]
		if !ok {
			panic(fmt.Sprintf("Closing unknown channel: %s", node.ch.Name()))
		}

		qEnd := encodeSymbols(genNewState(role.Name()))
		cfsm := fmt.Sprintf("%s %d ! STOP %s", q0, endCFSM, qEnd)
		if !initial {
			cfsm = fmt.Sprintf("END %s\n%s", q0, cfsm)
		}
		childCfsm := ""
		for _, child := range node.Children() {
			childCfsm += nodeToCFSM(child, role, qEnd, false)
		}
		if childCfsm == "" {
			return fmt.Sprintf("%s %s\n", cfsm, qEnd)
		}
		return fmt.Sprintf("%s %s", cfsm, childCfsm)

	case *NewChanNode, *EmptyBodyNode:
		cfsm := ""
		for _, child := range node.Children() {
			cfsm += nodeToCFSM(child, role, q0, initial)
		}
		return cfsm

	case *LabelNode:
		labelJumpState[node.name] = q0
		cfsm := ""
		for _, child := range node.Children() {
			cfsm += nodeToCFSM(child, role, q0, initial)
		}
		return cfsm

	case *GotoNode:
		qJumpto := labelJumpState[node.name]
		cfsm := ""
		for _, child := range node.Children() {
			cfsm += nodeToCFSM(child, role, qJumpto, initial)
		}
		return cfsm

	default:
		panic(fmt.Sprintf("Unhandled node type: %T", node))
	}
}

func genCFSM(role Role, root Node) string {
	q0 := encodeSymbols(genNewState(role.Name()))
	cfsmBody := nodeToCFSM(root, role, q0, true)
	if cfsmBody == "" {
		return ""
	}
	return fmt.Sprintf(".outputs\n.state graph\n%s.marking %s\n.end\n\n", cfsmBody, q0)
}

// Initialise the CFSM counts.
func initCFSMs(s *Session) {
	for _, c := range s.Chans {
		cfsmByName[c.Name()] = totalCFSMs
		chanCFSMs++
		totalCFSMs++
	}

	for r := range s.Types {
		cfsmByName[r.Name()] = totalCFSMs
		totalCFSMs++
	}
}

func PrintCFSMSummary() {
	fmt.Printf("Total of %d CFSMs (%d are channels)\n", totalCFSMs, chanCFSMs)
	for cfsmName, cfsmIndex := range cfsmByName {
		if cfsmIndex < chanCFSMs {
			fmt.Printf("\t%d\t= %s (channel)\n", cfsmIndex, cfsmName)
		} else {
			fmt.Printf("\t%d\t= %s\n", cfsmIndex, cfsmName)
		}
	}
}

// GenAllCFSMs generates CFSMs for all roles in the session, plus the static
// CFSMs for the channels.
func GenAllCFSMs(s *Session) {
	initCFSMs(s)

	allCFSMs := ""
	goroutineCFSMs := ""
	chanCFSMs := ""
	nonEmptyCFSMs := 0

	for r, root := range s.Types {
		cfsm := genCFSM(r, root)
		fmt.Fprintf(os.Stderr, "Generate %s CFSM\n", r.Name())
		if cfsm == "" {
			fmt.Fprintf(os.Stderr, "  ^ Empty\n")
		}
		if cfsm != "" {
			nonEmptyCFSMs++
			goroutineCFSMs += cfsm
		}
	}

	for _, c := range s.Chans {
		chanCFSMs += genChanCFSM(c.Name(), c.Type().String(), len(s.Chans), len(s.Chans)+nonEmptyCFSMs)
	}

	allCFSMs = chanCFSMs + goroutineCFSMs

	f, err := os.OpenFile("output_cfsms", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	defer f.Close()

	if err != nil {
		panic(err)
	}

	_, err = f.WriteString(allCFSMs)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Fix (sometimes) superflous CFSM q0 output<commit_after>package sesstype

import (
	"fmt"
	"os"
)

var (
	cfsmStateCount = make(map[string]int)    // Contains next state number for each CFSM.
	cfsmByName     = make(map[string]int)    // Converts role names to CFSM state number.
	labelJumpState = make(map[string]string) // Convert label names to state names to jump to.
	totalCFSMs     = 0                       // Number of CFSMs.
	chanCFSMs      = 0                       // Number of CFSMs for channels.
)

func genNewState(roleName string) string {
	stateIdx := cfsmStateCount[roleName]
	cfsmStateCount[roleName]++
	//return fmt.Sprintf("q%d%d", cfsmByName[roleName], stateIdx)
	return fmt.Sprintf("%s%d", roleName, stateIdx)
}

func isAlphanum(r rune) bool {
	return ('0' <= r && r <= '9') || ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z')
}

// Encode non-alphanum symbols to empty.
func encodeSymbols(name string) string {
	return name
	/*
		outstr := ""
		for _, runeVal := range name {
			if isAlphanum(runeVal) {
				outstr += string(runeVal)
			} else {
				switch runeVal {
				case '{':
					outstr += "LBRACE"
				case '}':
					outstr += "RBRACE"
				case '.':
					outstr += "DOT"
				case '(':
					outstr += "LPAREN"
				case ')':
					outstr += "RPAREN"
				case '/':
					outstr += "SLASH"
				}
			}
			// Ignore other non alphanum
		}
		return outstr
	*/
}

// Create CFSM for channel.
func genChanCFSM(name string, typ string, begin int, end int) string {
	q0 := fmt.Sprintf("%s", encodeSymbols(genNewState(name)))
	qTerm := fmt.Sprintf("%s", encodeSymbols(genNewState(name)))
	cfsm := ""
	for i := begin; i < end; i++ {
		q1 := encodeSymbols(genNewState(name))
		cfsm += fmt.Sprintf("%s %d ? %s %s\n", q0, i, encodeSymbols(typ), q1)
		for j := begin; j < end; j++ {
			if i != j {
				cfsm += fmt.Sprintf("%s %d ! %s %s\n", q1, j, encodeSymbols(typ), q0)
			}
		}
		cfsm += fmt.Sprintf("%s %d ? STOP %s\n", q0, i, qTerm)
		for j := begin; j < end; j++ {
			if i != j {
				cfsm += fmt.Sprintf("%s %d ! STOP %s\n", qTerm, j, qTerm)
			}
		}
	}

	return fmt.Sprintf(".outputs\n.state graph\n%s.marking %s\n.end\n\n", cfsm, q0)
}

// nodeToCFSM creates CFSM states from sesstype.Node. q0 is already written.
func nodeToCFSM(root Node, role Role, q0 string, initial bool) string {
	switch node := root.(type) {
	case *SendNode:
		toCFSM, ok := cfsmByName[node.dest.Name()]
		if !ok {
			panic(fmt.Sprintf("Sending to unknown channel: %s", node.dest.Name()))
		}

		sendType := encodeSymbols(node.dest.Type().String())
		qSend := encodeSymbols(genNewState(role.Name()))
		cfsm := fmt.Sprintf("%s %d ! %s ", q0, toCFSM, sendType)
		if !initial {
			cfsm = fmt.Sprintf("%s\n%s", q0, cfsm)
		}
		childrenCfsm := ""
		childInit := false
		for _, child := range node.Children() {
			childCfsm := nodeToCFSM(child, role, qSend, childInit)
			childInit = (childCfsm != "")
			childrenCfsm += childCfsm
		}
		if childrenCfsm == "" {
			return fmt.Sprintf("%s%s\n", cfsm, qSend)
		}
		return fmt.Sprintf("%s %s", cfsm, childrenCfsm)

	case *RecvNode:
		fromCFSM, ok := cfsmByName[node.orig.Name()]
		if !ok {
			panic(fmt.Sprintf("Receiving from unknown channel: %s", node.orig.Name()))
		}

		recvType := encodeSymbols(node.orig.Type().String())
		qRecv := encodeSymbols(genNewState(role.Name()))
		cfsm := fmt.Sprintf("%s %d ? %s ", q0, fromCFSM, recvType)
		if !initial {
			cfsm = fmt.Sprintf("%s\n%s", q0, cfsm)
		}
		childrenCfsm, childInit := "", false
		for _, child := range node.Children() {
			childCfsm := nodeToCFSM(child, role, qRecv, childInit)
			childInit = (childCfsm != "")
			childrenCfsm += childCfsm
		}
		if childrenCfsm == "" {
			return fmt.Sprintf("%s%s\n", cfsm, qRecv)
		}
		return fmt.Sprintf("%s %s", cfsm, childrenCfsm)

	case *EndNode:
		endCFSM, ok := cfsmByName[node.ch.Name()]
		if !ok {
			panic(fmt.Sprintf("Closing unknown channel: %s", node.ch.Name()))
		}

		qEnd := encodeSymbols(genNewState(role.Name()))
		cfsm := fmt.Sprintf("%s %d ! STOP ", q0, endCFSM)
		if !initial {
			cfsm = fmt.Sprintf("%s\n%s", q0, cfsm)
		}
		childrenCfsm, childInit := "", false
		for _, child := range node.Children() {
			childCfsm := nodeToCFSM(child, role, qEnd, childInit)
			childInit = (childCfsm != "")
			childrenCfsm += childCfsm
		}
		if childrenCfsm == "" {
			return fmt.Sprintf("%s%s\n", cfsm, qEnd)
		}
		return fmt.Sprintf("%s %s", cfsm, childrenCfsm)

	case *NewChanNode, *EmptyBodyNode:
		cfsm, childInit := "", initial
		for _, child := range node.Children() {
			childCfsm := nodeToCFSM(child, role, q0, childInit)
			childInit = (childCfsm != "" || initial)
			cfsm += childCfsm
		}
		return cfsm

	case *LabelNode:
		labelJumpState[node.name] = q0
		cfsm, childInit := "", initial
		for _, child := range node.Children() {
			childCfsm := nodeToCFSM(child, role, q0, childInit)
			childInit = (childCfsm != "" || initial)
			cfsm += childCfsm
		}
		return cfsm

	case *GotoNode:
		qJumpto := labelJumpState[node.name]
		cfsm, childInit := "", initial
		for _, child := range node.Children() {
			// qJumpto written, so initial again
			childCfsm := nodeToCFSM(child, role, qJumpto, childInit)
			childInit = (childCfsm != "" || initial)
			cfsm += childCfsm
		}
		return cfsm

	default:
		panic(fmt.Sprintf("Unhandled node type: %T", node))
	}
}

func genCFSM(role Role, root Node) string {
	q0 := encodeSymbols(genNewState(role.Name()))
	cfsmBody := nodeToCFSM(root, role, q0, true)
	if cfsmBody == "" {
		return ""
	}
	return fmt.Sprintf(".outputs\n.state graph\n%s.marking %s\n.end\n\n", cfsmBody, q0)
}

// Initialise the CFSM counts.
func initCFSMs(s *Session) {
	for _, c := range s.Chans {
		cfsmByName[c.Name()] = totalCFSMs
		chanCFSMs++
		totalCFSMs++
	}

	for r := range s.Types {
		cfsmByName[r.Name()] = totalCFSMs
		totalCFSMs++
	}
}

func PrintCFSMSummary() {
	fmt.Printf("Total of %d CFSMs (%d are channels)\n", totalCFSMs, chanCFSMs)
	for cfsmName, cfsmIndex := range cfsmByName {
		if cfsmIndex < chanCFSMs {
			fmt.Printf("\t%d\t= %s (channel)\n", cfsmIndex, cfsmName)
		} else {
			fmt.Printf("\t%d\t= %s\n", cfsmIndex, cfsmName)
		}
	}
}

// GenAllCFSMs generates CFSMs for all roles in the session, plus the static
// CFSMs for the channels.
func GenAllCFSMs(s *Session) {
	initCFSMs(s)

	allCFSMs := ""
	goroutineCFSMs := ""
	chanCFSMs := ""
	nonEmptyCFSMs := 0

	for r, root := range s.Types {
		cfsm := genCFSM(r, root)
		fmt.Fprintf(os.Stderr, "Generate %s CFSM\n", r.Name())
		if cfsm == "" {
			fmt.Fprintf(os.Stderr, "  ^ Empty\n")
		}
		if cfsm != "" {
			nonEmptyCFSMs++
			goroutineCFSMs += cfsm
		}
	}

	for _, c := range s.Chans {
		chanCFSMs += genChanCFSM(c.Name(), c.Type().String(), len(s.Chans), len(s.Chans)+nonEmptyCFSMs)
	}

	allCFSMs = chanCFSMs + goroutineCFSMs

	f, err := os.OpenFile("output_cfsms", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	defer f.Close()

	if err != nil {
		panic(err)
	}

	_, err = f.WriteString(allCFSMs)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package slinga

import (
	"fmt"
	"github.com/golang/glog"
)

// Attach or detach user to a service
type ServiceUsageUserAction struct {
	ComponentKey string
	User         string
}

// Difference between two usage states
type ServiceUsageStateDiff struct {
	// Pointers to previous and next states
	Prev *ServiceUsageState
	Next *ServiceUsageState

	// Actions that need to be taken
	ComponentInstantiate map[string]bool
	ComponentDestruct    map[string]bool
	ComponentAttachUser  []ServiceUsageUserAction
	ComponentDetachUser  []ServiceUsageUserAction
}

// Calculate difference between two usage states
func (next *ServiceUsageState) CalculateDifference(prev *ServiceUsageState) ServiceUsageStateDiff {
	// resulting difference
	result := ServiceUsageStateDiff{
		Prev:                 prev,
		Next:                 next,
		ComponentInstantiate: make(map[string]bool),
		ComponentDestruct:    make(map[string]bool)}

	// map of all instances
	allKeys := make(map[string]bool)

	// merge all the keys
	for k, _ := range prev.ResolvedLinks {
		allKeys[k] = true
	}
	for k, _ := range next.ResolvedLinks {
		allKeys[k] = true
	}

	// go over all the keys and see which one appear and which one disappear
	for k, _ := range allKeys {
		uPrev := prev.ResolvedLinks[k]
		uNext := next.ResolvedLinks[k]

		var userIdsPrev []string
		if uPrev != nil {
			userIdsPrev = uPrev.UserIds
		}

		var userIdsNext []string
		if uNext != nil {
			userIdsNext = uNext.UserIds
		}

		// see if a component needs to be instantiated
		if userIdsPrev == nil && userIdsNext != nil {
			result.ComponentInstantiate[k] = true
		}

		// see if a component needs to be destructed
		if userIdsPrev != nil && userIdsNext == nil {
			result.ComponentDestruct[k] = true
		}

		// see what needs to happen to users
		uPrevIdsMap := toMap(userIdsPrev)
		uNextIdsMap := toMap(userIdsNext)

		// see if a user needs to be detached from a component
		for u, _ := range uPrevIdsMap {
			if !uNextIdsMap[u] {
				result.ComponentDetachUser = append(result.ComponentDetachUser, ServiceUsageUserAction{ComponentKey: k, User: u})
			}
		}

		// see if a user needs to be attached to a component
		for u, _ := range uNextIdsMap {
			if !uPrevIdsMap[u] {
				result.ComponentAttachUser = append(result.ComponentAttachUser, ServiceUsageUserAction{ComponentKey: k, User: u})
			}
		}
	}

	return result
}

func toMap(p []string) map[string]bool {
	result := make(map[string]bool)
	for _, s := range p {
		result[s] = true
	}
	return result
}

func (diff ServiceUsageStateDiff) isEmpty() bool {
	if len(diff.ComponentInstantiate) > 0 {
		return false
	}
	if len(diff.ComponentAttachUser) > 0 {
		return false
	}
	if len(diff.ComponentDetachUser) > 0 {
		return false
	}
	if len(diff.ComponentDestruct) > 0 {
		return false
	}
	return true
}

func (diff ServiceUsageStateDiff) Print() {
	if len(diff.ComponentInstantiate) > 0 {
		fmt.Println("New components to instantiate:")
		for k, _ := range diff.ComponentInstantiate {
			fmt.Println("[+] " + k)
		}
	}

	if len(diff.ComponentAttachUser) > 0 {
		fmt.Println("Add users for components:")
		for _, cu := range diff.ComponentAttachUser {
			fmt.Println("[+] " + cu.User + " -> " + cu.ComponentKey)
		}
	}

	if len(diff.ComponentDetachUser) > 0 {
		fmt.Println("Delete users for components:")
		for _, cu := range diff.ComponentDetachUser {
			fmt.Println("[-] " + cu.User + " -> " + cu.ComponentKey)
		}
	}

	if len(diff.ComponentDestruct) > 0 {
		fmt.Println("Components to destruct (no usage):")
		for k, _ := range diff.ComponentDestruct {
			fmt.Println("[-] " + k)
		}
	}

	if diff.isEmpty() {
		fmt.Println("[*] No changes to apply")
	}
}

func (diff ServiceUsageStateDiff) Apply() {

	// Process destructions in the right order
	for _, key := range diff.Prev.ProcessingOrder {
		// Does it need to be destructed?
		if _, ok := diff.ComponentDestruct[key]; ok {
			serviceName, _ /*contextName*/, _ /*allocationName*/, componentName := parseServiceUsageKey(key)
			component := diff.Prev.Policy.Services[serviceName].getComponentsMap()[componentName]
			if component == nil {
				glog.Infof("Destructing service: %s", serviceName)
				// TODO: add processing code
			} else {
				glog.Infof("Destructing component: %s (%s)", component.Name, component.Code)

				codeExecutor, err := component.Code.GetCodeExecutor()
				if err != nil {
					glog.Fatal("Error while getting codeExecutor")
				}
				codeExecutor.Destroy(key)
			}
		}
	}

	// Process instantiations in the right order
	for _, key := range diff.Next.ProcessingOrder {
		// Does it need to be instantiated?
		if _, ok := diff.ComponentInstantiate[key]; ok {
			serviceName, _ /*contextName*/, _ /*allocationName*/, componentName := parseServiceUsageKey(key)
			component := diff.Next.Policy.Services[serviceName].getComponentsMap()[componentName]
			 labels := diff.Next.ResolvedLinks[key].CalculatedLabels

			if component == nil {
				glog.Infof("Instantiating service: %s (%s)", serviceName, key)
				// TODO: add processing code
			} else {
				glog.Infof("Instantiating component: %s (%s)", component.Name, key)

				codeExecutor, err := component.Code.GetCodeExecutor()
				if err != nil {
					glog.Fatal("Error while getting codeExecutor")
				}
				codeExecutor.Install(key, labels)
			}
		}
	}

	// save new state
	diff.Next.SaveServiceUsageState()
}
<commit_msg>Fix Code commands execution<commit_after>package slinga

import (
	"fmt"
	"github.com/golang/glog"
)

// Attach or detach user to a service
type ServiceUsageUserAction struct {
	ComponentKey string
	User         string
}

// Difference between two usage states
type ServiceUsageStateDiff struct {
	// Pointers to previous and next states
	Prev *ServiceUsageState
	Next *ServiceUsageState

	// Actions that need to be taken
	ComponentInstantiate map[string]bool
	ComponentDestruct    map[string]bool
	ComponentAttachUser  []ServiceUsageUserAction
	ComponentDetachUser  []ServiceUsageUserAction
}

// Calculate difference between two usage states
func (next *ServiceUsageState) CalculateDifference(prev *ServiceUsageState) ServiceUsageStateDiff {
	// resulting difference
	result := ServiceUsageStateDiff{
		Prev:                 prev,
		Next:                 next,
		ComponentInstantiate: make(map[string]bool),
		ComponentDestruct:    make(map[string]bool)}

	// map of all instances
	allKeys := make(map[string]bool)

	// merge all the keys
	for k, _ := range prev.ResolvedLinks {
		allKeys[k] = true
	}
	for k, _ := range next.ResolvedLinks {
		allKeys[k] = true
	}

	// go over all the keys and see which one appear and which one disappear
	for k, _ := range allKeys {
		uPrev := prev.ResolvedLinks[k]
		uNext := next.ResolvedLinks[k]

		var userIdsPrev []string
		if uPrev != nil {
			userIdsPrev = uPrev.UserIds
		}

		var userIdsNext []string
		if uNext != nil {
			userIdsNext = uNext.UserIds
		}

		// see if a component needs to be instantiated
		if userIdsPrev == nil && userIdsNext != nil {
			result.ComponentInstantiate[k] = true
		}

		// see if a component needs to be destructed
		if userIdsPrev != nil && userIdsNext == nil {
			result.ComponentDestruct[k] = true
		}

		// see what needs to happen to users
		uPrevIdsMap := toMap(userIdsPrev)
		uNextIdsMap := toMap(userIdsNext)

		// see if a user needs to be detached from a component
		for u, _ := range uPrevIdsMap {
			if !uNextIdsMap[u] {
				result.ComponentDetachUser = append(result.ComponentDetachUser, ServiceUsageUserAction{ComponentKey: k, User: u})
			}
		}

		// see if a user needs to be attached to a component
		for u, _ := range uNextIdsMap {
			if !uPrevIdsMap[u] {
				result.ComponentAttachUser = append(result.ComponentAttachUser, ServiceUsageUserAction{ComponentKey: k, User: u})
			}
		}
	}

	return result
}

func toMap(p []string) map[string]bool {
	result := make(map[string]bool)
	for _, s := range p {
		result[s] = true
	}
	return result
}

func (diff ServiceUsageStateDiff) isEmpty() bool {
	if len(diff.ComponentInstantiate) > 0 {
		return false
	}
	if len(diff.ComponentAttachUser) > 0 {
		return false
	}
	if len(diff.ComponentDetachUser) > 0 {
		return false
	}
	if len(diff.ComponentDestruct) > 0 {
		return false
	}
	return true
}

func (diff ServiceUsageStateDiff) Print() {
	if len(diff.ComponentInstantiate) > 0 {
		fmt.Println("New components to instantiate:")
		for k, _ := range diff.ComponentInstantiate {
			fmt.Println("[+] " + k)
		}
	}

	if len(diff.ComponentAttachUser) > 0 {
		fmt.Println("Add users for components:")
		for _, cu := range diff.ComponentAttachUser {
			fmt.Println("[+] " + cu.User + " -> " + cu.ComponentKey)
		}
	}

	if len(diff.ComponentDetachUser) > 0 {
		fmt.Println("Delete users for components:")
		for _, cu := range diff.ComponentDetachUser {
			fmt.Println("[-] " + cu.User + " -> " + cu.ComponentKey)
		}
	}

	if len(diff.ComponentDestruct) > 0 {
		fmt.Println("Components to destruct (no usage):")
		for k, _ := range diff.ComponentDestruct {
			fmt.Println("[-] " + k)
		}
	}

	if diff.isEmpty() {
		fmt.Println("[*] No changes to apply")
	}
}

func (diff ServiceUsageStateDiff) Apply() {

	// Process destructions in the right order
	for _, key := range diff.Prev.ProcessingOrder {
		// Does it need to be destructed?
		if _, ok := diff.ComponentDestruct[key]; ok {
			serviceName, _ /*contextName*/, _ /*allocationName*/, componentName := parseServiceUsageKey(key)
			component := diff.Prev.Policy.Services[serviceName].getComponentsMap()[componentName]
			if component == nil {
				glog.Infof("Destructing service: %s", serviceName)
				// TODO: add processing code
			} else {
				glog.Infof("Destructing component: %s (%s)", component.Name, component.Code)

				if component.Code != nil {
					codeExecutor, err := component.Code.GetCodeExecutor()
					if err != nil {
						glog.Fatal("Error while getting codeExecutor")
					}
					codeExecutor.Destroy(key)
				}
			}
		}
	}

	// Process instantiations in the right order
	for _, key := range diff.Next.ProcessingOrder {
		// Does it need to be instantiated?
		if _, ok := diff.ComponentInstantiate[key]; ok {
			serviceName, _ /*contextName*/, _ /*allocationName*/, componentName := parseServiceUsageKey(key)
			component := diff.Next.Policy.Services[serviceName].getComponentsMap()[componentName]
			labels := diff.Next.ResolvedLinks[key].CalculatedLabels

			if component == nil {
				glog.Infof("Instantiating service: %s (%s)", serviceName, key)
				// TODO: add processing code
			} else {
				glog.Infof("Instantiating component: %s (%s)", component.Name, key)

				if component.Code != nil {
					codeExecutor, err := component.Code.GetCodeExecutor()
					if err != nil {
						glog.Fatal("Error while getting codeExecutor")
					}
					codeExecutor.Install(key, labels)
				}
			}
		}
	}

	// save new state
	diff.Next.SaveServiceUsageState()
}
<|endoftext|>


<commit_before>package f5

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
)

// Paths for file upload.
const (
	PathUploadImage = "/mgmt/cm/autodeploy/software-image-uploads"
	PathUploadFile  = "/mgmt/shared/file-transfer/uploads"
	PathUploadUCS   = "mgmt/shared/file-transfer/ucs-uploads"

	// For backward compatibility
	// DEPRECATED
	UploadRESTPath = PathUploadFile
)

// Paths for file download.
const (
	PathDownloadUCS = "/mgmt/shared/file-transfer/ucs-downloads"
)

// DownloadUCS downloads an UCS file and writes its content to w.
func (c *Client) DownloadUCS(w io.Writer, filename string) (n int64, err error) {
	if n, err = c.download(w, PathDownloadUCS+"/"+filename); err != nil {
		return 0, fmt.Errorf("cannot download ucs file: %v", err)
	}
	return
}

func (c *Client) download(w io.Writer, restPath string) (n int64, err error) {
	resp, err := c.SendRequest("GET", restPath, nil)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	if err := c.ReadError(resp); err != nil {
		return 0, err
	}

	if n, err = io.Copy(w, resp.Body); err != nil {
		return 0, err
	}

	if resp.StatusCode == http.StatusPartialContent {
		contentRange := resp.Header.Get("Content-Range")

		parts := strings.Split(contentRange, "/")
		if len(parts) != 2 {
			return 0, errors.New("malformed Content-Range header")
		}
		filesize, err := strconv.ParseInt(parts[1], 10, 64)
		if err != nil {
			return 0, errors.New("malformed Content-Range header")
		}

		rangeParts := strings.Split(parts[0], "-")
		if len(rangeParts) != 2 {
			return 0, errors.New("malformed Content-Range header")
		}
		offset, err := strconv.ParseInt(rangeParts[1], 10, 64)
		if err != nil {
			return 0, errors.New("malformed Content-Range header")
		}

		np, err := c.downloadByChunks(w, restPath, filesize, offset+1, offset)
		if err != nil {
			return 0, err
		}
		n += np
	}

	return
}

func (c *Client) downloadByChunks(w io.Writer, restPath string, filesize, offset, chunkSize int64) (n int64, err error) {
	req, err := c.MakeRequest("GET", restPath, nil)
	if err != nil {
		return 0, err
	}

	// Bound limit to filesize
	limit := offset + chunkSize - 1
	if limit >= filesize {
		limit = filesize - 1
	}

	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", offset, limit, filesize))

	resp, err := c.Do(req)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	if err := c.ReadError(resp); err != nil {
		return 0, err
	}

	if n, err = io.Copy(w, resp.Body); err != nil {
		return 0, err
	}

	if limit < filesize-1 {
		nn, err := c.downloadByChunks(w, restPath, filesize, offset+chunkSize, chunkSize)
		if err != nil {
			return 0, err
		}
		n += nn
	}

	return
}

// An UploadResponse holds the responses send by the BigIP API while uploading
// files.
type UploadResponse struct {
	RemainingByteCount int64          `json:"remainingByteCount"`
	UsedChunks         map[string]int `json:"usedChunks"`
	TotalByteCount     int64          `json:"totalByteCount"`
	LocalFilePath      string         `json:"localFilePath"`
	TemporaryFilePath  string         `json:"temporaryFilePath"`
	Generation         int64          `json:"generation"`
	LastUpdateMicros   int64          `json:"lastUpdateMicros"`
}

// UploadFile reads the content of a file from r and uploads it to the BigIP.
// The uploaded file will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadFile(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadFile, filename, filesize)
}

// UploadImage reads the content of an disk image from r and uploads it to the
// BigIP.
//
// The uploaded image will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadImage(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadImage, filename, filesize)
}

// UploadUCS reads the content of an UCS archive from r and uploads it to the
// BigIP.
//
// The uploaded UCS archive will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadUCS(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadUCS, filename, filesize)
}

func (c *Client) upload(r io.Reader, restPath, filename string, filesize int64) (*UploadResponse, error) {
	var uploadResp UploadResponse
	for bytesSent := int64(0); bytesSent < filesize; {
		var chunk int64
		if remainingBytes := filesize - bytesSent; remainingBytes >= 512*1024 {
			chunk = 512 * 1024
		} else {
			chunk = remainingBytes
		}

		req, err := c.makeUploadRequest(restPath+"/"+filename, io.LimitReader(r, chunk), bytesSent, chunk, filesize)
		if err != nil {
			return nil, err
		}
		resp, err := c.Do(req)
		if err != nil {
			return nil, err
		}
		if err := c.ReadError(resp); err != nil {
			resp.Body.Close()
			return nil, err
		}

		if filesize-bytesSent <= 512*1024 {
			dec := json.NewDecoder(resp.Body)
			if err := dec.Decode(&uploadResp); err != nil {
				resp.Body.Close()
				return nil, err
			}
		}

		bytesSent += chunk
	}
	return &uploadResp, nil
}

// makeUploadRequest constructs a single upload request.
//
// restPath can be any of the Path* constants defined at the top of this file.
//
// The file to be uploaded is read from r and must not exceed 524288 bytes.
//
// off represents the number of bytes already sent while chunk is the size of
// chunk to be send in this request.
//
// filesize denotes the size of the entire file.
func (c *Client) makeUploadRequest(restPath string, r io.Reader, off, chunk, filesize int64) (*http.Request, error) {
	if chunk > 512*1024 {
		return nil, fmt.Errorf("chunk size greater than %d is not supported", 512*1024)
	}
	req, err := http.NewRequest("POST", c.makeURL(restPath), r)
	if err != nil {
		return nil, fmt.Errorf("failed to create F5 authenticated request: %v", err)
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", off, off+chunk-1, filesize))
	req.Header.Set("Content-Type", "application/octet-stream")
	if err := c.makeAuth(req); err != nil {
		return nil, err
	}
	return req, nil
}
<commit_msg>f5: fix UCS file download for BigIP 12.x.x<commit_after>package f5

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
)

// Paths for file upload.
const (
	PathUploadImage = "/mgmt/cm/autodeploy/software-image-uploads"
	PathUploadFile  = "/mgmt/shared/file-transfer/uploads"
	PathUploadUCS   = "mgmt/shared/file-transfer/ucs-uploads"

	// For backward compatibility
	// DEPRECATED
	UploadRESTPath = PathUploadFile
)

// Paths for file download.
const (
	PathDownloadUCS = "/mgmt/shared/file-transfer/ucs-downloads"
)

// MaxChunkSize is the maximum chunk size allowed by the iControl REST
const MaxChunkSize = 1048576

// DownloadUCS downloads an UCS file and writes its content to w.
func (c *Client) DownloadUCS(w io.Writer, filename string) (n int64, err error) {
	// BigIP 12.x.x only support download requests with a Content-Range header,
	// thus, it is required to know the size of the file to download beforehand.
	//
	// BigIP 13.x.x automatically download the first chunk and provide the
	// Content-Range header with all information in the response, which is far
	// more convenient. Unfortunately, we need to support BigIP 12 and as a
	// result, we need to first retrieve the UCS file size information.
	resp, err := c.SendRequest("GET", "/mgmt/tm/sys/ucs", nil)
	if err != nil {
		return 0, fmt.Errorf("cannot retrieve info for ucs file: %v", err)
	}
	defer resp.Body.Close()
	if err := c.ReadError(resp); err != nil {
		return 0, fmt.Errorf("cannot retrieve info for ucs file: %v", err)
	}

	// As far as I know, there is no direct way to fetch UCS file info for a
	// specific file and therefore we need to list all UCS files and search
	// for the one we want in the list.
	var ucsInfo struct {
		Items []struct {
			APIRawValues struct {
				Filename string `json:"filename"`
				FileSize string `json:"file_size"`
			} `json:"apiRawValues"`
		} `json:"items"`
	}
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&ucsInfo); err != nil {
		return 0, fmt.Errorf("cannot decode ucs file info: %v", err)
	}

	// File size is a raw string and we need to parse it in order to extract the
	// size as an integer.
	var rawFileSize string
	for _, item := range ucsInfo.Items {
		if strings.HasSuffix(item.APIRawValues.Filename, filename) {
			rawFileSize = strings.TrimSuffix(item.APIRawValues.FileSize, " (in bytes)")
			break
		}
	}
	if rawFileSize == "" {
		return 0, errors.New("ucs file does not exist")
	}
	fileSize, err := strconv.ParseInt(rawFileSize, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("malformed file size in ucs file info: %v", err)
	}

	if n, err = c.download(w, PathDownloadUCS+"/"+filename, fileSize, MaxChunkSize); err != nil {
		return 0, fmt.Errorf("cannot download ucs file: %v", err)
	}
	return
}

func (c *Client) download(w io.Writer, restPath string, filesize, chunkSize int64) (n int64, err error) {
	if filesize < chunkSize {
		chunkSize = filesize
	}
	return c.downloadByChunks(w, restPath, filesize, 0, chunkSize)
}

func (c *Client) downloadByChunks(w io.Writer, restPath string, filesize, offset, chunkSize int64) (n int64, err error) {
	req, err := c.MakeRequest("GET", restPath, nil)
	if err != nil {
		return 0, err
	}

	// Bound limit to filesize
	limit := offset + chunkSize - 1
	if limit >= filesize {
		limit = filesize - 1
	}

	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", offset, limit, filesize))
	fmt.Println("Content-Range: ", fmt.Sprintf("%d-%d/%d", offset, limit, filesize))

	resp, err := c.Do(req)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	if err := c.ReadError(resp); err != nil {
		return 0, err
	}

	if n, err = io.Copy(w, resp.Body); err != nil {
		return 0, err
	}

	if limit < filesize-1 {
		nn, err := c.downloadByChunks(w, restPath, filesize, offset+chunkSize, chunkSize)
		if err != nil {
			return 0, err
		}
		n += nn
	}

	return
}

// An UploadResponse holds the responses send by the BigIP API while uploading
// files.
type UploadResponse struct {
	RemainingByteCount int64          `json:"remainingByteCount"`
	UsedChunks         map[string]int `json:"usedChunks"`
	TotalByteCount     int64          `json:"totalByteCount"`
	LocalFilePath      string         `json:"localFilePath"`
	TemporaryFilePath  string         `json:"temporaryFilePath"`
	Generation         int64          `json:"generation"`
	LastUpdateMicros   int64          `json:"lastUpdateMicros"`
}

// UploadFile reads the content of a file from r and uploads it to the BigIP.
// The uploaded file will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadFile(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadFile, filename, filesize)
}

// UploadImage reads the content of an disk image from r and uploads it to the
// BigIP.
//
// The uploaded image will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadImage(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadImage, filename, filesize)
}

// UploadUCS reads the content of an UCS archive from r and uploads it to the
// BigIP.
//
// The uploaded UCS archive will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadUCS(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadUCS, filename, filesize)
}

func (c *Client) upload(r io.Reader, restPath, filename string, filesize int64) (*UploadResponse, error) {
	var uploadResp UploadResponse
	for bytesSent := int64(0); bytesSent < filesize; {
		var chunk int64
		if remainingBytes := filesize - bytesSent; remainingBytes >= 512*1024 {
			chunk = 512 * 1024
		} else {
			chunk = remainingBytes
		}

		req, err := c.makeUploadRequest(restPath+"/"+filename, io.LimitReader(r, chunk), bytesSent, chunk, filesize)
		if err != nil {
			return nil, err
		}
		resp, err := c.Do(req)
		if err != nil {
			return nil, err
		}
		if err := c.ReadError(resp); err != nil {
			resp.Body.Close()
			return nil, err
		}

		if filesize-bytesSent <= 512*1024 {
			dec := json.NewDecoder(resp.Body)
			if err := dec.Decode(&uploadResp); err != nil {
				resp.Body.Close()
				return nil, err
			}
		}

		bytesSent += chunk
	}
	return &uploadResp, nil
}

// makeUploadRequest constructs a single upload request.
//
// restPath can be any of the Path* constants defined at the top of this file.
//
// The file to be uploaded is read from r and must not exceed 524288 bytes.
//
// off represents the number of bytes already sent while chunk is the size of
// chunk to be send in this request.
//
// filesize denotes the size of the entire file.
func (c *Client) makeUploadRequest(restPath string, r io.Reader, off, chunk, filesize int64) (*http.Request, error) {
	if chunk > 512*1024 {
		return nil, fmt.Errorf("chunk size greater than %d is not supported", 512*1024)
	}
	req, err := http.NewRequest("POST", c.makeURL(restPath), r)
	if err != nil {
		return nil, fmt.Errorf("failed to create F5 authenticated request: %v", err)
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", off, off+chunk-1, filesize))
	req.Header.Set("Content-Type", "application/octet-stream")
	if err := c.makeAuth(req); err != nil {
		return nil, err
	}
	return req, nil
}
<|endoftext|>


<commit_before>package io

import (
	"os"
	"path/filepath"
)

//	写文件
func WriteFile(filePath string, lines []string) error {

	//	检查文件所处目录是否存在
	fileDir := filepath.Dir(filePath)
	_, err := os.Stat(fileDir)
	if os.IsNotExist(err) {
		//	如果不存在就先创建目录
		err = os.Mkdir(fileDir, 0x777)
		if err != nil {
			return err
		}
	}

	//	打开文件
	file, err := os.OpenFile(filePath, os.O_CREATE, 0x777)
	if err != nil {
		return err
	}
	defer file.Close()

	for _, line := range lines {

		//	将股价写入文件
		_, err = file.WriteString(line)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>增加将单字符串写入文件的方法，并在打开文件前确保目录存在<commit_after>package io

import (
	"os"
	"path/filepath"
)

//	写文件
func WriteLines(filePath string, lines []string) error {

	//	打开文件
	file, err := openFile(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	for _, line := range lines {

		//	将股价写入文件
		_, err = file.WriteString(line)
		if err != nil {
			return err
		}
	}

	return nil
}

//	写入字符串
func WriteString(filePath, content string) error {

	//	打开文件
	file, err := openFile(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	file.WriteString(content)

	return nil
}

//	打开文件
func openFile(filePath string) (*os.File, error) {
	//	检查文件所处目录是否存在
	fileDir := filepath.Dir(filePath)
	_, err := os.Stat(fileDir)
	if os.IsNotExist(err) {
		//	如果不存在就先创建目录
		err = os.Mkdir(fileDir, 0x777)
		if err != nil {
			return nil, err
		}
	}

	//	打开文件
	return os.OpenFile(filePath, os.O_CREATE, 0x777)
}
<|endoftext|>


<commit_before>// chris 090115 Unix removable lock file.

// TODO Note how Close calls errors are not handled.
// TODO Generalize to lockfile library:
//       - Lock
//       - LockNb
//       - LockRm

package lockfile

import (
	"os"

	"golang.org/x/sys/unix"
)

const mode = 0666

type LockContext struct {
	f *os.File
}

type LockRmContext struct {
	globalname string

	local *os.File
}

// TODO document:
// - blocking
// - doesn't remove
func Lock(filename string) (*LockContext, error) {
	f, err := os.OpenFile(filename, os.O_CREATE, mode)
	if err != nil {
		return nil, err
	}

	if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
		f.Close()
		return nil, err
	}

	return &LockContext{f}, nil
}

func (lc *LockContext) Unlock() {
	// Close implicitly releases any kernel advisory locks.
	lc.f.Close()
}

func globalCtx(globalname string, inner func() error) error {
	f, err := os.OpenFile(globalname, os.O_CREATE, mode)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
		return err
	}

	if err := inner(); err != nil {
		return err
	}

	if err = unix.Flock(int(f.Fd()), unix.LOCK_UN); err != nil {
		return err
	}

	return nil
}

func LockRm(globalname, localname string) (*LockRmContext, error) {
	var lrc *LockRmContext

	err := globalCtx(globalname, func() error {
		f, err := os.OpenFile(localname, os.O_CREATE, mode)
		if err != nil {
			return err
		}

		err = unix.Flock(int(f.Fd()), unix.LOCK_EX | unix.LOCK_NB)
		if err != nil {
			f.Close()
			return err
		}

		lrc = &LockRmContext{
			globalname: globalname,
			local: f,
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return lrc, nil

}

func (lrc *LockRmContext) Unlock() error {
	return globalCtx(lrc.globalname, func() error {
		lrc.local.Close()
		return os.Remove(lrc.local.Name())
	})
}
<commit_msg>lockfile: Adds todo item.<commit_after>// chris 090115 Unix removable lock file.

// TODO Note how Close calls errors are not handled.
// TODO Generalize to lockfile library:
//       - Lock
//       - LockNb
//       - LockRm
// TODO Test on Linux and Windows.

package lockfile

import (
	"os"

	"golang.org/x/sys/unix"
)

const mode = 0666

type LockContext struct {
	f *os.File
}

type LockRmContext struct {
	globalname string

	local *os.File
}

// TODO document:
// - blocking
// - doesn't remove
func Lock(filename string) (*LockContext, error) {
	f, err := os.OpenFile(filename, os.O_CREATE, mode)
	if err != nil {
		return nil, err
	}

	if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
		f.Close()
		return nil, err
	}

	return &LockContext{f}, nil
}

func (lc *LockContext) Unlock() {
	// Close implicitly releases any kernel advisory locks.
	lc.f.Close()
}

func globalCtx(globalname string, inner func() error) error {
	f, err := os.OpenFile(globalname, os.O_CREATE, mode)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
		return err
	}

	if err := inner(); err != nil {
		return err
	}

	if err = unix.Flock(int(f.Fd()), unix.LOCK_UN); err != nil {
		return err
	}

	return nil
}

func LockRm(globalname, localname string) (*LockRmContext, error) {
	var lrc *LockRmContext

	err := globalCtx(globalname, func() error {
		f, err := os.OpenFile(localname, os.O_CREATE, mode)
		if err != nil {
			return err
		}

		err = unix.Flock(int(f.Fd()), unix.LOCK_EX | unix.LOCK_NB)
		if err != nil {
			f.Close()
			return err
		}

		lrc = &LockRmContext{
			globalname: globalname,
			local: f,
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return lrc, nil

}

func (lrc *LockRmContext) Unlock() error {
	return globalCtx(lrc.globalname, func() error {
		lrc.local.Close()
		return os.Remove(lrc.local.Name())
	})
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package syslog

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"text/template"
)

// tagOffset represents the substring start value for the tag to return
// the logfileName value from the syslogtag.  Substrings in syslog are
// indexed from 1, hence the + 1.
const tagOffset = len("juju-") + 1

// The rsyslog conf for state server nodes.
// Messages are gathered from other nodes and accumulated in an all-machines.log file.
//
// I would dearly love to write the filtering action as follows to avoid setting
// and resetting the global $FileCreateMode, but alas, precise doesn't support it
//
// if $syslogtag startswith "juju{{namespace}}-" then
//   action(type="omfile"
//          File="/var/log/juju{{namespace}}/all-machines.log"
//          Template="JujuLogFormat{{namespace}}"
//          FileCreateMode="0644")
// & stop
const stateServerRsyslogTemplate = `
$ModLoad imfile

$InputFilePersistStateInterval 50
$InputFilePollInterval 5
$InputFileName {{logfilePath}}
$InputFileTag juju{{namespace}}-{{logfileName}}:
$InputFileStateFile {{logfileName}}{{namespace}}
$InputRunFileMonitor

$ModLoad imudp
$UDPServerRun {{portNumber}}

# Messages received from remote rsyslog machines have messages prefixed with a space,
# so add one in for local messages too if needed.
$template JujuLogFormat{{namespace}},"%syslogtag:{{tagStart}}:$%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n"

$FileCreateMode 0644
:syslogtag, startswith, "juju{{namespace}}-" /var/log/juju{{namespace}}/all-machines.log;JujuLogFormat{{namespace}}
& ~
$FileCreateMode 0640
`

// The rsyslog conf for non-state server nodes.
// Messages are forwarded to the state server node.
const nodeRsyslogTemplate = `
$ModLoad imfile

$InputFilePersistStateInterval 50
$InputFilePollInterval 5
$InputFileName {{logfilePath}}
$InputFileTag juju{{namespace}}-{{logfileName}}:
$InputFileStateFile {{logfileName}}{{namespace}}
$InputRunFileMonitor

$template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%"

:syslogtag, startswith, "juju{{namespace}}-" @{{bootstrapIP}}:{{portNumber}};LongTagForwardFormat
& ~
`

const defaultConfigDir = "/etc/rsyslog.d"

// SyslogConfigRenderer instances are used to generate a rsyslog conf file.
type SyslogConfigRenderer interface {
	Render() ([]byte, error)
}

// SyslogConfig provides a means to configure and generate rsyslog conf files for
// the state server nodes and unit nodes.
// rsyslog is configured to tail the specified log file.
type SyslogConfig struct {
	// the template representing the config file contents.
	configTemplate string
	// the directory where the config file is written.
	ConfigDir string
	// the config file name.
	ConfigFileName string
	// the name of the log file to tail.
	LogFileName string
	// the addresses of the state server to which messages should be forwarded.
	StateServerAddresses []string
	// the port number for the udp listener
	Port int
	// the directory for the logfiles
	LogDir string
	// namespace is used when there are multiple environments on one machine
	Namespace string
}

// NewForwardConfig creates a SyslogConfig instance used on unit nodes to forward log entries
// to the state server nodes.
func NewForwardConfig(logFile string, port int, namespace string, stateServerAddresses []string) *SyslogConfig {
	conf := &SyslogConfig{
		configTemplate:       nodeRsyslogTemplate,
		StateServerAddresses: stateServerAddresses,
		LogFileName:          logFile,
		Port:                 port,
		LogDir:               "/var/log/juju",
	}
	if namespace != "" {
		conf.Namespace = "-" + namespace
	}
	return conf
}

// NewAccumulateConfig creates a SyslogConfig instance used to accumulate log entries from the
// various unit nodes.
func NewAccumulateConfig(logFile string, port int, namespace string) *SyslogConfig {
	conf := &SyslogConfig{
		configTemplate: stateServerRsyslogTemplate,
		LogFileName:    logFile,
		Port:           port,
		LogDir:         "/var/log/juju",
	}
	if namespace != "" {
		conf.Namespace = "-" + namespace
	}
	return conf
}

func (slConfig *SyslogConfig) ConfigFilePath() string {
	dir := slConfig.ConfigDir
	if dir == "" {
		dir = defaultConfigDir
	}
	return filepath.Join(dir, slConfig.ConfigFileName)
}

// Render generates the rsyslog config.
func (slConfig *SyslogConfig) Render() ([]byte, error) {

	// TODO: for HA, we will want to send to all state server addresses (maybe).
	var bootstrapIP = func() string {
		addr := slConfig.StateServerAddresses[0]
		parts := strings.Split(addr, ":")
		return parts[0]
	}

	var logFilePath = func() string {
		return fmt.Sprintf("%s/%s.log", slConfig.LogDir, slConfig.LogFileName)
	}

	t := template.New("")
	t.Funcs(template.FuncMap{
		"logfileName": func() string { return slConfig.LogFileName },
		"bootstrapIP": bootstrapIP,
		"logfilePath": logFilePath,
		"portNumber":  func() int { return slConfig.Port },
		"logDir":      func() string { return slConfig.LogDir },
		"namespace":   func() string { return slConfig.Namespace },
		"tagStart":    func() int { return tagOffset + len(slConfig.Namespace) },
	})

	// Process the rsyslog config template and echo to the conf file.
	p, err := t.Parse(slConfig.configTemplate)
	if err != nil {
		return nil, err
	}
	var confBuf bytes.Buffer
	if err := p.Execute(&confBuf, nil); err != nil {
		return nil, err
	}
	return confBuf.Bytes(), nil
}

// Write generates and writes the rsyslog config.
func (slConfig *SyslogConfig) Write() error {
	data, err := slConfig.Render()
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(slConfig.ConfigFilePath(), data, 0644)
	return err
}
<commit_msg>More detail in the syslog config comment.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package syslog

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"text/template"
)

// tagOffset represents the substring start value for the tag to return
// the logfileName value from the syslogtag.  Substrings in syslog are
// indexed from 1, hence the + 1.
const tagOffset = len("juju-") + 1

// The rsyslog conf for state server nodes.
// Messages are gathered from other nodes and accumulated in an all-machines.log file.
//
// The apparmor profile is quite strict about where rsyslog can write files.
// Instead of poking with the profile, the local provider now logs to
// /var/log/juju-{{user}}-{{env name}}/all-machines.log, and a symlink is made
// in the local provider log dir to point to that file. The file is also
// created with 0644 so the user can read it without poking permissions. By
// default rsyslog creates files with 0644, but in the ubuntu package, the
// setting is changed to 0640, which means normal users can't read the log
// file. Using a new action directive (new as in not-legacy), we can specify
// the file create mode so it doesn't use the default.
//
// I would dearly love to write the filtering action as follows to avoid setting
// and resetting the global $FileCreateMode, but alas, precise doesn't support it
//
// if $syslogtag startswith "juju{{namespace}}-" then
//   action(type="omfile"
//          File="/var/log/juju{{namespace}}/all-machines.log"
//          Template="JujuLogFormat{{namespace}}"
//          FileCreateMode="0644")
// & stop
//
// Instead we need to mess with the global FileCreateMode.  We set it back
// to the ubuntu default after defining our rule.
const stateServerRsyslogTemplate = `
$ModLoad imfile

$InputFilePersistStateInterval 50
$InputFilePollInterval 5
$InputFileName {{logfilePath}}
$InputFileTag juju{{namespace}}-{{logfileName}}:
$InputFileStateFile {{logfileName}}{{namespace}}
$InputRunFileMonitor

$ModLoad imudp
$UDPServerRun {{portNumber}}

# Messages received from remote rsyslog machines have messages prefixed with a space,
# so add one in for local messages too if needed.
$template JujuLogFormat{{namespace}},"%syslogtag:{{tagStart}}:$%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n"

$FileCreateMode 0644
:syslogtag, startswith, "juju{{namespace}}-" /var/log/juju{{namespace}}/all-machines.log;JujuLogFormat{{namespace}}
& ~
$FileCreateMode 0640
`

// The rsyslog conf for non-state server nodes.
// Messages are forwarded to the state server node.
const nodeRsyslogTemplate = `
$ModLoad imfile

$InputFilePersistStateInterval 50
$InputFilePollInterval 5
$InputFileName {{logfilePath}}
$InputFileTag juju{{namespace}}-{{logfileName}}:
$InputFileStateFile {{logfileName}}{{namespace}}
$InputRunFileMonitor

$template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%"

:syslogtag, startswith, "juju{{namespace}}-" @{{bootstrapIP}}:{{portNumber}};LongTagForwardFormat
& ~
`

const defaultConfigDir = "/etc/rsyslog.d"

// SyslogConfigRenderer instances are used to generate a rsyslog conf file.
type SyslogConfigRenderer interface {
	Render() ([]byte, error)
}

// SyslogConfig provides a means to configure and generate rsyslog conf files for
// the state server nodes and unit nodes.
// rsyslog is configured to tail the specified log file.
type SyslogConfig struct {
	// the template representing the config file contents.
	configTemplate string
	// the directory where the config file is written.
	ConfigDir string
	// the config file name.
	ConfigFileName string
	// the name of the log file to tail.
	LogFileName string
	// the addresses of the state server to which messages should be forwarded.
	StateServerAddresses []string
	// the port number for the udp listener
	Port int
	// the directory for the logfiles
	LogDir string
	// namespace is used when there are multiple environments on one machine
	Namespace string
}

// NewForwardConfig creates a SyslogConfig instance used on unit nodes to forward log entries
// to the state server nodes.
func NewForwardConfig(logFile string, port int, namespace string, stateServerAddresses []string) *SyslogConfig {
	conf := &SyslogConfig{
		configTemplate:       nodeRsyslogTemplate,
		StateServerAddresses: stateServerAddresses,
		LogFileName:          logFile,
		Port:                 port,
		LogDir:               "/var/log/juju",
	}
	if namespace != "" {
		conf.Namespace = "-" + namespace
	}
	return conf
}

// NewAccumulateConfig creates a SyslogConfig instance used to accumulate log entries from the
// various unit nodes.
func NewAccumulateConfig(logFile string, port int, namespace string) *SyslogConfig {
	conf := &SyslogConfig{
		configTemplate: stateServerRsyslogTemplate,
		LogFileName:    logFile,
		Port:           port,
		LogDir:         "/var/log/juju",
	}
	if namespace != "" {
		conf.Namespace = "-" + namespace
	}
	return conf
}

func (slConfig *SyslogConfig) ConfigFilePath() string {
	dir := slConfig.ConfigDir
	if dir == "" {
		dir = defaultConfigDir
	}
	return filepath.Join(dir, slConfig.ConfigFileName)
}

// Render generates the rsyslog config.
func (slConfig *SyslogConfig) Render() ([]byte, error) {

	// TODO: for HA, we will want to send to all state server addresses (maybe).
	var bootstrapIP = func() string {
		addr := slConfig.StateServerAddresses[0]
		parts := strings.Split(addr, ":")
		return parts[0]
	}

	var logFilePath = func() string {
		return fmt.Sprintf("%s/%s.log", slConfig.LogDir, slConfig.LogFileName)
	}

	t := template.New("")
	t.Funcs(template.FuncMap{
		"logfileName": func() string { return slConfig.LogFileName },
		"bootstrapIP": bootstrapIP,
		"logfilePath": logFilePath,
		"portNumber":  func() int { return slConfig.Port },
		"logDir":      func() string { return slConfig.LogDir },
		"namespace":   func() string { return slConfig.Namespace },
		"tagStart":    func() int { return tagOffset + len(slConfig.Namespace) },
	})

	// Process the rsyslog config template and echo to the conf file.
	p, err := t.Parse(slConfig.configTemplate)
	if err != nil {
		return nil, err
	}
	var confBuf bytes.Buffer
	if err := p.Execute(&confBuf, nil); err != nil {
		return nil, err
	}
	return confBuf.Bytes(), nil
}

// Write generates and writes the rsyslog config.
func (slConfig *SyslogConfig) Write() error {
	data, err := slConfig.Render()
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(slConfig.ConfigFilePath(), data, 0644)
	return err
}
<|endoftext|>


<commit_before>package bamstats

import (
	"math"

	"github.com/biogo/hts/sam"
)

type ElementStats struct {
	ExonIntron int `json:"exonic_intronic"`
	Intron     int `json:"intron"`
	Exon       int `json:"exon"`
	Intergenic int `json:"intergenic"`
	Other      int `json:"others"`
	Total      int `json:"total"`
}

type CoverageStats struct {
	Total      ElementStats `json:"Total reads"`
	Continuous ElementStats `json:"Continuous read"`
	Split      ElementStats `json:"Split reads"`
}

func (s *CoverageStats) Update(other Stats) {
	if other, ok := other.(*CoverageStats); ok {
		s.Continuous.Update(other.Continuous)
		s.Split.Update(other.Split)
		s.UpdateTotal()
	}
}

func (s *CoverageStats) UpdateTotal() {
	s.Total.ExonIntron = s.Continuous.ExonIntron + s.Split.ExonIntron
	s.Total.Exon = s.Continuous.Exon + s.Split.Exon
	s.Total.Intron = s.Continuous.Intron + s.Split.Intron
	s.Total.Intergenic = s.Continuous.Intergenic + s.Split.Intergenic
	s.Total.Other = s.Continuous.Other + s.Split.Other
	s.Total.Total = s.Continuous.Total + s.Split.Total
}

func (s *CoverageStats) Merge(others chan Stats) {
	for other := range others {
		if other, ok := other.(*CoverageStats); ok {
			s.Update(other)
		}
	}
}

func (s *ElementStats) Update(other ElementStats) {
	s.ExonIntron += other.ExonIntron
	s.Exon += other.Exon
	s.Intron += other.Intron
	s.Intergenic += other.Intergenic
	s.Other += other.Other
	s.Total += other.Total
}

func updateCount(r *sam.Record, elems map[string]uint8, st *ElementStats) {
	exons, hasExon := elems["exon"]
	introns, hasIntron := elems["intron"]
	st.Total++
	if _, isIntergenic := elems["intergenic"]; isIntergenic {
		if len(elems) > 1 {
			st.Other++
		} else {
			st.Intergenic++
		}
		return
	}
	if hasExon && !hasIntron && exons > 0 {
		st.Exon++
		return
	}
	if hasIntron && !hasExon && introns > 0 {
		st.Intron++
		return
	}
	st.ExonIntron++
}

func (s *CoverageStats) Collect(record *sam.Record, index *RtreeMap) {
	if index == nil || !isPrimary(record) || isUnmapped(record) {
		return
	}
	elements := map[string]uint8{}
	for _, mappingLocation := range getBlocks(record) {
		results := QueryIndex(index.Get(mappingLocation.Chrom()), mappingLocation.Start(), mappingLocation.End(), math.MaxFloat64)
		getElements(mappingLocation, &results, elements)
	}
	if isSplit(record) {
		updateCount(record, elements, &s.Split)
	} else {
		updateCount(record, elements, &s.Continuous)
	}
}

func NewCoverageStats() *CoverageStats {
	return &CoverageStats{}
}
<commit_msg>Update coverage stats json tags<commit_after>package bamstats

import (
	"math"

	"github.com/biogo/hts/sam"
)

type ElementStats struct {
	ExonIntron int `json:"exonic_intronic"`
	Intron     int `json:"intron"`
	Exon       int `json:"exon"`
	Intergenic int `json:"intergenic"`
	Other      int `json:"others"`
	Total      int `json:"total"`
}

type CoverageStats struct {
	Total      ElementStats `json:"total"`
	Continuous ElementStats `json:"continuous"`
	Split      ElementStats `json:"split"`
}

func (s *CoverageStats) Update(other Stats) {
	if other, ok := other.(*CoverageStats); ok {
		s.Continuous.Update(other.Continuous)
		s.Split.Update(other.Split)
		s.UpdateTotal()
	}
}

func (s *CoverageStats) UpdateTotal() {
	s.Total.ExonIntron = s.Continuous.ExonIntron + s.Split.ExonIntron
	s.Total.Exon = s.Continuous.Exon + s.Split.Exon
	s.Total.Intron = s.Continuous.Intron + s.Split.Intron
	s.Total.Intergenic = s.Continuous.Intergenic + s.Split.Intergenic
	s.Total.Other = s.Continuous.Other + s.Split.Other
	s.Total.Total = s.Continuous.Total + s.Split.Total
}

func (s *CoverageStats) Merge(others chan Stats) {
	for other := range others {
		if other, ok := other.(*CoverageStats); ok {
			s.Update(other)
		}
	}
}

func (s *ElementStats) Update(other ElementStats) {
	s.ExonIntron += other.ExonIntron
	s.Exon += other.Exon
	s.Intron += other.Intron
	s.Intergenic += other.Intergenic
	s.Other += other.Other
	s.Total += other.Total
}

func updateCount(r *sam.Record, elems map[string]uint8, st *ElementStats) {
	exons, hasExon := elems["exon"]
	introns, hasIntron := elems["intron"]
	st.Total++
	if _, isIntergenic := elems["intergenic"]; isIntergenic {
		if len(elems) > 1 {
			st.Other++
		} else {
			st.Intergenic++
		}
		return
	}
	if hasExon && !hasIntron && exons > 0 {
		st.Exon++
		return
	}
	if hasIntron && !hasExon && introns > 0 {
		st.Intron++
		return
	}
	st.ExonIntron++
}

func (s *CoverageStats) Collect(record *sam.Record, index *RtreeMap) {
	if index == nil || !isPrimary(record) || isUnmapped(record) {
		return
	}
	elements := map[string]uint8{}
	for _, mappingLocation := range getBlocks(record) {
		results := QueryIndex(index.Get(mappingLocation.Chrom()), mappingLocation.Start(), mappingLocation.End(), math.MaxFloat64)
		getElements(mappingLocation, &results, elements)
	}
	if isSplit(record) {
		updateCount(record, elements, &s.Split)
	} else {
		updateCount(record, elements, &s.Continuous)
	}
}

func NewCoverageStats() *CoverageStats {
	return &CoverageStats{}
}
<|endoftext|>


<commit_before>package gopsutil

import (
	"fmt"
	"runtime"
	"testing"
	"time"
)

func TestCpu_times(t *testing.T) {
	v, err := CPUTimes(false)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if len(v) == 0 {
		t.Error("could not get CPUs ", err)
	}
	empty := CPUTimesStat{}
	for _, vv := range v {
		if vv == empty {
			t.Errorf("could not get CPU User: %v", vv)
		}
	}
}

func TestCpu_counts(t *testing.T) {
	v, err := CPUCounts(true)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if v == 0 {
		t.Errorf("could not get CPU counts: %v", v)
	}
}

func TestCPUTimeStat_String(t *testing.T) {
	v := CPUTimesStat{
		CPU:    "cpu0",
		User:   100.1,
		System: 200.1,
		Idle:   300.1,
	}
	e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0,"iowait":0,"irq":0,"softirq":0,"steal":0,"guest":0,"guest_nice":0,"stolen":0}`
	if e != fmt.Sprintf("%v", v) {
		t.Errorf("CPUTimesStat string is invalid: %v", v)
	}
}

func TestCpuInfo(t *testing.T) {
	v, err := CPUInfo()
	if err != nil {
		t.Errorf("error %v", err)
	}
	for _, vv := range v {
		if vv.ModelName == "" {
			t.Errorf("could not get CPU Info: %v", vv)
		}
	}
}

func testCPUPercent(t *testing.T, percpu bool) {
	v, err := CPUPercent(time.Millisecond, percpu)
	if err != nil {
		t.Errorf("error %v", err)
	}
	numcpu := runtime.NumCPU()
	if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) {
		t.Fatalf("wrong number of entries from CPUPercent: %v", v)
	}
	for i := 0; i < 1000; i++ {
		v, err := CPUPercent(0, percpu)
		if err != nil {
			t.Errorf("error %v", err)
		}
		for _, percent := range v {
			if percent < 0.0 || percent > 100.0*float32(numcpu) {
				t.Fatalf("CPUPercent value is invalid: %f", percent)
			}
		}
	}
}

func TestCPUPercent(t *testing.T) {
	testCPUPercent(t, false)
}

func TestCPUPercentPerCpu(t *testing.T) {
	testCPUPercent(t, true)
}
<commit_msg>use 10microsend duration for CPUPercent test.<commit_after>package gopsutil

import (
	"fmt"
	"runtime"
	"testing"
	"time"
)

func TestCpu_times(t *testing.T) {
	v, err := CPUTimes(false)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if len(v) == 0 {
		t.Error("could not get CPUs ", err)
	}
	empty := CPUTimesStat{}
	for _, vv := range v {
		if vv == empty {
			t.Errorf("could not get CPU User: %v", vv)
		}
	}
}

func TestCpu_counts(t *testing.T) {
	v, err := CPUCounts(true)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if v == 0 {
		t.Errorf("could not get CPU counts: %v", v)
	}
}

func TestCPUTimeStat_String(t *testing.T) {
	v := CPUTimesStat{
		CPU:    "cpu0",
		User:   100.1,
		System: 200.1,
		Idle:   300.1,
	}
	e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0,"iowait":0,"irq":0,"softirq":0,"steal":0,"guest":0,"guest_nice":0,"stolen":0}`
	if e != fmt.Sprintf("%v", v) {
		t.Errorf("CPUTimesStat string is invalid: %v", v)
	}
}

func TestCpuInfo(t *testing.T) {
	v, err := CPUInfo()
	if err != nil {
		t.Errorf("error %v", err)
	}
	for _, vv := range v {
		if vv.ModelName == "" {
			t.Errorf("could not get CPU Info: %v", vv)
		}
	}
}

func testCPUPercent(t *testing.T, percpu bool) {
	v, err := CPUPercent(time.Millisecond, percpu)
	if err != nil {
		t.Errorf("error %v", err)
	}
	numcpu := runtime.NumCPU()
	if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) {
		t.Fatalf("wrong number of entries from CPUPercent: %v", v)
	}
	for i := 0; i < 100; i++ {
		duration := time.Duration(10) * time.Microsecond
		v, err := CPUPercent(duration, percpu)
		if err != nil {
			t.Errorf("error %v", err)
		}
		for _, percent := range v {
			if percent < 0.0 || percent > 100.0*float32(numcpu) {
				t.Fatalf("CPUPercent value is invalid: %f", percent)
			}
		}
	}
}

func TestCPUPercent(t *testing.T) {
	testCPUPercent(t, false)
}

func TestCPUPercentPerCpu(t *testing.T) {
	testCPUPercent(t, true)
}
<|endoftext|>