Browse code

Added support for Generic Resources

Signed-off-by: Renaud Gaubert <rgaubert@nvidia.com>

Renaud Gaubert authored on 2017/05/31 09:02:11
Showing 13 changed files
... ...
@@ -487,6 +487,41 @@ definitions:
487 487
         type: "integer"
488 488
         format: "int64"
489 489
 
490
+  ResourceObject:
491
+    description: "An object describing the resources which can be advertised by a node and requested by a task"
492
+    type: "object"
493
+    properties:
494
+      NanoCPUs:
495
+        type: "integer"
496
+        format: "int64"
497
+      MemoryBytes:
498
+        type: "integer"
499
+        format: "int64"
500
+      GenericResources:
501
+        $ref: "#/definitions/GenericResources"
502
+
503
+  GenericResources:
504
+    description: "User defined Resources, can be either Integer resources (e.g: SSD=3) or String resources (e.g: GPU={UUID1, UUID2})"
505
+    type: "array"
506
+    items:
507
+      type: "object"
508
+      properties:
509
+        NamedResourceSpec:
510
+          type: "object"
511
+          properties:
512
+            Kind:
513
+              type: "string"
514
+            Value:
515
+              type: "string"
516
+        DiscreteResourceSpec:
517
+          type: "object"
518
+          properties:
519
+            Kind:
520
+              type: "string"
521
+            Value:
522
+              type: "integer"
523
+              format: "int64"
524
+
490 525
   HealthConfig:
491 526
     description: "A test to perform to check that the container is healthy."
492 527
     type: "object"
... ...
@@ -1702,14 +1737,7 @@ definitions:
1702 1702
               OS:
1703 1703
                 type: "string"
1704 1704
           Resources:
1705
-            type: "object"
1706
-            properties:
1707
-              NanoCPUs:
1708
-                type: "integer"
1709
-                format: "int64"
1710
-              MemoryBytes:
1711
-                type: "integer"
1712
-                format: "int64"
1705
+            $ref: "#/definitions/ResourceObject"
1713 1706
           Engine:
1714 1707
             type: "object"
1715 1708
             properties:
... ...
@@ -1750,6 +1778,16 @@ definitions:
1750 1750
         Resources:
1751 1751
           NanoCPUs: 4000000000
1752 1752
           MemoryBytes: 8272408576
1753
+          GenericResources:
1754
+            - DiscreteResourceSpec:
1755
+                Kind: "SSD"
1756
+                Value: 3
1757
+            - NamedResourceSpec:
1758
+                Kind: "GPU"
1759
+                Value: "UUID1"
1760
+            - NamedResourceSpec:
1761
+                Kind: "GPU"
1762
+                Value: "UUID2"
1753 1763
         Engine:
1754 1764
           EngineVersion: "17.04.0"
1755 1765
           Labels:
... ...
@@ -2132,27 +2170,10 @@ definitions:
2132 2132
         properties:
2133 2133
           Limits:
2134 2134
             description: "Define resources limits."
2135
-            type: "object"
2136
-            properties:
2137
-              NanoCPUs:
2138
-                description: "CPU limit in units of 10<sup>-9</sup> CPU shares."
2139
-                type: "integer"
2140
-                format: "int64"
2141
-              MemoryBytes:
2142
-                description: "Memory limit in Bytes."
2143
-                type: "integer"
2144
-                format: "int64"
2135
+            $ref: "#/definitions/ResourceObject"
2145 2136
           Reservation:
2146 2137
             description: "Define resources reservation."
2147
-            properties:
2148
-              NanoCPUs:
2149
-                description: "CPU reservation in units of 10<sup>-9</sup> CPU shares."
2150
-                type: "integer"
2151
-                format: "int64"
2152
-              MemoryBytes:
2153
-                description: "Memory reservation in Bytes."
2154
-                type: "integer"
2155
-                format: "int64"
2138
+            $ref: "#/definitions/ResourceObject"
2156 2139
       RestartPolicy:
2157 2140
         description: "Specification for the restart policy which applies to containers created as part of this service."
2158 2141
         type: "object"
... ...
@@ -2283,6 +2304,8 @@ definitions:
2283 2283
       NodeID:
2284 2284
         description: "The ID of the node that this task is on."
2285 2285
         type: "string"
2286
+      AssignedGenericResources:
2287
+        $ref: "#/definitions/GenericResources"
2286 2288
       Status:
2287 2289
         type: "object"
2288 2290
         properties:
... ...
@@ -2362,6 +2385,16 @@ definitions:
2362 2362
                   Gateway: "10.255.0.1"
2363 2363
           Addresses:
2364 2364
             - "10.255.0.10/16"
2365
+      AssignedGenericResources:
2366
+        - DiscreteResourceSpec:
2367
+            Kind: "SSD"
2368
+            Value: 3
2369
+        - NamedResourceSpec:
2370
+            Kind: "GPU"
2371
+            Value: "UUID1"
2372
+        - NamedResourceSpec:
2373
+            Kind: "GPU"
2374
+            Value: "UUID2"
2365 2375
   ServiceSpec:
2366 2376
     description: "User modifiable configuration for a service."
2367 2377
     properties:
... ...
@@ -5392,6 +5425,8 @@ paths:
5392 5392
                   type: "string"
5393 5393
               MemTotal:
5394 5394
                 type: "integer"
5395
+              GenericResources:
5396
+                $ref: "#/definitions/GenericResources"
5395 5397
               MemoryLimit:
5396 5398
                 type: "boolean"
5397 5399
               NCPU:
... ...
@@ -51,6 +51,7 @@ type Task struct {
51 51
 	Status              TaskStatus          `json:",omitempty"`
52 52
 	DesiredState        TaskState           `json:",omitempty"`
53 53
 	NetworksAttachments []NetworkAttachment `json:",omitempty"`
54
+	GenericResources    []GenericResource   `json:",omitempty"`
54 55
 }
55 56
 
56 57
 // TaskSpec represents the spec of a task.
... ...
@@ -79,8 +80,34 @@ type TaskSpec struct {
79 79
 
80 80
 // Resources represents resources (CPU/Memory).
81 81
 type Resources struct {
82
-	NanoCPUs    int64 `json:",omitempty"`
83
-	MemoryBytes int64 `json:",omitempty"`
82
+	NanoCPUs         int64             `json:",omitempty"`
83
+	MemoryBytes      int64             `json:",omitempty"`
84
+	GenericResources []GenericResource `json:",omitempty"`
85
+}
86
+
87
+// GenericResource represents a "user defined" resource which can
88
+// be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1)
89
+type GenericResource struct {
90
+	NamedResourceSpec    *NamedGenericResource    `json:",omitempty"`
91
+	DiscreteResourceSpec *DiscreteGenericResource `json:",omitempty"`
92
+}
93
+
94
+// NamedGenericResource represents a "user defined" resource which is defined
95
+// as a string.
96
+// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
97
+// Value is used to identify the resource (GPU="UUID-1", FPGA="/dev/sdb5", ...)
98
+type NamedGenericResource struct {
99
+	Kind  string `json:",omitempty"`
100
+	Value string `json:",omitempty"`
101
+}
102
+
103
+// DiscreteGenericResource represents a "user defined" resource which is defined
104
+// as an integer
105
+// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
106
+// Value is used to count the resource (SSD=5, HDD=3, ...)
107
+type DiscreteGenericResource struct {
108
+	Kind  string `json:",omitempty"`
109
+	Value int64  `json:",omitempty"`
84 110
 }
85 111
 
86 112
 // ResourceRequirements represents resources requirements.
... ...
@@ -168,6 +168,7 @@ type Info struct {
168 168
 	RegistryConfig     *registry.ServiceConfig
169 169
 	NCPU               int
170 170
 	MemTotal           int64
171
+	GenericResources   []swarm.GenericResource
171 172
 	DockerRootDir      string
172 173
 	HTTPProxy          string `json:"HttpProxy"`
173 174
 	HTTPSProxy         string `json:"HttpsProxy"`
... ...
@@ -62,6 +62,8 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
62 62
 
63 63
 	flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
64 64
 
65
+	flags.StringVar(&conf.NodeGenericResources, "node-generic-resources", "", "user defined resources (e.g. fpga=2;gpu={UUID1,UUID2,UUID3})")
66
+
65 67
 	// "--deprecated-key-path" is to allow configuration of the key used
66 68
 	// for the daemon ID and the deprecated image signing. It was never
67 69
 	// exposed as a command line option but is added here to allow
... ...
@@ -42,6 +42,7 @@ func NodeFromGRPC(n swarmapi.Node) types.Node {
42 42
 		if n.Description.Resources != nil {
43 43
 			node.Description.Resources.NanoCPUs = n.Description.Resources.NanoCPUs
44 44
 			node.Description.Resources.MemoryBytes = n.Description.Resources.MemoryBytes
45
+			node.Description.Resources.GenericResources = GenericResourcesFromGRPC(n.Description.Resources.Generic)
45 46
 		}
46 47
 		if n.Description.Engine != nil {
47 48
 			node.Description.Engine.EngineVersion = n.Description.Engine.EngineVersion
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/docker/api/types/swarm/runtime"
9 9
 	"github.com/docker/docker/pkg/namesgenerator"
10 10
 	swarmapi "github.com/docker/swarmkit/api"
11
+	"github.com/docker/swarmkit/api/genericresource"
11 12
 	"github.com/gogo/protobuf/proto"
12 13
 	gogotypes "github.com/gogo/protobuf/types"
13 14
 	"github.com/pkg/errors"
... ...
@@ -301,6 +302,31 @@ func annotationsFromGRPC(ann swarmapi.Annotations) types.Annotations {
301 301
 	return a
302 302
 }
303 303
 
304
+// GenericResourcesFromGRPC converts a GRPC GenericResource to a GenericResource
305
+func GenericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []types.GenericResource {
306
+	var generic []types.GenericResource
307
+	for _, res := range genericRes {
308
+		var current types.GenericResource
309
+
310
+		switch r := res.Resource.(type) {
311
+		case *swarmapi.GenericResource_DiscreteResourceSpec:
312
+			current.DiscreteResourceSpec = &types.DiscreteGenericResource{
313
+				Kind:  r.DiscreteResourceSpec.Kind,
314
+				Value: r.DiscreteResourceSpec.Value,
315
+			}
316
+		case *swarmapi.GenericResource_NamedResourceSpec:
317
+			current.NamedResourceSpec = &types.NamedGenericResource{
318
+				Kind:  r.NamedResourceSpec.Kind,
319
+				Value: r.NamedResourceSpec.Value,
320
+			}
321
+		}
322
+
323
+		generic = append(generic, current)
324
+	}
325
+
326
+	return generic
327
+}
328
+
304 329
 func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequirements {
305 330
 	var resources *types.ResourceRequirements
306 331
 	if res != nil {
... ...
@@ -313,8 +339,9 @@ func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequir
313 313
 		}
314 314
 		if res.Reservations != nil {
315 315
 			resources.Reservations = &types.Resources{
316
-				NanoCPUs:    res.Reservations.NanoCPUs,
317
-				MemoryBytes: res.Reservations.MemoryBytes,
316
+				NanoCPUs:         res.Reservations.NanoCPUs,
317
+				MemoryBytes:      res.Reservations.MemoryBytes,
318
+				GenericResources: GenericResourcesFromGRPC(res.Reservations.Generic),
318 319
 			}
319 320
 		}
320 321
 	}
... ...
@@ -322,6 +349,24 @@ func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequir
322 322
 	return resources
323 323
 }
324 324
 
325
+// GenericResourcesToGRPC converts a GenericResource to a GRPC GenericResource
326
+func GenericResourcesToGRPC(genericRes []types.GenericResource) []*swarmapi.GenericResource {
327
+	var generic []*swarmapi.GenericResource
328
+	for _, res := range genericRes {
329
+		var r *swarmapi.GenericResource
330
+
331
+		if res.DiscreteResourceSpec != nil {
332
+			r = genericresource.NewDiscrete(res.DiscreteResourceSpec.Kind, res.DiscreteResourceSpec.Value)
333
+		} else if res.NamedResourceSpec != nil {
334
+			r = genericresource.NewString(res.NamedResourceSpec.Kind, res.NamedResourceSpec.Value)
335
+		}
336
+
337
+		generic = append(generic, r)
338
+	}
339
+
340
+	return generic
341
+}
342
+
325 343
 func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirements {
326 344
 	var reqs *swarmapi.ResourceRequirements
327 345
 	if res != nil {
... ...
@@ -336,6 +381,7 @@ func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirem
336 336
 			reqs.Reservations = &swarmapi.Resources{
337 337
 				NanoCPUs:    res.Reservations.NanoCPUs,
338 338
 				MemoryBytes: res.Reservations.MemoryBytes,
339
+				Generic:     GenericResourcesToGRPC(res.Reservations.GenericResources),
339 340
 			}
340 341
 
341 342
 		}
... ...
@@ -30,7 +30,8 @@ func TaskFromGRPC(t swarmapi.Task) (types.Task, error) {
30 30
 			Message: t.Status.Message,
31 31
 			Err:     t.Status.Err,
32 32
 		},
33
-		DesiredState: types.TaskState(strings.ToLower(t.DesiredState.String())),
33
+		DesiredState:     types.TaskState(strings.ToLower(t.DesiredState.String())),
34
+		GenericResources: GenericResourcesFromGRPC(t.AssignedGenericResources),
34 35
 	}
35 36
 
36 37
 	// Meta
... ...
@@ -25,6 +25,7 @@ import (
25 25
 	netconst "github.com/docker/libnetwork/datastore"
26 26
 	"github.com/docker/swarmkit/agent/exec"
27 27
 	"github.com/docker/swarmkit/api"
28
+	"github.com/docker/swarmkit/api/genericresource"
28 29
 	"github.com/docker/swarmkit/template"
29 30
 	gogotypes "github.com/gogo/protobuf/types"
30 31
 )
... ...
@@ -186,13 +187,16 @@ func (c *containerConfig) exposedPorts() map[nat.Port]struct{} {
186 186
 }
187 187
 
188 188
 func (c *containerConfig) config() *enginecontainer.Config {
189
+	genericEnvs := genericresource.EnvFormat(c.task.AssignedGenericResources, "DOCKER_RESOURCE")
190
+	env := append(c.spec().Env, genericEnvs...)
191
+
189 192
 	config := &enginecontainer.Config{
190 193
 		Labels:       c.labels(),
191 194
 		StopSignal:   c.spec().StopSignal,
192 195
 		Tty:          c.spec().TTY,
193 196
 		OpenStdin:    c.spec().OpenStdin,
194 197
 		User:         c.spec().User,
195
-		Env:          c.spec().Env,
198
+		Env:          env,
196 199
 		Hostname:     c.spec().Hostname,
197 200
 		WorkingDir:   c.spec().Dir,
198 201
 		Image:        c.image(),
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/docker/docker/api/types/network"
12 12
 	swarmtypes "github.com/docker/docker/api/types/swarm"
13 13
 	"github.com/docker/docker/daemon/cluster/controllers/plugin"
14
+	"github.com/docker/docker/daemon/cluster/convert"
14 15
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
15 16
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
16 17
 	networktypes "github.com/docker/libnetwork/types"
... ...
@@ -119,6 +120,7 @@ func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) {
119 119
 		Resources: &api.Resources{
120 120
 			NanoCPUs:    int64(info.NCPU) * 1e9,
121 121
 			MemoryBytes: info.MemTotal,
122
+			Generic:     convert.GenericResourcesToGRPC(info.GenericResources),
122 123
 		},
123 124
 	}
124 125
 
... ...
@@ -168,6 +168,9 @@ type CommonConfig struct {
168 168
 	ValuesSet map[string]interface{}
169 169
 
170 170
 	Experimental bool `json:"experimental"` // Experimental indicates whether experimental features should be exposed or not
171
+
172
+	// Exposed node Generic Resources
173
+	NodeGenericResources string `json:"node-generic-resources,omitempty"`
171 174
 }
172 175
 
173 176
 // IsValueSet returns true if a configuration value
... ...
@@ -497,6 +500,10 @@ func Validate(config *Config) error {
497 497
 		}
498 498
 	}
499 499
 
500
+	if _, err := opts.ParseGenericResources(config.NodeGenericResources); err != nil {
501
+		return err
502
+	}
503
+
500 504
 	if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" && defaultRuntime != StockRuntimeName {
501 505
 		runtimes := config.GetAllRuntimes()
502 506
 		if _, ok := runtimes[defaultRuntime]; !ok {
... ...
@@ -23,12 +23,14 @@ import (
23 23
 	"github.com/docker/docker/api"
24 24
 	"github.com/docker/docker/api/types"
25 25
 	containertypes "github.com/docker/docker/api/types/container"
26
+	"github.com/docker/docker/api/types/swarm"
26 27
 	"github.com/docker/docker/container"
27 28
 	"github.com/docker/docker/daemon/config"
28 29
 	"github.com/docker/docker/daemon/discovery"
29 30
 	"github.com/docker/docker/daemon/events"
30 31
 	"github.com/docker/docker/daemon/exec"
31 32
 	"github.com/docker/docker/daemon/logger"
33
+	"github.com/docker/docker/opts"
32 34
 	// register graph drivers
33 35
 	_ "github.com/docker/docker/daemon/graphdriver/register"
34 36
 	"github.com/docker/docker/daemon/initlayer"
... ...
@@ -111,6 +113,7 @@ type Daemon struct {
111 111
 	defaultIsolation      containertypes.Isolation // Default isolation mode on Windows
112 112
 	clusterProvider       cluster.Provider
113 113
 	cluster               Cluster
114
+	genericResources      []swarm.GenericResource
114 115
 	metricsPluginListener net.Listener
115 116
 
116 117
 	machineMemory uint64
... ...
@@ -568,6 +571,9 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
568 568
 		}
569 569
 	}()
570 570
 
571
+	if err := d.setGenericResources(config); err != nil {
572
+		return nil, err
573
+	}
571 574
 	// set up SIGUSR1 handler on Unix-like systems, or a Win32 global event
572 575
 	// on Windows to dump Go routine stacks
573 576
 	stackDumpDir := config.Root
... ...
@@ -1036,6 +1042,17 @@ func (daemon *Daemon) setupInitLayer(initPath string) error {
1036 1036
 	return initlayer.Setup(initPath, rootIDs)
1037 1037
 }
1038 1038
 
1039
+func (daemon *Daemon) setGenericResources(conf *config.Config) error {
1040
+	genericResources, err := opts.ParseGenericResources(conf.NodeGenericResources)
1041
+	if err != nil {
1042
+		return err
1043
+	}
1044
+
1045
+	daemon.genericResources = genericResources
1046
+
1047
+	return nil
1048
+}
1049
+
1039 1050
 func setDefaultMtu(conf *config.Config) {
1040 1051
 	// do nothing if the config does not have the default 0 value.
1041 1052
 	if conf.Mtu != 0 {
... ...
@@ -123,6 +123,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
123 123
 		RegistryConfig:     daemon.RegistryService.ServiceConfig(),
124 124
 		NCPU:               sysinfo.NumCPU(),
125 125
 		MemTotal:           meminfo.MemTotal,
126
+		GenericResources:   daemon.genericResources,
126 127
 		DockerRootDir:      daemon.configStore.Root,
127 128
 		Labels:             daemon.configStore.Labels,
128 129
 		ExperimentalBuild:  daemon.configStore.Experimental,
... ...
@@ -7,7 +7,10 @@ import (
7 7
 	"regexp"
8 8
 	"strings"
9 9
 
10
+	"github.com/docker/docker/api/types/swarm"
11
+	"github.com/docker/docker/daemon/cluster/convert"
10 12
 	units "github.com/docker/go-units"
13
+	"github.com/docker/swarmkit/api/genericresource"
11 14
 )
12 15
 
13 16
 var (
... ...
@@ -325,3 +328,19 @@ func (m *MemBytes) UnmarshalJSON(s []byte) error {
325 325
 	*m = MemBytes(val)
326 326
 	return err
327 327
 }
328
+
329
+// ParseGenericResources parses and validates the specified string as a list of GenericResource
330
+func ParseGenericResources(value string) ([]swarm.GenericResource, error) {
331
+	if value == "" {
332
+		return nil, nil
333
+	}
334
+
335
+	resources, err := genericresource.Parse(value)
336
+	if err != nil {
337
+		return nil, err
338
+	}
339
+
340
+	obj := convert.GenericResourcesFromGRPC(resources)
341
+
342
+	return obj, nil
343
+}