// GetMetricTypes returns list of available metrics
func (d *docker) GetMetricTypes(_ plugin.ConfigType) ([]plugin.MetricType, error) {
	var metricTypes []plugin.MetricType
	var err error

	// initialize containerData struct
	data := containerData{
		Stats: wrapper.NewStatistics(),
	}

	// generate available namespace for data container structure
	dockerMetrics := []string{}
	utils.FromCompositeObject(data, "", &dockerMetrics)
	nscreator := nsCreator{dynamicElements: definedDynamicElements}

	for _, metricName := range dockerMetrics {

		ns := core.NewNamespace(NS_VENDOR, NS_PLUGIN).
			AddDynamicElement("docker_id", "an id of docker container")

		if ns, err = nscreator.createMetricNamespace(ns, metricName); err != nil {
			// skip this metric name which is not supported
			// fmt.Fprintf(os.Stderr, "Error in creating metric namespace: %v\n", err)
			continue
		}
		metricType := plugin.MetricType{
			Namespace_: ns,
			Version_:   VERSION,
		}
		metricTypes = append(metricTypes, metricType)
	}

	return metricTypes, nil
}
func CreateMockStats() *wrapper.Statistics {
	stats := wrapper.NewStatistics()
	stats.Connection.Tcp.Close = 1
	stats.Connection.Tcp.Established = 1

	stats.Connection.Tcp6.Close = 2
	stats.Connection.Tcp6.Established = 2

	stats.Network = []wrapper.NetworkInterface{
		wrapper.NetworkInterface{
			Name:      "eth0",
			RxBytes:   1024,
			RxPackets: 1,
			TxBytes:   4096,
			TxPackets: 4,
			TxErrors:  0,
		},
		wrapper.NetworkInterface{
			Name:      "eth1",
			RxBytes:   2048,
			RxPackets: 2,
			TxBytes:   0,
			TxPackets: 0,
			TxErrors:  10,
		},
	}
	stats.Filesystem["dev1"] = wrapper.FilesystemInterface{
		Device:         "dev1",
		Type:           "vfs",
		Limit:          0,
		Usage:          0,
		BaseUsage:      0,
		Available:      0,
		InodesFree:     0,
		ReadsCompleted: 0,
	}

	stats.Filesystem["dev2"] = wrapper.FilesystemInterface{
		Device:         "dev2",
		Type:           "vfs",
		Limit:          0,
		Usage:          0,
		BaseUsage:      0,
		Available:      0,
		InodesFree:     0,
		ReadsCompleted: 0,
	}

	stats.CgroupStats.CpuStats.CpuUsage.PercpuUsage = []uint64{1000, 2000, 3000, 4000}

	return stats
}
// GetStatsFromContainer returns docker containers stats: cgroups stats (cpu usage, memory usage, etc.) and network stats (tx_bytes, rx_bytes etc.);
// notes that incoming container id has to be full-length to be able to inspect container
func (dc *DockerClient) GetStatsFromContainer(id string, collectFs bool) (*wrapper.Statistics, error) {
	var (
		err        error
		pid        int
		workingSet uint64

		container = &docker.Container{}
		groupWrap = wrapper.CgroupsToStats // wrapper for cgroup name and interface for stats extraction
		stats     = wrapper.NewStatistics()
	)

	if !isHost(id) {
		if !isFullLengthID(id) {
			return nil, fmt.Errorf("Container id %+v is not fully-length - cannot inspect container", id)
		}
		// inspect container based only on fully-length container id.
		container, err = dc.InspectContainer(id)

		if err != nil {
			return nil, err
		}
		// take docker container PID
		pid = container.State.Pid
	}

	for cg, stat := range groupWrap {
		groupPath, err := getSubsystemPath(cg, id)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Cannot found subsystem path for cgroup=", cg, " for container id=", container)
			continue
		}

		switch cg {
		case "cpuset":
			cpuset := wrapper.CpuSet{}
			// get cpuset group stats
			err = cpuset.GetExtendedStats(groupPath, stats.CgroupsExtended)
		case "shares":
			// get cpu.shares stats
			shares := wrapper.Shares{}
			err = shares.GetExtendedStats(groupPath, stats.CgroupsExtended)
		default:
			// get cgroup stats for given docker
			err = stat.GetStats(groupPath, stats.CgroupStats)
		}

		if err != nil {
			// just log about it
			if isHost(id) {
				fmt.Fprintln(os.Stderr, "Cannot obtain cgroups statistics for host, err=", err)
			} else {
				fmt.Fprintln(os.Stderr, "Cannot obtain cgroups statistics for container: id=", id, ", image=", container.Image, ", name=", container.Name, ", err=", err)
			}
			continue
		}
	}

	// calculate additional stats memory:working_set based on memory_stats
	if totalInactiveAnon, ok := stats.CgroupStats.MemoryStats.Stats["total_inactive_anon"]; ok {
		workingSet = stats.CgroupStats.MemoryStats.Usage.Usage
		if workingSet < totalInactiveAnon {
			workingSet = 0
		} else {
			workingSet -= totalInactiveAnon
		}

		if totalInactiveFile, ok := stats.CgroupStats.MemoryStats.Stats["total_inactive_file"]; ok {
			if workingSet < totalInactiveFile {
				workingSet = 0
			} else {
				workingSet -= totalInactiveFile
			}
		}
	}
	stats.CgroupStats.MemoryStats.Stats["working_set"] = workingSet

	if !isHost(id) {
		rootFs := "/"

		stats.Network, err = network.NetworkStatsFromProc(rootFs, pid)
		if err != nil {
			// only log error message
			fmt.Fprintf(os.Stderr, "Unable to get network stats, containerID=%+v, pid %d: %v", container.ID, pid, err)
		}

		stats.Connection.Tcp, err = network.TcpStatsFromProc(rootFs, pid)
		if err != nil {
			// only log error message
			fmt.Fprintf(os.Stderr, "Unable to get tcp stats from pid %d: %v", pid, err)
		}

		stats.Connection.Tcp6, err = network.Tcp6StatsFromProc(rootFs, pid)
		if err != nil {
			// only log error message
			fmt.Fprintf(os.Stderr, "Unable to get tcp6 stats from pid %d: %v", pid, err)
		}

	} else {
		stats.Network, err = network.NetworkStatsFromRoot()
		if err != nil {
			// only log error message
			fmt.Fprintf(os.Stderr, "Unable to get network stats, containerID=%v, %v", id, err)
		}

	}
	if collectFs {
		stats.Filesystem, err = fs.GetFsStats(container)
		if err != nil {
			// only log error message
			fmt.Fprintf(os.Stderr, "Unable to get filesystem stats for docker: %v, err=%v", id, err)
		}
	}

	return stats, nil
}