func (s *subscriptionGroups) validatePluginUnloading(pluginToUnload *loadedPlugin) (errs []serror.SnapError) {
	s.Lock()
	defer s.Unlock()
	for id, group := range s.subscriptionMap {
		if err := group.validatePluginUnloading(id, pluginToUnload); err != nil {
			errs = append(errs, err)
		}
	}
	return errs
}
validatePluginUnloading checks if process of unloading the plugin is safe for existing running tasks.
If the plugin is used by running task and there is no replacements, return an error with appropriate message
containing ids of tasks which use the plugin, what blocks unloading process until they are stopped
package control
import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/core/cdata"
	"github.com/intelsdi-x/snap/core/control_event"
	"github.com/intelsdi-x/snap/core/serror"
	log "github.com/sirupsen/logrus"
)
var (
	ErrSubscriptionGroupAlreadyExists = core.ErrSubscriptionGroupAlreadyExists
	ErrSubscriptionGroupDoesNotExist = core.ErrSubscriptionGroupDoesNotExist
	ErrConfigRequiredForMetric = errors.New("config required")
)
type ManagesSubscriptionGroups interface {
	Process() (errs []serror.SnapError)
	Add(id string, requested []core.RequestedMetric,
		configTree *cdata.ConfigDataTree,
		plugins []core.SubscribedPlugin) []serror.SnapError
	Get(id string) (map[string]metricTypes, []serror.SnapError, error)
	Remove(id string) []serror.SnapError
	ValidateDeps(requested []core.RequestedMetric,
		plugins []core.SubscribedPlugin,
		configTree *cdata.ConfigDataTree, asserts ...core.SubscribedPluginAssert) (serrs []serror.SnapError)
	validateMetric(metric core.Metric) (serrs []serror.SnapError)
	validatePluginUnloading(*loadedPlugin) (errs []serror.SnapError)
}
type subscriptionGroup struct {
	*pluginControl
	requestedMetrics []core.RequestedMetric
	requestedPlugins []core.SubscribedPlugin
	configTree *cdata.ConfigDataTree
	metrics map[string]metricTypes
	plugins []core.SubscribedPlugin
	errors []serror.SnapError
}
type subscriptionMap map[string]*subscriptionGroup
type subscriptionGroups struct {
	subscriptionMap
	*sync.Mutex
	*pluginControl
}
func newSubscriptionGroups(control *pluginControl) *subscriptionGroups {
	return &subscriptionGroups{
		make(map[string]*subscriptionGroup),
		&sync.Mutex{},
		control,
	}
}
func (s subscriptionGroups) Add(id string, requested []core.RequestedMetric,
	configTree *cdata.ConfigDataTree,
	plugins []core.SubscribedPlugin) []serror.SnapError {
	s.Lock()
	defer s.Unlock()
	errs := s.add(id, requested, configTree, plugins)
	return errs
}
func (s subscriptionGroups) add(id string, requested []core.RequestedMetric,
	configTree *cdata.ConfigDataTree,
	plugins []core.SubscribedPlugin) []serror.SnapError {
	if _, ok := s.subscriptionMap[id]; ok {
		return []serror.SnapError{serror.New(ErrSubscriptionGroupAlreadyExists)}
	}
	subscriptionGroup := &subscriptionGroup{
		requestedMetrics: requested,
		requestedPlugins: plugins,
		configTree:       configTree,
		pluginControl:    s.pluginControl,
	}
	errs := subscriptionGroup.process(id)
	if errs != nil {
		return errs
	}
	s.subscriptionMap[id] = subscriptionGroup
	return nil
}
func (s subscriptionGroups) Remove(id string) []serror.SnapError {
	s.Lock()
	defer s.Unlock()
	return s.remove(id)
}
func (s subscriptionGroups) remove(id string) []serror.SnapError {
	subscriptionGroup, ok := s.subscriptionMap[id]
	if !ok {
		return []serror.SnapError{serror.New(ErrSubscriptionGroupDoesNotExist)}
	}
	serrs := subscriptionGroup.unsubscribePlugins(id, s.subscriptionMap[id].plugins)
	delete(s.subscriptionMap, id)
	return serrs
}
func (s subscriptionGroups) Get(id string) (map[string]metricTypes, []serror.SnapError, error) {
	s.Lock()
	defer s.Unlock()
	return s.get(id)
}
func (s subscriptionGroups) get(id string) (map[string]metricTypes, []serror.SnapError, error) {
	if _, ok := s.subscriptionMap[id]; !ok {
		return nil, nil, ErrSubscriptionGroupDoesNotExist
	}
	sg := s.subscriptionMap[id]
	return sg.metrics, sg.errors, nil
}
func (s *subscriptionGroups) Process() (errs []serror.SnapError) {
	s.Lock()
	defer s.Unlock()
	for id, group := range s.subscriptionMap {
		if serrs := group.process(id); serrs != nil {
			errs = append(errs, serrs...)
		}
	}
	return errs
}
func (s *subscriptionGroups) ValidateDeps(requested []core.RequestedMetric,
	plugins []core.SubscribedPlugin,
	configTree *cdata.ConfigDataTree, asserts ...core.SubscribedPluginAssert) (serrs []serror.SnapError) {
	pluginToMetricMap, collectors, errs := s.getMetricsAndCollectors(requested, configTree)
	if errs != nil {
		serrs = append(serrs, errs...)
	}
	for _, assert := range asserts {
		if serr := assert(collectors); serr != nil {
			serrs = append(serrs, serr)
		}
	}
	if len(serrs) > 0 {
		return serrs
	}
	for _, pmt := range pluginToMetricMap {
		for _, mt := range pmt.Metrics() {
			errs := s.validateMetric(mt)
			if len(errs) > 0 {
				serrs = append(serrs, errs...)
			}
		}
	}
	for _, collector := range collectors {
		plugins = append(plugins, collector)
	}
	for _, plg := range plugins {
		typ, err := core.ToPluginType(plg.TypeName())
		if err != nil {
			return []serror.SnapError{serror.New(err)}
		}
		mergedConfig := plg.Config().ReverseMerge(
			s.Config.Plugins.getPluginConfigDataNode(
				typ, plg.Name(), plg.Version()))
		errs := s.validatePluginSubscription(plg, mergedConfig)
		if len(errs) > 0 {
			serrs = append(serrs, errs...)
			return serrs
		}
	}
	return
}