changeset 7070 | cc52fb049d9b |
parent 6874 | 4b09efc24535 |
child 7082 | c589e79cefef |
7066:83336d48cee7 | 7070:cc52fb049d9b |
---|---|
1 In-house patch which contains a full port of the v1.10.3 Docker |
1 From 3cf1310ac1de1c3ed1be6341d5d8e6afe255c89f Mon Sep 17 00:00:00 2001 |
2 Engine for Solaris. This is being integrated as the first |
2 From: Amit Krishnan <[email protected]> |
3 version of Docker on Solaris, targeting release with Solaris 12. |
3 Date: Thu, 6 Oct 2016 14:24:52 -0700 |
4 |
|
5 While work is ongoing upstream in the public project, and |
|
6 most if not all of that code will be integrated upstream, that |
|
7 work will not be done in time to target a Solaris 12 release. |
|
8 This version is the first in hopefully many, and this patch |
|
9 will be deprecated in future release integrations. |
|
10 Subject: [PATCH] Solaris-v1.10.3 |
4 Subject: [PATCH] Solaris-v1.10.3 |
11 |
5 |
12 --- |
6 --- |
13 Dockerfile.solaris | 26 + |
7 Dockerfile.solaris | 26 + |
14 Makefile | 35 +- |
8 Makefile | 35 +- |
15 api/client/run.go | 1 + |
9 api/client/run.go | 1 + |
10 api/client/version.go | 20 - |
|
16 api/server/router/container/container_routes.go | 7 + |
11 api/server/router/container/container_routes.go | 7 + |
17 api/server/server_unix.go | 2 +- |
12 api/server/server_unix.go | 2 +- |
18 container/container_solaris.go | 649 ++++++++++++ |
13 container/container_solaris.go | 649 ++++++++++++ |
19 container/monitor.go | 9 + |
14 container/monitor.go | 9 + |
20 container/state_solaris.go | 9 + |
15 container/state_solaris.go | 9 + |
25 daemon/commit.go | 22 +- |
20 daemon/commit.go | 22 +- |
26 daemon/config.go | 4 + |
21 daemon/config.go | 4 + |
27 daemon/config_solaris.go | 66 ++ |
22 daemon/config_solaris.go | 66 ++ |
28 daemon/config_test.go | 30 +- |
23 daemon/config_test.go | 30 +- |
29 daemon/container_operations_solaris.go | 973 ++++++++++++++++++ |
24 daemon/container_operations_solaris.go | 973 ++++++++++++++++++ |
30 daemon/daemon.go | 9 +- |
25 daemon/daemon.go | 6 +- |
31 daemon/daemon_solaris.go | 544 ++++++++++ |
26 daemon/daemon_solaris.go | 544 ++++++++++ |
32 daemon/daemon_test.go | 2 + |
27 daemon/daemon_test.go | 2 + |
33 daemon/daemon_unix_test.go | 2 +- |
28 daemon/daemon_unix_test.go | 2 +- |
34 daemon/daemon_unsupported.go | 2 +- |
29 daemon/daemon_unsupported.go | 2 +- |
35 daemon/daemonbuilder/builder_unix.go | 2 +- |
30 daemon/daemonbuilder/builder_unix.go | 2 +- |
36 daemon/debugtrap_unsupported.go | 2 +- |
31 daemon/debugtrap_unsupported.go | 2 +- |
37 daemon/exec_solaris.go | 18 + |
32 daemon/exec_solaris.go | 18 + |
38 daemon/execdriver/driver_solaris.go | 76 ++ |
33 daemon/execdriver/driver_solaris.go | 76 ++ |
39 daemon/execdriver/driver_unix.go | 2 +- |
34 daemon/execdriver/driver_unix.go | 2 +- |
40 .../execdriver/execdrivers/execdrivers_solaris.go | 13 + |
35 .../execdriver/execdrivers/execdrivers_solaris.go | 13 + |
41 daemon/execdriver/zones/driver.go | 772 ++++++++++++++ |
36 daemon/execdriver/zones/driver.go | 745 ++++++++++++++ |
42 daemon/execdriver/zones/driver_unsupported.go | 12 + |
37 daemon/execdriver/zones/driver_unsupported.go | 12 + |
43 .../execdriver/zones/driver_unsupported_nocgo.go | 13 + |
38 .../execdriver/zones/driver_unsupported_nocgo.go | 13 + |
44 daemon/graphdriver/driver_solaris.go | 8 + |
39 daemon/graphdriver/driver_solaris.go | 8 + |
45 daemon/graphdriver/driver_unsupported.go | 2 +- |
40 daemon/graphdriver/driver_unsupported.go | 2 +- |
46 daemon/graphdriver/graphtest/graphtest_unix.go | 2 +- |
41 daemon/graphdriver/graphtest/graphtest_unix.go | 2 +- |
48 daemon/graphdriver/zfs/zfs.go | 47 +- |
43 daemon/graphdriver/zfs/zfs.go | 47 +- |
49 daemon/graphdriver/zfs/zfs_freebsd.go | 36 + |
44 daemon/graphdriver/zfs/zfs_freebsd.go | 36 + |
50 daemon/graphdriver/zfs/zfs_linux.go | 37 + |
45 daemon/graphdriver/zfs/zfs_linux.go | 37 + |
51 daemon/graphdriver/zfs/zfs_solaris.go | 95 ++ |
46 daemon/graphdriver/zfs/zfs_solaris.go | 95 ++ |
52 daemon/graphdriver/zfs/zfs_unsupported.go | 2 +- |
47 daemon/graphdriver/zfs/zfs_unsupported.go | 2 +- |
48 daemon/info.go | 2 - |
|
53 daemon/inspect_solaris.go | 30 + |
49 daemon/inspect_solaris.go | 30 + |
54 daemon/inspect_unix.go | 2 +- |
50 daemon/inspect_unix.go | 2 +- |
55 daemon/list_unix.go | 2 +- |
51 daemon/list_unix.go | 2 +- |
56 daemon/network.go | 7 + |
52 daemon/network.go | 7 + |
57 daemon/selinux_unsupported.go | 8 + |
53 daemon/selinux_unsupported.go | 8 + |
58 daemon/start.go | 63 ++ |
54 daemon/start.go | 65 ++ |
59 daemon/stats_collector_solaris.go | 139 +++ |
55 daemon/stats_collector_solaris.go | 537 ++++++++++ |
60 daemon/stats_collector_unix.go | 2 +- |
56 daemon/stats_collector_unix.go | 2 +- |
61 daemon/stats_solaris.go | 82 ++ |
57 daemon/stats_solaris.go | 84 ++ |
62 docker/daemon_solaris.go | 58 ++ |
58 docker/daemon_solaris.go | 58 ++ |
63 docker/daemon_unix.go | 2 +- |
59 docker/daemon_unix.go | 2 +- |
64 hack/.vendor-helpers.sh | 8 +- |
60 hack/.vendor-helpers.sh | 8 +- |
65 hack/make.sh | 14 +- |
61 hack/make.sh | 14 +- |
66 hack/make/.detect-daemon-osarch | 20 +- |
62 hack/make/.detect-daemon-osarch | 20 +- |
126 pkg/reexec/command_solaris.go | 23 + |
122 pkg/reexec/command_solaris.go | 23 + |
127 pkg/reexec/command_unsupported.go | 2 +- |
123 pkg/reexec/command_unsupported.go | 2 +- |
128 pkg/signal/signal_solaris.go | 42 + |
124 pkg/signal/signal_solaris.go | 42 + |
129 pkg/signal/signal_unsupported.go | 2 +- |
125 pkg/signal/signal_unsupported.go | 2 +- |
130 pkg/sysinfo/sysinfo_solaris.go | 117 +++ |
126 pkg/sysinfo/sysinfo_solaris.go | 117 +++ |
131 pkg/system/meminfo_solaris.go | 133 +++ |
127 pkg/system/meminfo_solaris.go | 127 +++ |
132 pkg/system/meminfo_unsupported.go | 2 +- |
128 pkg/system/meminfo_unsupported.go | 2 +- |
133 pkg/system/stat_linux.go | 33 - |
129 pkg/system/stat_linux.go | 33 - |
134 pkg/system/stat_solaris.go | 20 +- |
130 pkg/system/stat_solaris.go | 20 +- |
135 pkg/system/stat_unix.go | 35 + |
131 pkg/system/stat_unix.go | 35 + |
136 pkg/system/stat_unsupported.go | 2 +- |
132 pkg/system/stat_unsupported.go | 2 +- |
146 runconfig/hostconfig_unix.go | 1 + |
142 runconfig/hostconfig_unix.go | 1 + |
147 runconfig/opts/parse.go | 2 + |
143 runconfig/opts/parse.go | 2 + |
148 .../github.com/Sirupsen/logrus/terminal_solaris.go | 15 + |
144 .../github.com/Sirupsen/logrus/terminal_solaris.go | 15 + |
149 .../docker/engine-api/types/container/config.go | 4 + |
145 .../docker/engine-api/types/container/config.go | 4 + |
150 .../engine-api/types/container/host_config.go | 1 + |
146 .../engine-api/types/container/host_config.go | 1 + |
147 .../github.com/docker/engine-api/types/types.go | 2 - |
|
151 .../docker/go-connections/sockets/unix_socket.go | 2 +- |
148 .../docker/go-connections/sockets/unix_socket.go | 2 +- |
152 .../docker/libnetwork/default_gateway_solaris.go | 7 + |
149 .../docker/libnetwork/default_gateway_solaris.go | 7 + |
153 .../libnetwork/drivers/solaris/bridge/bridge.go | 1062 ++++++++++++++++++++ |
150 .../libnetwork/drivers/solaris/bridge/bridge.go | 1076 ++++++++++++++++++++ |
154 .../drivers/solaris/bridge/bridge_store.go | 212 ++++ |
151 .../drivers/solaris/bridge/bridge_store.go | 212 ++++ |
155 .../libnetwork/drivers/solaris/bridge/errors.go | 341 +++++++ |
152 .../libnetwork/drivers/solaris/bridge/errors.go | 341 +++++++ |
156 .../drivers/solaris/bridge/port_mapping.go | 199 ++++ |
153 .../drivers/solaris/bridge/port_mapping.go | 218 ++++ |
157 .../docker/libnetwork/drivers_solaris.go | 13 + |
154 .../docker/libnetwork/drivers_solaris.go | 13 + |
158 .../docker/libnetwork/ipamutils/utils_solaris.go | 92 ++ |
155 .../docker/libnetwork/ipamutils/utils_solaris.go | 92 ++ |
159 vendor/src/github.com/docker/libnetwork/network.go | 2 - |
156 vendor/src/github.com/docker/libnetwork/network.go | 2 - |
160 .../docker/libnetwork/osl/interface_solaris.go | 6 + |
157 .../docker/libnetwork/osl/interface_solaris.go | 6 + |
161 .../docker/libnetwork/osl/namespace_solaris.go | 41 + |
158 .../docker/libnetwork/osl/namespace_solaris.go | 41 + |
170 .../docker/libnetwork/portmapper/mapper_solaris.go | 150 +++ |
167 .../docker/libnetwork/portmapper/mapper_solaris.go | 150 +++ |
171 .../docker/libnetwork/portmapper/mock_proxy.go | 18 - |
168 .../docker/libnetwork/portmapper/mock_proxy.go | 18 - |
172 .../libnetwork/portmapper/mock_proxy_linux.go | 18 + |
169 .../libnetwork/portmapper/mock_proxy_linux.go | 18 + |
173 .../docker/libnetwork/portmapper/proxy.go | 209 ---- |
170 .../docker/libnetwork/portmapper/proxy.go | 209 ---- |
174 .../docker/libnetwork/portmapper/proxy_linux.go | 209 ++++ |
171 .../docker/libnetwork/portmapper/proxy_linux.go | 209 ++++ |
172 vendor/src/github.com/docker/libnetwork/sandbox.go | 7 +- |
|
175 .../libnetwork/sandbox_externalkey_solaris.go | 45 + |
173 .../libnetwork/sandbox_externalkey_solaris.go | 45 + |
176 .../docker/libnetwork/sandbox_externalkey_unix.go | 2 +- |
174 .../docker/libnetwork/sandbox_externalkey_unix.go | 2 +- |
177 .../src/github.com/godbus/dbus/transport_unix.go | 2 +- |
175 .../src/github.com/godbus/dbus/transport_unix.go | 2 +- |
178 vendor/src/github.com/kr/pty/ioctl.go | 2 + |
176 vendor/src/github.com/kr/pty/ioctl.go | 2 + |
179 vendor/src/github.com/kr/pty/util.go | 11 - |
177 vendor/src/github.com/kr/pty/util.go | 11 - |
188 .../github.com/mistifyio/go-zfs/zpool_solaris.go | 40 + |
186 .../github.com/mistifyio/go-zfs/zpool_solaris.go | 40 + |
189 .../runc/libcontainer/configs/cgroup_solaris.go | 6 + |
187 .../runc/libcontainer/configs/cgroup_solaris.go | 6 + |
190 .../libcontainer/configs/cgroup_unsupported.go | 2 +- |
188 .../libcontainer/configs/cgroup_unsupported.go | 2 +- |
191 .../runc/libcontainer/configs/device_defaults.go | 4 +- |
189 .../runc/libcontainer/configs/device_defaults.go | 4 +- |
192 .../runc/libcontainer/console_solaris.go | 13 + |
190 .../runc/libcontainer/console_solaris.go | 13 + |
193 .../runc/libcontainer/container_solaris.go | 103 ++ |
191 .../runc/libcontainer/container_solaris.go | 22 + |
194 .../runc/libcontainer/stats_solaris.go | 8 + |
192 .../runc/libcontainer/stats_solaris.go | 8 + |
195 .../runc/libcontainer/system/sysconfig.go | 2 +- |
193 .../runc/libcontainer/system/sysconfig.go | 2 +- |
196 .../runc/libcontainer/zones/stats.go | 86 ++ |
194 .../runc/libcontainer/zones/stats.go | 86 ++ |
197 vendor/src/gopkg.in/fsnotify.v1/fen.go | 188 ++++ |
195 vendor/src/gopkg.in/fsnotify.v1/fen.go | 188 ++++ |
198 vendor/src/gopkg.in/fsnotify.v1/fen_cgo.go | 82 ++ |
196 vendor/src/gopkg.in/fsnotify.v1/fen_cgo.go | 82 ++ |
199 vendor/src/gopkg.in/fsnotify.v1/fsnotify.go | 2 +- |
197 vendor/src/gopkg.in/fsnotify.v1/fsnotify.go | 2 +- |
200 volume/local/local_unix.go | 2 +- |
198 volume/local/local_unix.go | 2 +- |
201 volume/store/store_unix.go | 2 +- |
199 volume/store/store_unix.go | 2 +- |
202 189 files changed, 8839 insertions(+), 1215 deletions(-) |
200 193 files changed, 9162 insertions(+), 1241 deletions(-) |
203 create mode 100644 Dockerfile.solaris |
201 create mode 100644 Dockerfile.solaris |
204 create mode 100644 container/container_solaris.go |
202 create mode 100644 container/container_solaris.go |
205 create mode 100644 container/state_solaris.go |
203 create mode 100644 container/state_solaris.go |
206 create mode 100755 contrib/mkimage/solaris |
204 create mode 100755 contrib/mkimage/solaris |
207 create mode 100644 daemon/config_solaris.go |
205 create mode 100644 daemon/config_solaris.go |
396 } else { |
394 } else { |
397 + |
395 + |
398 // No Autoremove: Simply retrieve the exit code |
396 // No Autoremove: Simply retrieve the exit code |
399 if !config.Tty { |
397 if !config.Tty { |
400 // In non-TTY mode, we can't detach, so we must wait for container exit |
398 // In non-TTY mode, we can't detach, so we must wait for container exit |
399 diff --git a/api/client/version.go b/api/client/version.go |
|
400 index a64deef..3000b1a 100644 |
|
401 --- a/api/client/version.go |
|
402 +++ b/api/client/version.go |
|
403 @@ -3,7 +3,6 @@ package client |
|
404 import ( |
|
405 "runtime" |
|
406 "text/template" |
|
407 - "time" |
|
408 |
|
409 Cli "github.com/docker/docker/cli" |
|
410 "github.com/docker/docker/dockerversion" |
|
411 @@ -16,8 +15,6 @@ var versionTemplate = `Client: |
|
412 Version: {{.Client.Version}} |
|
413 API version: {{.Client.APIVersion}} |
|
414 Go version: {{.Client.GoVersion}} |
|
415 - Git commit: {{.Client.GitCommit}} |
|
416 - Built: {{.Client.BuildTime}} |
|
417 OS/Arch: {{.Client.Os}}/{{.Client.Arch}}{{if .Client.Experimental}} |
|
418 Experimental: {{.Client.Experimental}}{{end}}{{if .ServerOK}} |
|
419 |
|
420 @@ -25,8 +22,6 @@ Server: |
|
421 Version: {{.Server.Version}} |
|
422 API version: {{.Server.APIVersion}} |
|
423 Go version: {{.Server.GoVersion}} |
|
424 - Git commit: {{.Server.GitCommit}} |
|
425 - Built: {{.Server.BuildTime}} |
|
426 OS/Arch: {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}} |
|
427 Experimental: {{.Server.Experimental}}{{end}}{{end}}` |
|
428 |
|
429 @@ -58,8 +53,6 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) { |
|
430 Version: dockerversion.Version, |
|
431 APIVersion: cli.client.ClientVersion(), |
|
432 GoVersion: runtime.Version(), |
|
433 - GitCommit: dockerversion.GitCommit, |
|
434 - BuildTime: dockerversion.BuildTime, |
|
435 Os: runtime.GOOS, |
|
436 Arch: runtime.GOARCH, |
|
437 Experimental: utils.ExperimentalBuild(), |
|
438 @@ -71,19 +64,6 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) { |
|
439 vd.Server = &serverVersion |
|
440 } |
|
441 |
|
442 - // first we need to make BuildTime more human friendly |
|
443 - t, errTime := time.Parse(time.RFC3339Nano, vd.Client.BuildTime) |
|
444 - if errTime == nil { |
|
445 - vd.Client.BuildTime = t.Format(time.ANSIC) |
|
446 - } |
|
447 - |
|
448 - if vd.ServerOK() { |
|
449 - t, errTime = time.Parse(time.RFC3339Nano, vd.Server.BuildTime) |
|
450 - if errTime == nil { |
|
451 - vd.Server.BuildTime = t.Format(time.ANSIC) |
|
452 - } |
|
453 - } |
|
454 - |
|
455 if err2 := tmpl.Execute(cli.out, vd); err2 != nil && err == nil { |
|
456 err = err2 |
|
457 } |
|
401 diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go |
458 diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go |
402 index 4e2ffca..e58405b 100644 |
459 index 4e2ffca..e58405b 100644 |
403 --- a/api/server/router/container/container_routes.go |
460 --- a/api/server/router/container/container_routes.go |
404 +++ b/api/server/router/container/container_routes.go |
461 +++ b/api/server/router/container/container_routes.go |
405 @@ -27,6 +27,11 @@ import ( |
462 @@ -27,6 +27,11 @@ import ( |
2469 + |
2526 + |
2470 +func errRemovalContainer(containerID string) error { |
2527 +func errRemovalContainer(containerID string) error { |
2471 + return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID) |
2528 + return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID) |
2472 +} |
2529 +} |
2473 diff --git a/daemon/daemon.go b/daemon/daemon.go |
2530 diff --git a/daemon/daemon.go b/daemon/daemon.go |
2474 index 6cb7f8c..3d2b2f8 100644 |
2531 index 6cb7f8c..f360392 100644 |
2475 --- a/daemon/daemon.go |
2532 --- a/daemon/daemon.go |
2476 +++ b/daemon/daemon.go |
2533 +++ b/daemon/daemon.go |
2477 @@ -766,7 +766,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo |
2534 @@ -766,7 +766,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo |
2478 sysInfo := sysinfo.New(false) |
2535 sysInfo := sysinfo.New(false) |
2479 // Check if Devices cgroup is mounted, it is hard requirement for container security, |
2536 // Check if Devices cgroup is mounted, it is hard requirement for container security, |
2495 } |
2552 } |
2496 + return nil |
2553 + return nil |
2497 } |
2554 } |
2498 |
2555 |
2499 // Run uses the execution driver to run a given container |
2556 // Run uses the execution driver to run a given container |
2500 @@ -1040,6 +1042,9 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error { |
|
2501 |
|
2502 // PushImage initiates a push operation on the repository named localName. |
|
2503 func (daemon *Daemon) PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { |
|
2504 + if runtime.GOOS == "solaris" { |
|
2505 + return fmt.Errorf("Pushing an image not supported on Solaris platform") |
|
2506 + } |
|
2507 // Include a buffer so that slow client connections don't affect |
|
2508 // transfer performance. |
|
2509 progressChan := make(chan progress.Progress, 100) |
|
2510 diff --git a/daemon/daemon_solaris.go b/daemon/daemon_solaris.go |
2557 diff --git a/daemon/daemon_solaris.go b/daemon/daemon_solaris.go |
2511 new file mode 100644 |
2558 new file mode 100644 |
2512 index 0000000..ebec5ad |
2559 index 0000000..ebec5ad |
2513 --- /dev/null |
2560 --- /dev/null |
2514 +++ b/daemon/daemon_solaris.go |
2561 +++ b/daemon/daemon_solaris.go |
3242 +func NewDriver(options []string, root, libPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) { |
3289 +func NewDriver(options []string, root, libPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) { |
3243 + return zones.NewDriver(root, options) |
3290 + return zones.NewDriver(root, options) |
3244 +} |
3291 +} |
3245 diff --git a/daemon/execdriver/zones/driver.go b/daemon/execdriver/zones/driver.go |
3292 diff --git a/daemon/execdriver/zones/driver.go b/daemon/execdriver/zones/driver.go |
3246 new file mode 100644 |
3293 new file mode 100644 |
3247 index 0000000..b407ba5 |
3294 index 0000000..de5b637 |
3248 --- /dev/null |
3295 --- /dev/null |
3249 +++ b/daemon/execdriver/zones/driver.go |
3296 +++ b/daemon/execdriver/zones/driver.go |
3250 @@ -0,0 +1,772 @@ |
3297 @@ -0,0 +1,745 @@ |
3251 +// +build solaris,cgo |
3298 +// +build solaris,cgo |
3252 + |
3299 + |
3253 +package zones |
3300 +package zones |
3254 + |
3301 + |
3255 +import ( |
3302 +import ( |
3264 + "path/filepath" |
3311 + "path/filepath" |
3265 + "strconv" |
3312 + "strconv" |
3266 + "strings" |
3313 + "strings" |
3267 + "sync" |
3314 + "sync" |
3268 + "syscall" |
3315 + "syscall" |
3269 + "time" |
|
3270 + |
3316 + |
3271 + "github.com/Sirupsen/logrus" |
3317 + "github.com/Sirupsen/logrus" |
3272 + "github.com/docker/docker/daemon/execdriver" |
3318 + "github.com/docker/docker/daemon/execdriver" |
3273 + sysinfo "github.com/docker/docker/pkg/system" |
|
3274 + "github.com/opencontainers/runc/libcontainer" |
3319 + "github.com/opencontainers/runc/libcontainer" |
3275 +) |
3320 +) |
3276 + |
3321 + |
3277 +/* |
3322 +/* |
3278 + |
3323 + |
3369 +} |
3414 +} |
3370 + |
3415 + |
3371 +type Driver struct { |
3416 +type Driver struct { |
3372 + root string |
3417 + root string |
3373 + activeContainers map[string]*activeContainer |
3418 + activeContainers map[string]*activeContainer |
3374 + machineMemory int64 |
|
3375 + sync.Mutex |
3419 + sync.Mutex |
3376 +} |
3420 +} |
3377 + |
3421 + |
3378 +type info struct { |
3422 +type info struct { |
3379 + ID string |
3423 + ID string |
3445 + Uid int64 `json:"uid"` |
3489 + Uid int64 `json:"uid"` |
3446 +} |
3490 +} |
3447 + |
3491 + |
3448 +func startWrapper(cmd *exec.Cmd) error { |
3492 +func startWrapper(cmd *exec.Cmd) error { |
3449 + // create processes in their own process contracts |
3493 + // create processes in their own process contracts |
3450 + var errn, cttmpl C.int |
3494 + var cttmpl C.int |
3451 + |
3495 + var err error |
3452 + if cttmpl = C.ct_pr_tmpl(); cttmpl == -1 { |
3496 + |
3453 + errno_msg := C.GoString(C.strerror(errn)) |
3497 + if cttmpl, err = C.ct_pr_tmpl(); cttmpl == -1 { |
3454 + return errors.New("Failed to create process contract template: " + errno_msg) |
3498 + return err |
3455 + } |
3499 + } |
3456 + defer func() { |
3500 + defer func() { |
3457 + if errn = C.ct_abandon_latest(); errn != 0 { |
3501 + if errn := C.ct_abandon_latest(); errn != 0 { |
3458 + logrus.Error("Failed to abandon process contract: %v", C.GoString(C.strerror(errn))) |
3502 + logrus.Error("Failed to abandon process contract: ", C.GoString(C.strerror(errn))) |
3459 + } else if errn = C.ct_clear(cttmpl); errn != 0 { |
3503 + } |
3460 + logrus.Error("Failed to clear process contract template %v", C.GoString(C.strerror(errn))) |
3504 + if errn := C.ct_clear(cttmpl); errn != 0 { |
3505 + logrus.Error("Failed to clear process contract template ", C.GoString(C.strerror(errn))) |
|
3461 + } |
3506 + } |
3462 + }() |
3507 + }() |
3463 + err := cmd.Start() |
3508 + err = cmd.Start() |
3464 + |
3509 + |
3465 + return err |
3510 + return err |
3466 +} |
3511 +} |
3467 + |
3512 + |
3468 +func NewDriver(root string, options []string) (*Driver, error) { |
3513 +func NewDriver(root string, options []string) (*Driver, error) { |
3469 + meminfo, err := sysinfo.ReadMemInfo() |
|
3470 + if err != nil { |
|
3471 + return nil, err |
|
3472 + } |
|
3473 + if err := os.MkdirAll(root, 0700); err != nil { |
3514 + if err := os.MkdirAll(root, 0700); err != nil { |
3474 + return nil, err |
3515 + return nil, err |
3475 + } |
3516 + } |
3476 + |
3517 + |
3477 + return &Driver{ |
3518 + return &Driver{ |
3478 + root: root, |
3519 + root: root, |
3479 + activeContainers: make(map[string]*activeContainer), |
3520 + activeContainers: make(map[string]*activeContainer), |
3480 + machineMemory: meminfo.MemTotal, |
|
3481 + }, nil |
3521 + }, nil |
3482 +} |
3522 +} |
3483 + |
3523 + |
3484 +func getEnv(key string, env []string) string { |
3524 +func getEnv(key string, env []string) string { |
3485 + for _, pair := range env { |
3525 + for _, pair := range env { |
3824 + r, w, err := os.Pipe() |
3864 + r, w, err := os.Pipe() |
3825 + if err != nil { |
3865 + if err != nil { |
3826 + return execdriver.ExitStatus{ExitCode: -1}, err |
3866 + return execdriver.ExitStatus{ExitCode: -1}, err |
3827 + } |
3867 + } |
3828 + |
3868 + |
3829 + |
|
3830 + if pipes.Stdin != nil { |
3869 + if pipes.Stdin != nil { |
3831 + go func() { |
3870 + go func() { |
3832 + io.Copy(w, pipes.Stdin) |
3871 + io.Copy(w, pipes.Stdin) |
3833 + w.Close() |
3872 + w.Close() |
3834 + }() |
3873 + }() |
3971 + return os.RemoveAll(filepath.Join(d.root, id)) |
4010 + return os.RemoveAll(filepath.Join(d.root, id)) |
3972 +} |
4011 +} |
3973 + |
4012 + |
3974 +// Stats implements the exec driver Driver interface. |
4013 +// Stats implements the exec driver Driver interface. |
3975 +func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) { |
4014 +func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) { |
3976 + stats, err := libcontainer.GetStats(id) |
4015 + return nil, errors.New("Stats is not supported in zones execdriver") |
3977 + if err != nil { |
|
3978 + return nil, err |
|
3979 + } |
|
3980 + // XXX: get from container configuration the memory resource limit |
|
3981 + // in linux: c.Config().Cgroups.Resources.Memory |
|
3982 + var memoryLimit int64 = 0 |
|
3983 + // if the container does not have any memory limit specified set the |
|
3984 + // limit to the machines memory |
|
3985 + if memoryLimit == 0 { |
|
3986 + memoryLimit = d.machineMemory |
|
3987 + } |
|
3988 + |
|
3989 + p := &execdriver.ResourceStats{ |
|
3990 + Stats: stats, |
|
3991 + Read: time.Now(), |
|
3992 + MemoryLimit: memoryLimit, |
|
3993 + } |
|
3994 + |
|
3995 + return p, nil |
|
3996 +} |
4016 +} |
3997 + |
4017 + |
3998 +// Stats implements the exec driver Driver interface. |
4018 +// Stats implements the exec driver Driver interface. |
3999 +func (d *Driver) Update(c *execdriver.Command) error { |
4019 +func (d *Driver) Update(c *execdriver.Command) error { |
4000 + return errors.New("Update is not supported in zones execdriver") |
4020 + return errors.New("Update is not supported in zones execdriver") |
4415 -// +build !linux,!freebsd |
4435 -// +build !linux,!freebsd |
4416 +// +build !linux,!freebsd,!solaris |
4436 +// +build !linux,!freebsd,!solaris |
4417 |
4437 |
4418 package zfs |
4438 package zfs |
4419 |
4439 |
4440 diff --git a/daemon/info.go b/daemon/info.go |
|
4441 index 008ac20..3d86bc0 100644 |
|
4442 --- a/daemon/info.go |
|
4443 +++ b/daemon/info.go |
|
4444 @@ -134,11 +134,9 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { |
|
4445 func (daemon *Daemon) SystemVersion() types.Version { |
|
4446 v := types.Version{ |
|
4447 Version: dockerversion.Version, |
|
4448 - GitCommit: dockerversion.GitCommit, |
|
4449 GoVersion: runtime.Version(), |
|
4450 Os: runtime.GOOS, |
|
4451 Arch: runtime.GOARCH, |
|
4452 - BuildTime: dockerversion.BuildTime, |
|
4453 Experimental: utils.ExperimentalBuild(), |
|
4454 } |
|
4455 |
|
4420 diff --git a/daemon/inspect_solaris.go b/daemon/inspect_solaris.go |
4456 diff --git a/daemon/inspect_solaris.go b/daemon/inspect_solaris.go |
4421 new file mode 100644 |
4457 new file mode 100644 |
4422 index 0000000..e42a61d |
4458 index 0000000..e42a61d |
4423 --- /dev/null |
4459 --- /dev/null |
4424 +++ b/daemon/inspect_solaris.go |
4460 +++ b/daemon/inspect_solaris.go |
4527 + |
4563 + |
4528 +func mergeLxcConfIntoOptions(hostConfig *containertypes.HostConfig) ([]string, error) { |
4564 +func mergeLxcConfIntoOptions(hostConfig *containertypes.HostConfig) ([]string, error) { |
4529 + return nil, nil |
4565 + return nil, nil |
4530 +} |
4566 +} |
4531 diff --git a/daemon/start.go b/daemon/start.go |
4567 diff --git a/daemon/start.go b/daemon/start.go |
4532 index 418dace..c74f07a 100644 |
4568 index 418dace..9d5e6cb 100644 |
4533 --- a/daemon/start.go |
4569 --- a/daemon/start.go |
4534 +++ b/daemon/start.go |
4570 +++ b/daemon/start.go |
4535 @@ -1,7 +1,10 @@ |
4571 @@ -1,7 +1,10 @@ |
4536 package daemon |
4572 package daemon |
4537 |
4573 |
4550 +const SVCCFG = "/usr/sbin/svccfg" |
4586 +const SVCCFG = "/usr/sbin/svccfg" |
4551 + |
4587 + |
4552 // ContainerStart starts a container. |
4588 // ContainerStart starts a container. |
4553 func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig) error { |
4589 func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig) error { |
4554 container, err := daemon.GetContainer(name) |
4590 container, err := daemon.GetContainer(name) |
4555 @@ -109,6 +114,10 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error) |
4591 @@ -142,6 +147,24 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error) |
4556 return err |
|
4557 } |
|
4558 |
|
4559 + if err := daemon.injectHostConfig(container); err != nil { |
|
4560 + return err |
|
4561 + } |
|
4562 + |
|
4563 // Make sure NetworkMode has an acceptable value. We do this to ensure |
|
4564 // backwards API compatibility. |
|
4565 container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig) |
|
4566 @@ -142,6 +151,18 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error) |
|
4567 mounts = append(mounts, container.TmpfsMounts()...) |
4592 mounts = append(mounts, container.TmpfsMounts()...) |
4568 |
4593 |
4569 container.Command.Mounts = mounts |
4594 container.Command.Mounts = mounts |
4570 + |
4595 + |
4571 + if runtime.GOOS == "solaris" { |
4596 + if runtime.GOOS == "solaris" { |
4577 + } |
4602 + } |
4578 + } |
4603 + } |
4579 + container.Command.ContOS = img.Os |
4604 + container.Command.ContOS = img.Os |
4580 + } |
4605 + } |
4581 + |
4606 + |
4607 + if container.Command.ContOS == "solaris" { |
|
4608 + if err := daemon.injectHostConfig(container); err != nil { |
|
4609 + return err |
|
4610 + } |
|
4611 + |
|
4612 + } |
|
4582 if err := daemon.waitForStart(container); err != nil { |
4613 if err := daemon.waitForStart(container); err != nil { |
4583 return err |
4614 return err |
4584 } |
4615 } |
4585 @@ -170,3 +191,45 @@ func (daemon *Daemon) Cleanup(container *container.Container) { |
4616 @@ -170,3 +193,45 @@ func (daemon *Daemon) Cleanup(container *container.Container) { |
4586 logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err) |
4617 logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err) |
4587 } |
4618 } |
4588 } |
4619 } |
4589 + |
4620 + |
4590 +// injectHostConfig() should be abstracted away. |
4621 +// injectHostConfig() should be abstracted away. |
4628 + |
4659 + |
4629 + return err |
4660 + return err |
4630 +} |
4661 +} |
4631 diff --git a/daemon/stats_collector_solaris.go b/daemon/stats_collector_solaris.go |
4662 diff --git a/daemon/stats_collector_solaris.go b/daemon/stats_collector_solaris.go |
4632 new file mode 100644 |
4663 new file mode 100644 |
4633 index 0000000..9a51b27 |
4664 index 0000000..4d4b4a0 |
4634 --- /dev/null |
4665 --- /dev/null |
4635 +++ b/daemon/stats_collector_solaris.go |
4666 +++ b/daemon/stats_collector_solaris.go |
4636 @@ -0,0 +1,139 @@ |
4667 @@ -0,0 +1,537 @@ |
4637 +package daemon |
4668 +package daemon |
4638 + |
4669 + |
4639 +import ( |
4670 +import ( |
4640 + "bufio" |
4671 + "bufio" |
4641 + "sync" |
|
4642 + "time" |
|
4643 + "github.com/Sirupsen/logrus" |
4672 + "github.com/Sirupsen/logrus" |
4644 + "github.com/docker/docker/container" |
4673 + "github.com/docker/docker/container" |
4645 + "github.com/docker/docker/daemon/execdriver" |
4674 + "github.com/docker/docker/daemon/execdriver" |
4646 + "github.com/docker/docker/pkg/pubsub" |
4675 + "github.com/docker/docker/pkg/pubsub" |
4676 + "github.com/docker/docker/pkg/system" |
|
4677 + "github.com/docker/libnetwork/types" |
|
4678 + "github.com/opencontainers/runc/libcontainer" |
|
4679 + "github.com/opencontainers/runc/libcontainer/zones" |
|
4680 + "strings" |
|
4681 + "sync" |
|
4682 + "time" |
|
4647 +) |
4683 +) |
4684 + |
|
4685 +/* |
|
4686 + |
|
4687 +#cgo LDFLAGS: -lzonestat -lkstat2 |
|
4688 + |
|
4689 +#include <errno.h> |
|
4690 +#include <kstat2.h> |
|
4691 +#include <string.h> |
|
4692 +#include <strings.h> |
|
4693 +#include <stdlib.h> |
|
4694 +#include <zone.h> |
|
4695 +#include <zonestat.h> |
|
4696 +#include <sys/param.h> |
|
4697 + |
|
4698 +typedef struct z_iostat { |
|
4699 + int64_t nread; |
|
4700 + int64_t read_bytes; |
|
4701 + int64_t nwrite; |
|
4702 + int64_t write_bytes; |
|
4703 +} z_iostat_t; |
|
4704 + |
|
4705 +typedef struct z_netstat { |
|
4706 + int64_t ibytes; |
|
4707 + int64_t ipackets; |
|
4708 + int64_t ierrors; |
|
4709 + int64_t idrops; |
|
4710 + int64_t obytes; |
|
4711 + int64_t opackets; |
|
4712 + int64_t oerrors; |
|
4713 + int64_t odrops; |
|
4714 +} z_netstat_t; |
|
4715 + |
|
4716 +#define KSTAT2_IO_URI "kstat:/misc/unix/vopstats_zfs/%d" |
|
4717 +z_iostat_t * |
|
4718 +get_io_stats(zoneid_t zoneid) { |
|
4719 + kstat2_handle_t handle; |
|
4720 + kstat2_status_t stat; |
|
4721 + kstat2_map_t map; |
|
4722 + kstat2_mapiter_t iter; |
|
4723 + boolean_t has_next; |
|
4724 + z_iostat_t *zio = malloc(sizeof(*zio)); |
|
4725 + |
|
4726 + if (zio == NULL) { |
|
4727 + errno = ENOMEM; |
|
4728 + return (NULL); |
|
4729 + } |
|
4730 + char lookup_uri[sizeof(KSTAT2_IO_URI) + 10]; // 12 extra digits |
|
4731 + |
|
4732 + (void) snprintf(lookup_uri, sizeof(lookup_uri), KSTAT2_IO_URI, zoneid); |
|
4733 + |
|
4734 + stat = kstat2_open(&handle, NULL); |
|
4735 + if (stat != KSTAT2_S_OK) { |
|
4736 + return (NULL); |
|
4737 + } |
|
4738 + |
|
4739 + stat = kstat2_lookup_map(handle, lookup_uri, &map); |
|
4740 + if (stat != KSTAT2_S_OK) { |
|
4741 + (void) kstat2_close(&handle); |
|
4742 + return (NULL); |
|
4743 + } |
|
4744 + |
|
4745 + if ((stat = kstat2_mapiter_start(map, KSTAT2_NVK_ALL, &iter)) != |
|
4746 + KSTAT2_S_OK) { |
|
4747 + (void) kstat2_close(&handle); |
|
4748 + return (NULL); |
|
4749 + } |
|
4750 + |
|
4751 + while (kstat2_mapiter_hasnext(iter, &has_next) == KSTAT2_S_OK && |
|
4752 + has_next) { |
|
4753 + kstat2_nv_t val; |
|
4754 + |
|
4755 + if (kstat2_mapiter_next(iter, &val) != KSTAT2_S_OK) { |
|
4756 + continue; |
|
4757 + } |
|
4758 + |
|
4759 + if (!strcmp(val->name, "nread") && (val->type == KSTAT2_NVVT_INT)) |
|
4760 + zio->nread = val->data.integer; |
|
4761 + else if (!strcmp(val->name, "read_bytes") && (val->type == KSTAT2_NVVT_INT)) |
|
4762 + zio->read_bytes = val->data.integer; |
|
4763 + else if (!strcmp(val->name, "nwrite") && (val->type == KSTAT2_NVVT_INT)) |
|
4764 + zio->nwrite = val->data.integer; |
|
4765 + else if (!strcmp(val->name, "write_bytes") && (val->type == KSTAT2_NVVT_INT)) |
|
4766 + zio->write_bytes = val->data.integer; |
|
4767 + } |
|
4768 + |
|
4769 + (void) kstat2_close(&handle); |
|
4770 + return (zio); |
|
4771 +} |
|
4772 + |
|
4773 +void |
|
4774 +io_stats_free(z_iostat_t *p) |
|
4775 +{ |
|
4776 + free(p); |
|
4777 +} |
|
4778 + |
|
4779 +#define KSTAT2_NET_LINK_URI "kstat:/net/link" |
|
4780 + |
|
4781 +z_netstat_t * |
|
4782 +get_net_stats(const char *linkname, zoneid_t zoneid) { |
|
4783 + kstat2_handle_t handle; |
|
4784 + kstat2_status_t stat; |
|
4785 + kstat2_map_t map; |
|
4786 + kstat2_mapiter_t iter; |
|
4787 + boolean_t has_next; |
|
4788 + // URI prefix + '/' + max data link name + '/' + zoneid digits + '\0' |
|
4789 + char lookup_uri[sizeof(KSTAT2_NET_LINK_URI) + MAXLINKNAMELEN + 12]; |
|
4790 + z_netstat_t *zns = calloc(1, sizeof(*zns)); |
|
4791 + |
|
4792 + if (zns == NULL) { |
|
4793 + errno = ENOMEM; |
|
4794 + return (NULL); |
|
4795 + } |
|
4796 + |
|
4797 + (void) snprintf(lookup_uri, sizeof(lookup_uri), "%s/%s/%d", |
|
4798 + KSTAT2_NET_LINK_URI, linkname, zoneid); |
|
4799 + |
|
4800 + stat = kstat2_open(&handle, NULL); |
|
4801 + if (stat != KSTAT2_S_OK) { |
|
4802 + free(zns); |
|
4803 + return (NULL); |
|
4804 + } |
|
4805 + |
|
4806 + stat = kstat2_lookup_map(handle, lookup_uri, &map); |
|
4807 + if (stat != KSTAT2_S_OK) { |
|
4808 + (void) kstat2_close(&handle); |
|
4809 + free(zns); |
|
4810 + return (NULL); |
|
4811 + } |
|
4812 + |
|
4813 + stat = kstat2_mapiter_start(map, KSTAT2_NVK_ALL, &iter); |
|
4814 + if (stat != KSTAT2_S_OK) { |
|
4815 + (void) kstat2_close(&handle); |
|
4816 + free(zns); |
|
4817 + return (NULL); |
|
4818 + } |
|
4819 + |
|
4820 + while (kstat2_mapiter_hasnext(iter, &has_next) == KSTAT2_S_OK && |
|
4821 + has_next) { |
|
4822 + kstat2_nv_t val; |
|
4823 + |
|
4824 + if (kstat2_mapiter_next(iter, &val) != KSTAT2_S_OK || |
|
4825 + val->type != KSTAT2_NVVT_INT) { |
|
4826 + continue; |
|
4827 + } |
|
4828 + |
|
4829 + if (strcmp(val->name, "ipackets64") == 0) { |
|
4830 + zns->ipackets = val->data.integer; |
|
4831 + } else if (strcmp(val->name, "rbytes64") == 0) { |
|
4832 + zns->ibytes = val->data.integer; |
|
4833 + } else if (strcmp(val->name, "ierrors") == 0) { |
|
4834 + zns->ierrors = val->data.integer; |
|
4835 + } else if (strcmp(val->name, "dl_idrops") == 0) { |
|
4836 + zns->idrops = val->data.integer; |
|
4837 + } else if (strcmp(val->name, "opackets64") == 0) { |
|
4838 + zns->opackets = val->data.integer; |
|
4839 + } else if (strcmp(val->name, "obytes64") == 0) { |
|
4840 + zns->obytes = val->data.integer; |
|
4841 + } else if (strcmp(val->name, "oerrors") == 0) { |
|
4842 + zns->oerrors = val->data.integer; |
|
4843 + } else if (strcmp(val->name, "dl_odrops") == 0) { |
|
4844 + zns->odrops = val->data.integer; |
|
4845 + } |
|
4846 + } |
|
4847 + |
|
4848 + (void) kstat2_close(&handle); |
|
4849 + return (zns); |
|
4850 +} |
|
4851 + |
|
4852 +void |
|
4853 +net_stats_free(z_netstat_t *p) |
|
4854 +{ |
|
4855 + free(p); |
|
4856 +} |
|
4857 + |
|
4858 +uint64_t |
|
4859 +getSystemCPUUsage(zs_usage_t usage) |
|
4860 +{ |
|
4861 + timestruc_t cpu; |
|
4862 + |
|
4863 + zs_resource_total_time(usage, ZS_RESOURCE_CPU, &cpu); |
|
4864 + return (1000000000ULL * cpu.tv_sec + cpu.tv_nsec); |
|
4865 +} |
|
4866 + |
|
4867 +char * |
|
4868 +get_zone_name(zs_zone_t z) |
|
4869 +{ |
|
4870 + zs_property_t prop; |
|
4871 + |
|
4872 + prop = zs_zone_property(z, ZS_ZONE_PROP_NAME); |
|
4873 + if (prop == NULL) |
|
4874 + return (NULL); |
|
4875 + |
|
4876 + return (zs_property_string(prop)); |
|
4877 +} |
|
4878 + |
|
4879 +zs_usage_t |
|
4880 +get_usage(zs_ctl_t zsctl) |
|
4881 +{ |
|
4882 + zs_usage_t zsu; |
|
4883 + |
|
4884 + while ((zsu = zs_usage_read(zsctl)) == NULL && |
|
4885 + (errno == EINTR || errno == EAGAIN)) |
|
4886 + ; |
|
4887 + |
|
4888 + return (zsu); |
|
4889 +} |
|
4890 + |
|
4891 +uint64_t |
|
4892 +getCpuUsage(zs_zone_t z) |
|
4893 +{ |
|
4894 + timestruc_t cpu; |
|
4895 + |
|
4896 + zs_resource_used_zone_time(z, ZS_RESOURCE_CPU, &cpu); |
|
4897 + return ((uint64_t)(cpu.tv_sec * 1000000000ULL + cpu.tv_nsec)); |
|
4898 +} |
|
4899 + |
|
4900 +*/ |
|
4901 +import "C" |
|
4902 + |
|
4903 +func getZoneName(z C.zs_zone_t) string { |
|
4904 + return C.GoString(C.get_zone_name(z)) |
|
4905 +} |
|
4648 + |
4906 + |
4649 +// XXX solaris: TODO Copied from Windows, refactor accordingly for collector actions. |
4907 +// XXX solaris: TODO Copied from Windows, refactor accordingly for collector actions. |
4650 +// XXX: Copied statsCollector struct and interface from unix |
4908 +// XXX: Copied statsCollector struct and interface from unix |
4651 + |
4909 + |
4652 +type statsSupervisor interface { |
4910 +type statsSupervisor interface { |
4653 + // GetContainerStats collects all the stats related to a container |
4911 + // GetZoneStats collects all the stats related to a zone container |
4654 + GetContainerStats(container *container.Container) (*execdriver.ResourceStats, error) |
4912 + getZoneStats(z C.zs_zone_t, zoneid uint64, container *container.Container) (*execdriver.ResourceStats, error) |
4913 +} |
|
4914 + |
|
4915 +type zonePublisher struct { |
|
4916 + publisher *pubsub.Publisher |
|
4917 + zoneid uint64 |
|
4655 +} |
4918 +} |
4656 + |
4919 + |
4657 +// newStatsCollector returns a new statsCollector for collection stats |
4920 +// newStatsCollector returns a new statsCollector for collection stats |
4658 +// for a registered container at the specified interval. The collector allows |
4921 +// for a registered container at the specified interval. The collector allows |
4659 +// non-running containers to be added and will start processing stats when |
4922 +// non-running containers to be added and will start processing stats when |
4660 +// they are started. |
4923 +// they are started. |
4661 +func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector { |
4924 +func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector { |
4662 + s := &statsCollector{ |
4925 + s := &statsCollector{ |
4663 + interval: interval, |
4926 + interval: interval, |
4664 + supervisor: daemon, |
4927 + supervisor: daemon, |
4665 + publishers: make(map[*container.Container]*pubsub.Publisher), |
4928 + publishers: make(map[*container.Container]*zonePublisher), |
4666 + bufReader: bufio.NewReaderSize(nil, 128), |
4929 + bufReader: bufio.NewReaderSize(nil, 128), |
4667 + } |
4930 + } |
4668 + go s.run() |
4931 + go s.run() |
4669 + |
4932 + |
4670 + return s |
4933 + return s |
4671 +} |
4934 +} |
4672 + |
4935 + |
4673 +// statsCollector manages and provides container resource stats |
4936 +// statsCollector manages and provides container resource stats |
4674 +type statsCollector struct { |
4937 +type statsCollector struct { |
4675 + m sync.Mutex |
4938 + m sync.Mutex |
4676 + supervisor statsSupervisor |
4939 + supervisor statsSupervisor |
4677 + interval time.Duration |
4940 + interval time.Duration |
4678 + publishers map[*container.Container]*pubsub.Publisher |
4941 + publishers map[*container.Container]*zonePublisher |
4679 + bufReader *bufio.Reader |
4942 + bufReader *bufio.Reader |
4680 +} |
4943 +} |
4681 + |
4944 + |
4682 +// collect registers the container with the collector and adds it to |
4945 +// collect registers the container with the collector and adds it to |
4946 + |
|
4683 +// the event loop for collection on the specified interval returning |
4947 +// the event loop for collection on the specified interval returning |
4684 +// a channel for the subscriber to receive on. |
4948 +// a channel for the subscriber to receive on. |
4685 +func (s *statsCollector) collect(c *container.Container) chan interface{} { |
4949 +func (s *statsCollector) collect(c *container.Container) chan interface{} { |
4686 + s.m.Lock() |
4950 + s.m.Lock() |
4687 + defer s.m.Unlock() |
4951 + defer s.m.Unlock() |
4688 + publisher, exists := s.publishers[c] |
4952 + zpub, exists := s.publishers[c] |
4689 + if !exists { |
4953 + if !exists { |
4690 + publisher = pubsub.NewPublisher(100*time.Millisecond, 1024) |
4954 + zid, err := C.getzoneidbyname(C.CString(strings.TrimPrefix(c.Name, "/"))) |
4691 + s.publishers[c] = publisher |
4955 + if err != nil { |
4692 + } |
4956 + // Too early to get zoneid, flag and defer |
4693 + return publisher.Subscribe() |
4957 + zid = 0 |
4958 + } |
|
4959 + zpub = &zonePublisher{ |
|
4960 + publisher: pubsub.NewPublisher(100*time.Millisecond, 1024), |
|
4961 + zoneid: uint64(zid), |
|
4962 + } |
|
4963 + s.publishers[c] = zpub |
|
4964 + } |
|
4965 + return zpub.publisher.Subscribe() |
|
4694 +} |
4966 +} |
4695 + |
4967 + |
4696 +// stopCollection closes the channels for all subscribers and removes |
4968 +// stopCollection closes the channels for all subscribers and removes |
4697 +// the container from metrics collection. |
4969 +// the container from metrics collection. |
4698 +func (s *statsCollector) stopCollection(c *container.Container) { |
4970 +func (s *statsCollector) stopCollection(c *container.Container) { |
4699 + s.m.Lock() |
4971 + s.m.Lock() |
4700 + defer s.m.Unlock() |
4972 + defer s.m.Unlock() |
4701 + if publisher, exists := s.publishers[c]; exists { |
4973 + if zpub, exists := s.publishers[c]; exists { |
4702 + publisher.Close() |
4974 + zpub.publisher.Close() |
4703 + delete(s.publishers, c) |
4975 + delete(s.publishers, c) |
4704 + } |
4976 + } |
4705 +} |
4977 +} |
4706 + |
4978 + |
4707 +// unsubscribe removes a specific subscriber from receiving updates for a container's stats. |
4979 +// unsubscribe removes a specific subscriber from receiving updates for a container's stats. |
4708 +func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) { |
4980 +func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) { |
4709 + s.m.Lock() |
4981 + s.m.Lock() |
4710 + defer s.m.Unlock() |
4982 + defer s.m.Unlock() |
4711 + publisher := s.publishers[c] |
4983 + zpub, exists := s.publishers[c] |
4712 + if publisher != nil { |
4984 + if exists { |
4713 + publisher.Evict(ch) |
4985 + zpub.publisher.Evict(ch) |
4714 + if publisher.Len() == 0 { |
4986 + if zpub.publisher.Len() == 0 { |
4715 + delete(s.publishers, c) |
4987 + delete(s.publishers, c) |
4716 + } |
4988 + } |
4717 + } |
4989 + } |
4718 +} |
4990 +} |
4719 + |
4991 + |
4720 +// XXX copied from unix |
4992 +// XXX copied from unix |
4721 +func (s *statsCollector) run() { |
4993 +func (s *statsCollector) run() { |
4722 + type publishersPair struct { |
4994 + type publishersPair struct { |
4723 + container *container.Container |
4995 + container *container.Container |
4724 + publisher *pubsub.Publisher |
4996 + zpub *zonePublisher |
4725 + } |
4997 + } |
4998 + memoryInfo, err := system.ReadMemInfo() |
|
4999 + machineMemory := memoryInfo.MemTotal |
|
5000 + |
|
4726 + // we cannot determine the capacity here. |
5001 + // we cannot determine the capacity here. |
4727 + // it will grow enough in first iteration |
5002 + // it will grow enough in first iteration |
4728 + var pairs []publishersPair |
5003 + pairs := make(map[string]publishersPair) |
4729 + |
5004 + |
5005 + zsctl := C.zs_open() |
|
5006 + usage_last, err := C.get_usage(zsctl) |
|
5007 + if usage_last == nil { |
|
5008 + logrus.Error("Failed to get usage: ", err) |
|
5009 + C.zs_close(zsctl) |
|
5010 + return |
|
5011 + } |
|
4730 + for range time.Tick(s.interval) { |
5012 + for range time.Tick(s.interval) { |
4731 + // it does not make sense in the first iteration, |
5013 + var usage_diff C.zs_usage_t |
4732 + // but saves allocations in further iterations |
|
4733 + pairs = pairs[:0] |
|
4734 + |
5014 + |
4735 + s.m.Lock() |
5015 + s.m.Lock() |
4736 + for container, publisher := range s.publishers { |
5016 + for container, zpub := range s.publishers { |
4737 + // copy pointers here to release the lock ASAP |
5017 + // copy pointers here to release the lock ASAP |
4738 + pairs = append(pairs, publishersPair{container, publisher}) |
5018 + pairs[strings.TrimPrefix(container.Name, "/")] = publishersPair{container, zpub} |
4739 + } |
5019 + } |
4740 + s.m.Unlock() |
5020 + s.m.Unlock() |
4741 + if len(pairs) == 0 { |
5021 + if len(pairs) == 0 { |
4742 + continue |
5022 + continue |
4743 + } |
5023 + } |
4744 + |
5024 + |
4745 + // XXX: need to implement getSystmCPUUsage() |
5025 + usage, err := C.get_usage(zsctl) |
4746 + // XXX: ? whole system usage or how much of container allocated resources used? |
5026 + if usage != nil { |
4747 + // XXX: The output of docker stats seem broken as the CPU % column show changes |
5027 + usage_diff = C.zs_usage_diff(usage_last, usage) |
4748 + // from previous reading, instead of actual usage. |
5028 + } |
4749 + // Thats in api/client/stats.go calculateCPUUsage() |
5029 + if usage == nil || usage_diff == nil { |
4750 + // |
5030 + logrus.Error("Failed to get usage: ", err) |
4751 + systemUsage, err := s.getSystemCPUUsage() |
5031 + // empty the map |
4752 + if err != nil { |
5032 + for name, _ := range pairs { |
4753 + logrus.Errorf("collecting system cpu usage: %v", err) |
5033 + delete(pairs, name) |
5034 + } |
|
4754 + continue |
5035 + continue |
4755 + } |
5036 + } |
4756 + |
5037 + systemUsage := uint64(C.getSystemCPUUsage(usage_diff)) |
4757 + for _, pair := range pairs { |
5038 + |
4758 + stats, err := s.supervisor.GetContainerStats(pair.container) |
5039 + var z C.zs_zone_t |
5040 + z = nil |
|
5041 + for z = C.zs_zone_walk(usage_diff, z); z != nil; z = C.zs_zone_walk(usage_diff, z) { |
|
5042 + name := getZoneName(z) |
|
5043 + pair, ok := pairs[name] |
|
5044 + if ok == false { |
|
5045 + continue |
|
5046 + } |
|
5047 + zoneid := pair.zpub.zoneid |
|
5048 + if zoneid == 0 { |
|
5049 + zid, err := C.getzoneidbyname(C.CString(name)) |
|
5050 + if err != nil { |
|
5051 + // zoneid is not available yet |
|
5052 + delete(pairs, name) |
|
5053 + continue |
|
5054 + } |
|
5055 + pair.zpub.zoneid = uint64(zid) |
|
5056 + zoneid = pair.zpub.zoneid |
|
5057 + } |
|
5058 + stats, err := s.supervisor.getZoneStats(z, zoneid, pair.container) |
|
4759 + if err != nil { |
5059 + if err != nil { |
4760 + if err != execdriver.ErrNotRunning { |
5060 + if err != execdriver.ErrNotRunning { |
4761 + logrus.Errorf("collecting stats for %s: %v", pair.container.ID, err) |
5061 + logrus.Errorf("collecting stats for %s: %v", pair.container.ID, err) |
4762 + } |
5062 + } |
5063 + delete(pairs, name) |
|
4763 + continue |
5064 + continue |
4764 + } |
5065 + } |
5066 + if stats.MemoryLimit == 0 { |
|
5067 + stats.MemoryLimit = machineMemory |
|
5068 + } |
|
4765 + stats.SystemUsage = systemUsage |
5069 + stats.SystemUsage = systemUsage |
4766 + |
5070 + |
4767 + pair.publisher.Publish(stats) |
5071 + pair.zpub.publisher.Publish(stats) |
4768 + } |
5072 + delete(pairs, name) |
4769 + } |
5073 + } |
4770 +} |
5074 + C.zs_usage_free(usage_last) |
4771 + |
5075 + C.zs_usage_free(usage_diff) |
4772 +// XXX needs to be implemented. |
5076 + usage_last = usage |
4773 +func (s *statsCollector) getSystemCPUUsage() (uint64, error) { |
5077 + } |
4774 + return 0, nil |
5078 + C.zs_close(zsctl) |
5079 +} |
|
5080 + |
|
5081 +func getIOStats(zoneid uint64) (*zones.BlkioStats, error) { |
|
5082 + iostats, err := C.get_io_stats(C.zoneid_t(zoneid)) |
|
5083 + if iostats == nil { |
|
5084 + return nil, err |
|
5085 + } |
|
5086 + |
|
5087 + ioBytes := []zones.BlkioStatEntry{ |
|
5088 + { |
|
5089 + Op: "write", |
|
5090 + Value: uint64(iostats.write_bytes), |
|
5091 + }, |
|
5092 + { |
|
5093 + Op: "read", |
|
5094 + Value: uint64(iostats.read_bytes), |
|
5095 + }, |
|
5096 + } |
|
5097 + ioCount := []zones.BlkioStatEntry{ |
|
5098 + { |
|
5099 + Op: "write", |
|
5100 + Value: uint64(iostats.nwrite), |
|
5101 + }, |
|
5102 + { |
|
5103 + Op: "read", |
|
5104 + Value: uint64(iostats.nread), |
|
5105 + }, |
|
5106 + } |
|
5107 + C.io_stats_free(iostats) |
|
5108 + ioStats := zones.BlkioStats{ |
|
5109 + IoServiceBytesRecursive: ioBytes, |
|
5110 + IoServicedRecursive: ioCount, |
|
5111 + } |
|
5112 + |
|
5113 + return &ioStats, nil |
|
5114 +} |
|
5115 + |
|
5116 +func getNetStats(zoneid uint64) ([]*libcontainer.NetworkInterface, error) { |
|
5117 + m := make(map[string]*types.InterfaceStatistics) |
|
5118 + s := types.InterfaceStatistics{} |
|
5119 + |
|
5120 + // XXX: Should get an array with all data links in the container from its |
|
5121 + // configuration. For now, net0 suffices. If multi-homed containers are allowed |
|
5122 + // in the future, this will be required to have accuarte network statistics. |
|
5123 + datalink := []string{"net0"} |
|
5124 + for _, linkname := range datalink { |
|
5125 + ns, err := C.get_net_stats(C.CString(linkname), C.zoneid_t(zoneid)) |
|
5126 + if ns == nil { |
|
5127 + return nil, err |
|
5128 + } |
|
5129 + |
|
5130 + s.RxBytes = uint64(ns.ibytes) |
|
5131 + s.RxPackets = uint64(ns.ipackets) |
|
5132 + s.RxErrors = uint64(ns.ierrors) |
|
5133 + s.RxDropped = uint64(ns.idrops) |
|
5134 + |
|
5135 + s.TxBytes = uint64(ns.obytes) |
|
5136 + s.TxPackets = uint64(ns.opackets) |
|
5137 + s.TxErrors = uint64(ns.oerrors) |
|
5138 + s.TxDropped = uint64(ns.odrops) |
|
5139 + m[linkname] = &s |
|
5140 + C.net_stats_free(ns) |
|
5141 + |
|
5142 + } |
|
5143 + // Convert libnetwork nw stats into libcontainer nw stats |
|
5144 + var list []*libcontainer.NetworkInterface |
|
5145 + for ifName, ifStats := range m { |
|
5146 + list = append(list, convertLnNetworkStats(ifName, ifStats)) |
|
5147 + } |
|
5148 + |
|
5149 + return list, nil |
|
5150 +} |
|
5151 + |
|
5152 +func getContainerStats(z C.zs_zone_t, zoneid uint64) (*libcontainer.Stats, error) { |
|
5153 + cstats := &libcontainer.Stats{} |
|
5154 + zstats := zones.Stats{} |
|
5155 + iostats, err := getIOStats(zoneid) |
|
5156 + if iostats == nil { |
|
5157 + return nil, err |
|
5158 + } |
|
5159 + |
|
5160 + // For now we only retrieve what's needed by docker stats |
|
5161 + cpuUsage := uint64(C.getCpuUsage(z)) |
|
5162 + memUsage := uint64(C.zs_resource_used_zone_uint64(z, C.ZS_RESOURCE_RAM_RSS)) |
|
5163 + |
|
5164 + // XXX: Need to use kstat2 for CPU usage |
|
5165 + // CPU% only seems to work if all these fields are populated. |
|
5166 + // Once kstat2 are used, these statistics will be filled accurately. |
|
5167 + zstats.CpuStats.CpuUsage.PercpuUsage = []uint64{cpuUsage} |
|
5168 + zstats.CpuStats.CpuUsage.TotalUsage = uint64(cpuUsage) |
|
5169 + zstats.CpuStats.CpuUsage.UsageInKernelmode = uint64(cpuUsage) |
|
5170 + zstats.CpuStats.CpuUsage.UsageInUsermode = uint64(cpuUsage) |
|
5171 + zstats.MemoryStats.Usage.Usage = memUsage |
|
5172 + zstats.BlkioStats = *iostats |
|
5173 + |
|
5174 + cstats.Stats = &zstats |
|
5175 + |
|
5176 + return cstats, nil |
|
5177 +} |
|
5178 + |
|
5179 +func (daemon *Daemon) getZoneStats(z C.zs_zone_t, zoneid uint64, container *container.Container) (*execdriver.ResourceStats, error) { |
|
5180 + |
|
5181 + zstats, err := getContainerStats(z, zoneid) |
|
5182 + if zstats == nil { |
|
5183 + return nil, err |
|
5184 + } |
|
5185 + netstats, err := getNetStats(zoneid) |
|
5186 + if netstats == nil { |
|
5187 + return nil, err |
|
5188 + } |
|
5189 + |
|
5190 + memoryLimit := int64(C.zs_zone_limit_uint64(z, C.ZS_LIMIT_RAM_RSS)) |
|
5191 + |
|
5192 + if memoryLimit == C.ZS_LIMIT_NONE { |
|
5193 + memoryLimit = 0 |
|
5194 + } |
|
5195 + |
|
5196 + zstats.Interfaces = netstats |
|
5197 + stats := &execdriver.ResourceStats{ |
|
5198 + Read: time.Now(), |
|
5199 + MemoryLimit: memoryLimit, |
|
5200 + Stats: zstats, |
|
5201 + } |
|
5202 + |
|
5203 + return stats, nil |
|
4775 +} |
5204 +} |
4776 diff --git a/daemon/stats_collector_unix.go b/daemon/stats_collector_unix.go |
5205 diff --git a/daemon/stats_collector_unix.go b/daemon/stats_collector_unix.go |
4777 index 2fd368c..ec408c6 100644 |
5206 index 2fd368c..ec408c6 100644 |
4778 --- a/daemon/stats_collector_unix.go |
5207 --- a/daemon/stats_collector_unix.go |
4779 +++ b/daemon/stats_collector_unix.go |
5208 +++ b/daemon/stats_collector_unix.go |
4783 |
5212 |
4784 package daemon |
5213 package daemon |
4785 |
5214 |
4786 diff --git a/daemon/stats_solaris.go b/daemon/stats_solaris.go |
5215 diff --git a/daemon/stats_solaris.go b/daemon/stats_solaris.go |
4787 new file mode 100644 |
5216 new file mode 100644 |
4788 index 0000000..1d99f1f |
5217 index 0000000..a1230c6 |
4789 --- /dev/null |
5218 --- /dev/null |
4790 +++ b/daemon/stats_solaris.go |
5219 +++ b/daemon/stats_solaris.go |
4791 @@ -0,0 +1,82 @@ |
5220 @@ -0,0 +1,84 @@ |
4792 +package daemon |
5221 +package daemon |
4793 + |
5222 + |
4794 +import ( |
5223 +import ( |
4795 + "github.com/docker/engine-api/types" |
5224 + "github.com/docker/engine-api/types" |
4796 + "github.com/opencontainers/runc/libcontainer" |
5225 + "github.com/opencontainers/runc/libcontainer" |
4820 + TxDropped: iface.TxDropped, |
5249 + TxDropped: iface.TxDropped, |
4821 + } |
5250 + } |
4822 + } |
5251 + } |
4823 + } |
5252 + } |
4824 + |
5253 + |
4825 + if cs != nil { |
5254 + if cs == nil { |
4826 + s.BlkioStats = types.BlkioStats{ |
5255 + return s |
4827 + IoServiceBytesRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceBytesRecursive), |
5256 + } |
4828 + IoServicedRecursive: copyBlkioEntry(cs.BlkioStats.IoServicedRecursive), |
5257 + |
4829 + IoQueuedRecursive: copyBlkioEntry(cs.BlkioStats.IoQueuedRecursive), |
5258 + s.BlkioStats = types.BlkioStats{ |
4830 + IoServiceTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceTimeRecursive), |
5259 + IoServiceBytesRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceBytesRecursive), |
4831 + IoWaitTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoWaitTimeRecursive), |
5260 + IoServicedRecursive: copyBlkioEntry(cs.BlkioStats.IoServicedRecursive), |
4832 + IoMergedRecursive: copyBlkioEntry(cs.BlkioStats.IoMergedRecursive), |
5261 + IoQueuedRecursive: copyBlkioEntry(cs.BlkioStats.IoQueuedRecursive), |
4833 + IoTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoTimeRecursive), |
5262 + IoServiceTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceTimeRecursive), |
4834 + SectorsRecursive: copyBlkioEntry(cs.BlkioStats.SectorsRecursive), |
5263 + IoWaitTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoWaitTimeRecursive), |
4835 + } |
5264 + IoMergedRecursive: copyBlkioEntry(cs.BlkioStats.IoMergedRecursive), |
4836 + cpu := cs.CpuStats |
5265 + IoTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoTimeRecursive), |
4837 + s.CPUStats = types.CPUStats{ |
5266 + SectorsRecursive: copyBlkioEntry(cs.BlkioStats.SectorsRecursive), |
4838 + CPUUsage: types.CPUUsage{ |
5267 + } |
4839 + TotalUsage: cpu.CpuUsage.TotalUsage, |
5268 + cpu := cs.CpuStats |
4840 + PercpuUsage: cpu.CpuUsage.PercpuUsage, |
5269 + s.CPUStats = types.CPUStats{ |
4841 + UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, |
5270 + CPUUsage: types.CPUUsage{ |
4842 + UsageInUsermode: cpu.CpuUsage.UsageInUsermode, |
5271 + TotalUsage: cpu.CpuUsage.TotalUsage, |
4843 + }, |
5272 + PercpuUsage: cpu.CpuUsage.PercpuUsage, |
4844 + ThrottlingData: types.ThrottlingData{ |
5273 + UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, |
4845 + Periods: cpu.ThrottlingData.Periods, |
5274 + UsageInUsermode: cpu.CpuUsage.UsageInUsermode, |
4846 + ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, |
5275 + }, |
4847 + ThrottledTime: cpu.ThrottlingData.ThrottledTime, |
5276 + ThrottlingData: types.ThrottlingData{ |
4848 + }, |
5277 + Periods: cpu.ThrottlingData.Periods, |
4849 + } |
5278 + ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, |
4850 + mem := cs.MemoryStats |
5279 + ThrottledTime: cpu.ThrottlingData.ThrottledTime, |
4851 + s.MemoryStats = types.MemoryStats{ |
5280 + }, |
4852 + Usage: mem.Usage.Usage, |
5281 + } |
4853 + MaxUsage: mem.Usage.MaxUsage, |
5282 + mem := cs.MemoryStats |
4854 + Stats: mem.Stats, |
5283 + s.MemoryStats = types.MemoryStats{ |
4855 + Failcnt: mem.Usage.Failcnt, |
5284 + Usage: mem.Usage.Usage, |
4856 + } |
5285 + MaxUsage: mem.Usage.MaxUsage, |
5286 + Stats: mem.Stats, |
|
5287 + Failcnt: mem.Usage.Failcnt, |
|
4857 + } |
5288 + } |
4858 + |
5289 + |
4859 + return s |
5290 + return s |
4860 +} |
5291 +} |
4861 + |
5292 + |
8297 + } |
8728 + } |
8298 + return strconv.FormatInt(int64(nlgrps), 16) |
8729 + return strconv.FormatInt(int64(nlgrps), 16) |
8299 +} |
8730 +} |
8300 diff --git a/pkg/system/meminfo_solaris.go b/pkg/system/meminfo_solaris.go |
8731 diff --git a/pkg/system/meminfo_solaris.go b/pkg/system/meminfo_solaris.go |
8301 new file mode 100644 |
8732 new file mode 100644 |
8302 index 0000000..f8c0af3 |
8733 index 0000000..bad227f |
8303 --- /dev/null |
8734 --- /dev/null |
8304 +++ b/pkg/system/meminfo_solaris.go |
8735 +++ b/pkg/system/meminfo_solaris.go |
8305 @@ -0,0 +1,133 @@ |
8736 @@ -0,0 +1,127 @@ |
8306 +// +build solaris,cgo |
8737 +// +build solaris,cgo |
8307 + |
8738 + |
8308 +package system |
8739 +package system |
8309 + |
8740 + |
8310 +import ( |
8741 +import ( |
8376 + pagesize := C.sysconf(C._SC_PAGESIZE) |
8807 + pagesize := C.sysconf(C._SC_PAGESIZE) |
8377 + npages := C.sysconf(C._SC_PHYS_PAGES) |
8808 + npages := C.sysconf(C._SC_PHYS_PAGES) |
8378 + return int64(pagesize * npages) |
8809 + return int64(pagesize * npages) |
8379 +} |
8810 +} |
8380 + |
8811 + |
8381 +func getFreeMem() int64 { |
|
8382 + pagesize := C.sysconf(C._SC_PAGESIZE) |
|
8383 + npages := C.sysconf(C._SC_AVPHYS_PAGES) |
|
8384 + return int64(pagesize * npages) |
|
8385 +} |
|
8386 + |
|
8387 +// ReadMemInfo retrieves memory statistics of the host system and returns a |
8812 +// ReadMemInfo retrieves memory statistics of the host system and returns a |
8388 +// MemInfo type. |
8813 +// MemInfo type. |
8389 +func ReadMemInfo() (*MemInfo, error) { |
8814 +func ReadMemInfo() (*MemInfo, error) { |
8390 + |
8815 + |
8391 + ppKernel := C.getPpKernel() |
8816 + ppKernel := C.getPpKernel() |
8392 + MemTotal := getTotalMem() |
8817 + MemTotal := getTotalMem() |
8393 + MemFree := getFreeMem() |
8818 + MemFree := MemTotal - int64(ppKernel) |
8394 + SwapTotal, SwapFree, err := getSysSwap() |
8819 + SwapTotal, SwapFree, err := getSysSwap() |
8395 + |
8820 + |
8396 + if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 || |
8821 + if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 || |
8397 + SwapFree < 0 { |
8822 + SwapFree < 0 { |
8398 + return nil, fmt.Errorf("Error getting system memory info %v\n", err) |
8823 + return nil, fmt.Errorf("Error getting system memory info %v\n", err) |
8399 + } |
8824 + } |
8400 + |
8825 + |
8401 + meminfo := &MemInfo{} |
8826 + meminfo := &MemInfo{} |
8402 + // Total memory is total physical memory less than memory locked by kernel |
8827 + // Total memory is total physical memory less than memory locked by kernel |
8403 + meminfo.MemTotal = MemTotal - int64(ppKernel) |
8828 + meminfo.MemTotal = MemTotal |
8404 + meminfo.MemFree = MemFree |
8829 + meminfo.MemFree = MemFree |
8405 + meminfo.SwapTotal = SwapTotal |
8830 + meminfo.SwapTotal = SwapTotal |
8406 + meminfo.SwapFree = SwapFree |
8831 + meminfo.SwapFree = SwapFree |
8407 + |
8832 + |
8408 + return meminfo, nil |
8833 + return meminfo, nil |
8954 |
9379 |
8955 // Contains container's resources (cgroups, ulimits) |
9380 // Contains container's resources (cgroups, ulimits) |
8956 Resources |
9381 Resources |
8957 + LimitPriv string // Comma separated list of privileges to limit container to |
9382 + LimitPriv string // Comma separated list of privileges to limit container to |
8958 } |
9383 } |
9384 diff --git a/vendor/src/github.com/docker/engine-api/types/types.go b/vendor/src/github.com/docker/engine-api/types/types.go |
|
9385 index 64c9981..d42395e 100644 |
|
9386 --- a/vendor/src/github.com/docker/engine-api/types/types.go |
|
9387 +++ b/vendor/src/github.com/docker/engine-api/types/types.go |
|
9388 @@ -178,13 +178,11 @@ type ContainerProcessList struct { |
|
9389 type Version struct { |
|
9390 Version string |
|
9391 APIVersion string `json:"ApiVersion"` |
|
9392 - GitCommit string |
|
9393 GoVersion string |
|
9394 Os string |
|
9395 Arch string |
|
9396 KernelVersion string `json:",omitempty"` |
|
9397 Experimental bool `json:",omitempty"` |
|
9398 - BuildTime string `json:",omitempty"` |
|
9399 } |
|
9400 |
|
9401 // Info contains response of Remote API: |
|
8959 diff --git a/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go b/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go |
9402 diff --git a/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go b/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go |
8960 index c10aced..d162734 100644 |
9403 index c10aced..d162734 100644 |
8961 --- a/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go |
9404 --- a/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go |
8962 +++ b/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go |
9405 +++ b/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go |
8963 @@ -1,4 +1,4 @@ |
9406 @@ -1,4 +1,4 @@ |
8979 +func (c *controller) createGWNetwork() (Network, error) { |
9422 +func (c *controller) createGWNetwork() (Network, error) { |
8980 + return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in solaris") |
9423 + return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in solaris") |
8981 +} |
9424 +} |
8982 diff --git a/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go |
9425 diff --git a/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go |
8983 new file mode 100644 |
9426 new file mode 100644 |
8984 index 0000000..960f8ea |
9427 index 0000000..12c8b69 |
8985 --- /dev/null |
9428 --- /dev/null |
8986 +++ b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go |
9429 +++ b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go |
8987 @@ -0,0 +1,1062 @@ |
9430 @@ -0,0 +1,1076 @@ |
8988 +package bridge |
9431 +package bridge |
8989 + |
9432 + |
8990 +import ( |
9433 +import ( |
8991 + "bufio" |
9434 + "bufio" |
8992 + "errors" |
9435 + "errors" |
9416 + |
9859 + |
9417 + fmt.Println("Creating bridge network:", config.ID[:12], |
9860 + fmt.Println("Creating bridge network:", config.ID[:12], |
9418 + config.BridgeName, config.AddressIPv4) |
9861 + config.BridgeName, config.AddressIPv4) |
9419 + |
9862 + |
9420 + networkList := d.getNetworks() |
9863 + networkList := d.getNetworks() |
9421 + for _, nw := range networkList { |
9864 + for i, nw := range networkList { |
9422 + nw.Lock() |
9865 + nw.Lock() |
9423 + nwConfig := nw.config |
9866 + nwConfig := nw.config |
9424 + nw.Unlock() |
9867 + nw.Unlock() |
9425 + if err := nwConfig.Conflicts(config); err != nil { |
9868 + if err := nwConfig.Conflicts(config); err != nil { |
9426 + return types.ForbiddenErrorf( |
9869 + if config.DefaultBridge { |
9427 + "cannot create network %s (%s): "+ |
9870 + // We encountered and identified a stale default network |
9428 + "conflicts with network %s (%s): %s", |
9871 + // We must delete it as libnetwork is the source of thruth |
9429 + nwConfig.BridgeName, config.ID, nw.id, |
9872 + // The default network being created must be the only one |
9430 + nw.config.BridgeName, err.Error()) |
9873 + // This can happen only from docker 1.12 on ward |
9874 + logrus.Infof("Removing stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName) |
|
9875 + if err := d.DeleteNetwork(nwConfig.ID); err != nil { |
|
9876 + logrus.Warnf("Failed to remove stale default network: %s (%s): %v. Will remove from store.", nwConfig.ID, nwConfig.BridgeName, err) |
|
9877 + d.storeDelete(nwConfig) |
|
9878 + } |
|
9879 + networkList = append(networkList[:i], networkList[i+1:]...) |
|
9880 + } else { |
|
9881 + return types.ForbiddenErrorf( |
|
9882 + "cannot create network %s (%s): "+ |
|
9883 + "conflicts with network %s (%s): %s", |
|
9884 + nwConfig.BridgeName, config.ID, nw.id, |
|
9885 + nw.config.BridgeName, err.Error()) |
|
9886 + } |
|
9431 + } |
9887 + } |
9432 + } |
9888 + } |
9433 + if config.DefaultBindingIP == nil || |
9889 + if config.DefaultBindingIP == nil || |
9434 + config.DefaultBindingIP.IsUnspecified() { |
9890 + config.DefaultBindingIP.IsUnspecified() { |
9435 + config.DefaultBindingIP = d.defrouteIP |
9891 + config.DefaultBindingIP = d.defrouteIP |
9592 + endpoint.addrv6 = ifInfo.AddressIPv6() |
10048 + endpoint.addrv6 = ifInfo.AddressIPv6() |
9593 + c := n.config |
10049 + c := n.config |
9594 + |
10050 + |
9595 + // Program any required port mapping and store them in the endpoint |
10051 + // Program any required port mapping and store them in the endpoint |
9596 + endpoint.portMapping, err = n.allocatePorts(epConfig, |
10052 + endpoint.portMapping, err = n.allocatePorts(epConfig, |
9597 + endpoint, c.DefaultBindingIntf, c.DefaultBindingIP) |
10053 + endpoint, c.DefaultBindingIntf, c.DefaultBindingIP, |
10054 + c.BridgeName+"_gw0") |
|
9598 + if err != nil { |
10055 + if err != nil { |
9599 + return err |
10056 + return err |
9600 + } |
10057 + } |
9601 + |
10058 + |
9602 + return nil |
10059 + return nil |
10612 + |
11069 + |
10613 +// BadRequest denotes the type of this error |
11070 +// BadRequest denotes the type of this error |
10614 +func (address InvalidLinkIPAddrError) BadRequest() {} |
11071 +func (address InvalidLinkIPAddrError) BadRequest() {} |
10615 diff --git a/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go |
11072 diff --git a/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go |
10616 new file mode 100644 |
11073 new file mode 100644 |
10617 index 0000000..3fae520 |
11074 index 0000000..f2b1fd5 |
10618 --- /dev/null |
11075 --- /dev/null |
10619 +++ b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go |
11076 +++ b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go |
10620 @@ -0,0 +1,199 @@ |
11077 @@ -0,0 +1,218 @@ |
10621 +package bridge |
11078 +package bridge |
10622 + |
11079 + |
10623 +import ( |
11080 +import ( |
10624 + "bytes" |
11081 + "bytes" |
10625 + "errors" |
11082 + "errors" |
10638 + |
11095 + |
10639 +const ( |
11096 +const ( |
10640 + maxAllocatePortAttempts = 10 |
11097 + maxAllocatePortAttempts = 10 |
10641 +) |
11098 +) |
10642 + |
11099 + |
10643 +func addPFRules(epid, bindIntf string, bs []types.PortBinding) { |
11100 +func addPFRules(epid, bindIntf string, bs []types.PortBinding, |
11101 + gwIntf string) { |
|
10644 + id := epid[:12] |
11102 + id := epid[:12] |
10645 + fname := "/var/lib/docker/network/files/pf." + id |
11103 + fname := "/var/lib/docker/network/files/pf." + id |
10646 + |
11104 + |
10647 + f, err := os.OpenFile(fname, |
11105 + f, err := os.OpenFile(fname, |
10648 + os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) |
11106 + os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) |
10658 + b.IP.String(), b.Port) |
11116 + b.IP.String(), b.Port) |
10659 + _, err = f.WriteString(r) |
11117 + _, err = f.WriteString(r) |
10660 + if err != nil { |
11118 + if err != nil { |
10661 + logrus.Warnf("cannot write to %s: %v", fname, err) |
11119 + logrus.Warnf("cannot write to %s: %v", fname, err) |
10662 + } |
11120 + } |
11121 + r = fmt.Sprintf( |
|
11122 + "pass out inet proto %s from (%s) to (%s) port %d " + |
|
11123 + "rdr-to %s port %d route-to %s" + "@" + "%s\n", |
|
11124 + b.Proto.String(), bindIntf, bindIntf, b.HostPort, |
|
11125 + b.IP.String(), b.Port, b.IP.String(), gwIntf) |
|
11126 + _, err = f.WriteString(r) |
|
11127 + if err != nil { |
|
11128 + logrus.Warnf("cannot write to %s: %v", fname, err) |
|
11129 + } |
|
11130 + r = fmt.Sprintf( |
|
11131 + "pass out on %s inet proto %s from (%s) to %s " + |
|
11132 + "port %d reply-to %s\n", gwIntf, |
|
11133 + b.Proto.String(), bindIntf, b.IP.String(), b.Port, |
|
11134 + bindIntf) |
|
11135 + _, err = f.WriteString(r) |
|
11136 + if err != nil { |
|
11137 + logrus.Warnf("cannot write to %s: %v", fname, err) |
|
11138 + } |
|
10663 + } |
11139 + } |
10664 + f.Close() |
11140 + f.Close() |
10665 + |
11141 + |
10666 + anchor := fmt.Sprintf("_auto/docker/ep%s", id) |
11142 + anchor := fmt.Sprintf("_auto/docker/ep%s", id) |
10667 + err = exec.Command("/usr/sbin/pfctl", "-a", anchor, "-f", fname).Run() |
11143 + err = exec.Command("/usr/sbin/pfctl", "-a", anchor, "-f", fname).Run() |
10677 + if err != nil { |
11153 + if err != nil { |
10678 + logrus.Warnf("pfctl -F failed: %v", err) |
11154 + logrus.Warnf("pfctl -F failed: %v", err) |
10679 + } |
11155 + } |
10680 +} |
11156 +} |
10681 + |
11157 + |
10682 +func (n *bridgeNetwork) allocatePorts(epc *endpointConfiguration, ep *bridgeEndpoint, bindIntf string, reqDefBindIP net.IP) ([]types.PortBinding, error) { |
11158 +func (n *bridgeNetwork) allocatePorts(epc *endpointConfiguration, ep *bridgeEndpoint, bindIntf string, reqDefBindIP net.IP, gwIntf string) ([]types.PortBinding, error) { |
10683 + if epc == nil || epc.PortBindings == nil || len(epc.PortBindings) == 0 { |
11159 + if epc == nil || epc.PortBindings == nil || len(epc.PortBindings) == 0 { |
10684 + return nil, nil |
11160 + return nil, nil |
10685 + } |
11161 + } |
10686 + |
11162 + |
10687 + defHostIP := defaultBindingIP |
11163 + defHostIP := defaultBindingIP |
10692 + bs, err := n.allocatePortsInternal(epc.PortBindings, |
11168 + bs, err := n.allocatePortsInternal(epc.PortBindings, |
10693 + bindIntf, ep.addr.IP, defHostIP) |
11169 + bindIntf, ep.addr.IP, defHostIP) |
10694 + if err != nil { |
11170 + if err != nil { |
10695 + return nil, err |
11171 + return nil, err |
10696 + } |
11172 + } |
10697 + addPFRules(ep.id, bindIntf, bs) |
11173 + addPFRules(ep.id, bindIntf, bs, gwIntf) |
10698 + return bs, err |
11174 + return bs, err |
10699 +} |
11175 +} |
10700 + |
11176 + |
10701 +func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, bindIntf string, containerIP, defHostIP net.IP) ([]types.PortBinding, error) { |
11177 +func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, bindIntf string, containerIP, defHostIP net.IP) ([]types.PortBinding, error) { |
10702 + bs := make([]types.PortBinding, 0, len(bindings)) |
11178 + bs := make([]types.PortBinding, 0, len(bindings)) |
12233 + if p.listener != nil { |
12709 + if p.listener != nil { |
12234 + return p.listener.Close() |
12710 + return p.listener.Close() |
12235 + } |
12711 + } |
12236 + return nil |
12712 + return nil |
12237 +} |
12713 +} |
12714 diff --git a/vendor/src/github.com/docker/libnetwork/sandbox.go b/vendor/src/github.com/docker/libnetwork/sandbox.go |
|
12715 index 71c8ebb..4853ee4 100644 |
|
12716 --- a/vendor/src/github.com/docker/libnetwork/sandbox.go |
|
12717 +++ b/vendor/src/github.com/docker/libnetwork/sandbox.go |
|
12718 @@ -148,12 +148,15 @@ func (sb *sandbox) Labels() map[string]interface{} { |
|
12719 func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { |
|
12720 m := make(map[string]*types.InterfaceStatistics) |
|
12721 |
|
12722 - if sb.osSbox == nil { |
|
12723 + sb.Lock() |
|
12724 + osb := sb.osSbox |
|
12725 + sb.Unlock() |
|
12726 + if osb == nil { |
|
12727 return m, nil |
|
12728 } |
|
12729 |
|
12730 var err error |
|
12731 - for _, i := range sb.osSbox.Info().Interfaces() { |
|
12732 + for _, i := range osb.Info().Interfaces() { |
|
12733 if m[i.DstName()], err = i.Statistics(); err != nil { |
|
12734 return m, err |
|
12735 } |
|
12238 diff --git a/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go b/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go |
12736 diff --git a/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go b/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go |
12239 new file mode 100644 |
12737 new file mode 100644 |
12240 index 0000000..7569e46 |
12738 index 0000000..7569e46 |
12241 --- /dev/null |
12739 --- /dev/null |
12242 +++ b/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go |
12740 +++ b/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go |
13257 +func NewConsole(uid, gid int) (Console, error) { |
13755 +func NewConsole(uid, gid int) (Console, error) { |
13258 + return nil, errors.New("libcontainer console is not supported on Solaris") |
13756 + return nil, errors.New("libcontainer console is not supported on Solaris") |
13259 +} |
13757 +} |
13260 diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go b/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go |
13758 diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go b/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go |
13261 new file mode 100644 |
13759 new file mode 100644 |
13262 index 0000000..27639a1 |
13760 index 0000000..503c339 |
13263 --- /dev/null |
13761 --- /dev/null |
13264 +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go |
13762 +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go |
13265 @@ -0,0 +1,103 @@ |
13763 @@ -0,0 +1,22 @@ |
13266 +// +build solaris |
13764 +// +build solaris |
13267 + |
13765 + |
13268 +package libcontainer |
13766 +package libcontainer |
13269 + |
|
13270 +import ( |
|
13271 + "github.com/opencontainers/runc/libcontainer/zones" |
|
13272 +) |
|
13273 + |
13767 + |
13274 +// State represents a running container's state |
13768 +// State represents a running container's state |
13275 +type State struct { |
13769 +type State struct { |
13276 + BaseState |
13770 + BaseState |
13277 + |
13771 + |
13286 +type Container interface { |
13780 +type Container interface { |
13287 + BaseContainer |
13781 + BaseContainer |
13288 + |
13782 + |
13289 + // Methods below here are platform specific |
13783 + // Methods below here are platform specific |
13290 + |
13784 + |
13291 +} |
|
13292 + |
|
13293 +// XXX: Should collect the networking stats too. |
|
13294 +// For the stubs, need config with slice of NICs. |
|
13295 +func GetStats(id string) (*Stats, error) { |
|
13296 + stats := &Stats{} |
|
13297 + zstats := zones.Stats{} |
|
13298 + cpuUsage := zones.CpuUsage { |
|
13299 + TotalUsage: 5, |
|
13300 + /* XXX: currently only TotalUsage is consumed. |
|
13301 + PercpuUsage: []uint64 { 1, 2, 3 }, |
|
13302 + UsageInKernelmode: 3, |
|
13303 + UsageInUsermode: 2, |
|
13304 + */ |
|
13305 + } |
|
13306 + cpuStats := zones.CpuStats { |
|
13307 + CpuUsage: cpuUsage, |
|
13308 + /* XXX: currently only CpuUsage.TotalUsage is consumed. |
|
13309 + ThrottlingData: ThrottlingData { |
|
13310 + Periods: 100, |
|
13311 + ThrottledPeriods: 5, |
|
13312 + ThrottledTime: 1, |
|
13313 + }, |
|
13314 + */ |
|
13315 + } |
|
13316 + memoryStats := zones.MemoryStats { |
|
13317 + Cache: 65536, |
|
13318 + Usage: zones.MemoryData { |
|
13319 + Usage: 32000000, |
|
13320 + MaxUsage: 64000000, |
|
13321 + Failcnt: 10, |
|
13322 + }, |
|
13323 + /* XXX: currently only MemboriStats.Usage is consumed |
|
13324 + SwapUsage: MemoryData { |
|
13325 + Usage: 8192000, |
|
13326 + MaxUsage: 8192000, |
|
13327 + Failcnt: 128, |
|
13328 + }, |
|
13329 + KernelUsage: MemoryData { |
|
13330 + Usage: 4096000, |
|
13331 + MaxUsage: 2048000, |
|
13332 + Failcnt: 0, |
|
13333 + }, |
|
13334 + */ |
|
13335 + } |
|
13336 + blkioStats := zones.BlkioStats { |
|
13337 + IoServiceBytesRecursive: []zones.BlkioStatEntry { |
|
13338 + { |
|
13339 + Major: 14, |
|
13340 + Minor: 1, |
|
13341 + Op: "read", //op name from api/client/stats.go |
|
13342 + Value: 9000000, |
|
13343 + }, |
|
13344 + { |
|
13345 + Major: 13, |
|
13346 + Minor: 0, |
|
13347 + Op: "write", //op name from api/client/stats.go |
|
13348 + Value: 500000, |
|
13349 + }, |
|
13350 + }, |
|
13351 + /* XXX: currently only IoServiceBytesRecursive is consumed |
|
13352 + IoServicedRecursive: []BlkioStatEntry { |
|
13353 + { |
|
13354 + Major: 14000000, |
|
13355 + Minor: 10000000, |
|
13356 + Op: "", |
|
13357 + Value: 9000000, |
|
13358 + }, |
|
13359 + }, |
|
13360 + */ |
|
13361 + } |
|
13362 + zstats.CpuStats = cpuStats |
|
13363 + zstats.MemoryStats = memoryStats |
|
13364 + zstats.BlkioStats = blkioStats |
|
13365 + |
|
13366 + stats.Stats = &zstats |
|
13367 + return stats, nil |
|
13368 +} |
13785 +} |
13369 diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/stats_solaris.go b/vendor/src/github.com/opencontainers/runc/libcontainer/stats_solaris.go |
13786 diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/stats_solaris.go b/vendor/src/github.com/opencontainers/runc/libcontainer/stats_solaris.go |
13370 new file mode 100644 |
13787 new file mode 100644 |
13371 index 0000000..7353cd8 |
13788 index 0000000..7353cd8 |
13372 --- /dev/null |
13789 --- /dev/null |