go/svc/(dc stuff) -> dc/

We want to start keeping codebases separated per 'team'/intent, to then
have simple OWNER files/trees to specify review rules.

This means dc/ stuff can all be OWNED by q3k, and review will only
involve a +1 for style/readability, instead  of a +2 for approval.

Change-Id: I05afbc4e1018944b841ec0d88cd24cc95bec8bf1
diff --git a/dc/topo/state/state.go b/dc/topo/state/state.go
new file mode 100644
index 0000000..116a55a
--- /dev/null
+++ b/dc/topo/state/state.go
@@ -0,0 +1,76 @@
+package state
+
+import (
+	"context"
+	"fmt"
+	"sync"
+
+	"google.golang.org/grpc"
+
+	"code.hackerspace.pl/hscloud/go/pki"
+	ipb "code.hackerspace.pl/hscloud/proto/infra"
+
+	pb "code.hackerspace.pl/hscloud/dc/topo/proto"
+)
+
+type SwitchportState struct {
+	Proto *ipb.SwitchPort
+}
+
+type SwitchState struct {
+	Name  string
+	Ports []*SwitchportState
+	Stub  ipb.SwitchControlClient
+}
+
+func (s *SwitchState) Fetch(ctx context.Context) error {
+	req := ipb.GetPortsRequest{}
+	res, err := s.Stub.GetPorts(ctx, &req)
+	if err != nil {
+		return fmt.Errorf("GetPorts: %v", err)
+	}
+	s.Ports = make([]*SwitchportState, len(res.Ports))
+	for i, port := range res.Ports {
+		s.Ports[i] = &SwitchportState{port}
+	}
+	return nil
+}
+
+type StateManager struct {
+	Switches map[string]*SwitchState
+	Conns    map[string]*grpc.ClientConn
+	Mu       sync.RWMutex
+}
+
+func NewManager() *StateManager {
+	return &StateManager{
+		Switches: make(map[string]*SwitchState),
+		Conns:    make(map[string]*grpc.ClientConn),
+	}
+}
+
+func (s *StateManager) FetchState(ctx context.Context, conf *pb.Config) error {
+	s.Mu.Lock()
+	defer s.Mu.Unlock()
+	for _, sw := range conf.Switch {
+		conn, ok := s.Conns[sw.ControlAddress]
+		if !ok {
+			var err error
+			conn, err = grpc.Dial(sw.ControlAddress, pki.WithClientHSPKI())
+			if err != nil {
+				return fmt.Errorf("when connecting to switch %s: %v", sw.Name, err)
+			}
+			s.Conns[sw.ControlAddress] = conn
+		}
+
+		s.Switches[sw.Name] = &SwitchState{
+			Name: sw.Name,
+			Stub: ipb.NewSwitchControlClient(conn),
+		}
+		err := s.Switches[sw.Name].Fetch(ctx)
+		if err != nil {
+			return fmt.Errorf("%q.Fetch: %v", sw.Name, err)
+		}
+	}
+	return nil
+}