1// Copyright 2018 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package procfs 15 16import ( 17 "fmt" 18 "reflect" 19 "strings" 20 "testing" 21 "time" 22) 23 24func TestMountStats(t *testing.T) { 25 tests := []struct { 26 name string 27 s string 28 mounts []*Mount 29 invalid bool 30 }{ 31 { 32 name: "no devices", 33 s: `hello`, 34 }, 35 { 36 name: "device has too few fields", 37 s: `device foo`, 38 invalid: true, 39 }, 40 { 41 name: "device incorrect format", 42 s: `device rootfs BAD on / with fstype rootfs`, 43 invalid: true, 44 }, 45 { 46 name: "device incorrect format", 47 s: `device rootfs mounted BAD / with fstype rootfs`, 48 invalid: true, 49 }, 50 { 51 name: "device incorrect format", 52 s: `device rootfs mounted on / BAD fstype rootfs`, 53 invalid: true, 54 }, 55 { 56 name: "device incorrect format", 57 s: `device rootfs mounted on / with BAD rootfs`, 58 invalid: true, 59 }, 60 { 61 name: "device rootfs cannot have stats", 62 s: `device rootfs mounted on / with fstype rootfs stats`, 63 invalid: true, 64 }, 65 { 66 name: "NFSv4 device with too little info", 67 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nhello", 68 invalid: true, 69 }, 70 { 71 name: "NFSv4 device with bad bytes", 72 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nbytes: 0", 73 invalid: true, 74 }, 75 { 76 name: "NFSv4 device with bad events", 77 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nevents: 0", 78 invalid: true, 79 }, 80 { 81 name: "NFSv4 device with bad per-op stats", 82 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nper-op statistics\nFOO 0", 83 invalid: true, 84 }, 85 { 86 name: "NFSv4 device with bad transport stats", 87 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nxprt: tcp", 88 invalid: true, 89 }, 90 { 91 name: "NFSv4 device with bad transport version", 92 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=foo\nxprt: tcp 0", 93 invalid: true, 94 }, 95 { 96 name: "NFSv4 device with bad transport stats version 1.0", 97 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.0\nxprt: tcp 0 0 0 0 0 0 0 0 0 0 0 0 0", 98 invalid: true, 99 }, 100 { 101 name: "NFSv4 device with bad transport stats version 1.1", 102 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nxprt: tcp 0 0 0 0 0 0 0 0 0 0", 103 invalid: true, 104 }, 105 { 106 name: "NFSv3 device with bad transport protocol", 107 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nxprt: tcpx 0 0 0 0 0 0 0 0 0 0", 108 invalid: true, 109 }, 110 { 111 name: "NFSv3 device using TCP with transport stats version 1.0 OK", 112 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.0\nxprt: tcp 1 2 3 4 5 6 7 8 9 10", 113 mounts: []*Mount{{ 114 Device: "192.168.1.1:/srv", 115 Mount: "/mnt/nfs", 116 Type: "nfs", 117 Stats: &MountStatsNFS{ 118 StatVersion: "1.0", 119 Transport: NFSTransportStats{ 120 Protocol: "tcp", 121 Port: 1, 122 Bind: 2, 123 Connect: 3, 124 ConnectIdleTime: 4, 125 IdleTimeSeconds: 5, 126 Sends: 6, 127 Receives: 7, 128 BadTransactionIDs: 8, 129 CumulativeActiveRequests: 9, 130 CumulativeBacklog: 10, 131 MaximumRPCSlotsUsed: 0, // these three are not 132 CumulativeSendingQueue: 0, // present in statvers=1.0 133 CumulativePendingQueue: 0, // 134 }, 135 }, 136 }}, 137 }, 138 { 139 name: "NFSv3 device using UDP with transport stats version 1.0 OK", 140 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.0\nxprt: udp 1 2 3 4 5 6 7", 141 mounts: []*Mount{{ 142 Device: "192.168.1.1:/srv", 143 Mount: "/mnt/nfs", 144 Type: "nfs", 145 Stats: &MountStatsNFS{ 146 StatVersion: "1.0", 147 Transport: NFSTransportStats{ 148 Protocol: "udp", 149 Port: 1, 150 Bind: 2, 151 Connect: 0, 152 ConnectIdleTime: 0, 153 IdleTimeSeconds: 0, 154 Sends: 3, 155 Receives: 4, 156 BadTransactionIDs: 5, 157 CumulativeActiveRequests: 6, 158 CumulativeBacklog: 7, 159 MaximumRPCSlotsUsed: 0, // these three are not 160 CumulativeSendingQueue: 0, // present in statvers=1.0 161 CumulativePendingQueue: 0, // 162 }, 163 }, 164 }}, 165 }, 166 { 167 name: "NFSv3 device using TCP with transport stats version 1.1 OK", 168 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1\nxprt: tcp 1 2 3 4 5 6 7 8 9 10 11 12 13", 169 mounts: []*Mount{{ 170 Device: "192.168.1.1:/srv", 171 Mount: "/mnt/nfs", 172 Type: "nfs", 173 Stats: &MountStatsNFS{ 174 StatVersion: "1.1", 175 Transport: NFSTransportStats{ 176 Protocol: "tcp", 177 Port: 1, 178 Bind: 2, 179 Connect: 3, 180 ConnectIdleTime: 4, 181 IdleTimeSeconds: 5, 182 Sends: 6, 183 Receives: 7, 184 BadTransactionIDs: 8, 185 CumulativeActiveRequests: 9, 186 CumulativeBacklog: 10, 187 MaximumRPCSlotsUsed: 11, 188 CumulativeSendingQueue: 12, 189 CumulativePendingQueue: 13, 190 }, 191 }, 192 }}, 193 }, 194 { 195 name: "NFSv3 device using UDP with transport stats version 1.1 OK", 196 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1\nxprt: udp 1 2 3 4 5 6 7 8 9 10", 197 mounts: []*Mount{{ 198 Device: "192.168.1.1:/srv", 199 Mount: "/mnt/nfs", 200 Type: "nfs", 201 Stats: &MountStatsNFS{ 202 StatVersion: "1.1", 203 Transport: NFSTransportStats{ 204 Protocol: "udp", 205 Port: 1, 206 Bind: 2, 207 Connect: 0, // these three are not 208 ConnectIdleTime: 0, // present for UDP 209 IdleTimeSeconds: 0, // 210 Sends: 3, 211 Receives: 4, 212 BadTransactionIDs: 5, 213 CumulativeActiveRequests: 6, 214 CumulativeBacklog: 7, 215 MaximumRPCSlotsUsed: 8, 216 CumulativeSendingQueue: 9, 217 CumulativePendingQueue: 10, 218 }, 219 }, 220 }}, 221 }, 222 { 223 name: "NFSv3 device with mountaddr OK", 224 s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1\nopts: rw,vers=3,mountaddr=192.168.1.1,proto=udp\n", 225 mounts: []*Mount{{ 226 Device: "192.168.1.1:/srv", 227 Mount: "/mnt/nfs", 228 Type: "nfs", 229 Stats: &MountStatsNFS{ 230 StatVersion: "1.1", 231 Opts: map[string]string{"rw": "", "vers": "3", "mountaddr": "192.168.1.1", "proto": "udp"}, 232 }, 233 }}, 234 }, 235 { 236 name: "device rootfs OK", 237 s: `device rootfs mounted on / with fstype rootfs`, 238 mounts: []*Mount{{ 239 Device: "rootfs", 240 Mount: "/", 241 Type: "rootfs", 242 }}, 243 }, 244 { 245 name: "NFSv3 device with minimal stats OK", 246 s: `device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1`, 247 mounts: []*Mount{{ 248 Device: "192.168.1.1:/srv", 249 Mount: "/mnt/nfs", 250 Type: "nfs", 251 Stats: &MountStatsNFS{ 252 StatVersion: "1.1", 253 }, 254 }}, 255 }, 256 { 257 name: "fixtures/proc OK", 258 mounts: []*Mount{ 259 { 260 Device: "rootfs", 261 Mount: "/", 262 Type: "rootfs", 263 }, 264 { 265 Device: "sysfs", 266 Mount: "/sys", 267 Type: "sysfs", 268 }, 269 { 270 Device: "proc", 271 Mount: "/proc", 272 Type: "proc", 273 }, 274 { 275 Device: "/dev/sda1", 276 Mount: "/", 277 Type: "ext4", 278 }, 279 { 280 Device: "192.168.1.1:/srv/test", 281 Mount: "/mnt/nfs/test", 282 Type: "nfs4", 283 Stats: &MountStatsNFS{ 284 StatVersion: "1.1", 285 Opts: map[string]string{"rw": "", "vers": "4.0", 286 "rsize": "1048576", "wsize": "1048576", "namlen": "255", "acregmin": "3", 287 "acregmax": "60", "acdirmin": "30", "acdirmax": "60", "hard": "", 288 "proto": "tcp", "port": "0", "timeo": "600", "retrans": "2", 289 "sec": "sys", "mountaddr": "192.168.1.1", "clientaddr": "192.168.1.5", 290 "local_lock": "none", 291 }, 292 Age: 13968 * time.Second, 293 Bytes: NFSBytesStats{ 294 Read: 1207640230, 295 ReadTotal: 1210214218, 296 ReadPages: 295483, 297 }, 298 Events: NFSEventsStats{ 299 InodeRevalidate: 52, 300 DnodeRevalidate: 226, 301 VFSOpen: 1, 302 VFSLookup: 13, 303 VFSAccess: 398, 304 VFSReadPages: 331, 305 VFSWritePages: 47, 306 VFSFlush: 77, 307 VFSFileRelease: 77, 308 }, 309 Operations: []NFSOperationStats{ 310 { 311 Operation: "NULL", 312 }, 313 { 314 Operation: "READ", 315 Requests: 1298, 316 Transmissions: 1298, 317 BytesSent: 207680, 318 BytesReceived: 1210292152, 319 CumulativeQueueMilliseconds: 6, 320 CumulativeTotalResponseMilliseconds: 79386, 321 CumulativeTotalRequestMilliseconds: 79407, 322 }, 323 { 324 Operation: "WRITE", 325 }, 326 { 327 Operation: "ACCESS", 328 Requests: 2927395007, 329 Transmissions: 2927394995, 330 BytesSent: 526931094212, 331 BytesReceived: 362996810236, 332 CumulativeQueueMilliseconds: 18446743919241604546, 333 CumulativeTotalResponseMilliseconds: 1667369447, 334 CumulativeTotalRequestMilliseconds: 1953587717, 335 }, 336 }, 337 Transport: NFSTransportStats{ 338 Protocol: "tcp", 339 Port: 832, 340 Connect: 1, 341 IdleTimeSeconds: 11, 342 Sends: 6428, 343 Receives: 6428, 344 CumulativeActiveRequests: 12154, 345 MaximumRPCSlotsUsed: 24, 346 CumulativeSendingQueue: 26, 347 CumulativePendingQueue: 5726, 348 }, 349 }, 350 }, 351 }, 352 }, 353 } 354 355 for i, tt := range tests { 356 t.Logf("[%02d] test %q", i, tt.name) 357 358 var mounts []*Mount 359 var err error 360 361 if tt.s != "" { 362 mounts, err = parseMountStats(strings.NewReader(tt.s)) 363 } else { 364 proc, e := getProcFixtures(t).Proc(26231) 365 if e != nil { 366 t.Fatalf("failed to create proc: %v", err) 367 } 368 369 mounts, err = proc.MountStats() 370 } 371 372 if tt.invalid && err == nil { 373 t.Error("expected an error, but none occurred") 374 } 375 if !tt.invalid && err != nil { 376 t.Errorf("unexpected error: %v", err) 377 } 378 379 if want, have := tt.mounts, mounts; !reflect.DeepEqual(want, have) { 380 t.Errorf("mounts:\nwant:\n%v\nhave:\n%v", mountsStr(want), mountsStr(have)) 381 } 382 } 383} 384 385func mountsStr(mounts []*Mount) string { 386 var out string 387 for i, m := range mounts { 388 out += fmt.Sprintf("[%d] %q on %q (%q)", i, m.Device, m.Mount, m.Type) 389 390 stats, ok := m.Stats.(*MountStatsNFS) 391 if !ok { 392 out += "\n" 393 continue 394 } 395 396 out += fmt.Sprintf("\n\t- opts: %s", stats.Opts) 397 out += fmt.Sprintf("\n\t- v%s, age: %s", stats.StatVersion, stats.Age) 398 out += fmt.Sprintf("\n\t- bytes: %v", stats.Bytes) 399 out += fmt.Sprintf("\n\t- events: %v", stats.Events) 400 out += fmt.Sprintf("\n\t- transport: %v", stats.Transport) 401 out += fmt.Sprintf("\n\t- per-operation stats:") 402 403 for _, o := range stats.Operations { 404 out += fmt.Sprintf("\n\t\t- %v", o) 405 } 406 407 out += "\n" 408 } 409 410 return out 411} 412 413func TestMountStatsExtendedOperationStats(t *testing.T) { 414 r := strings.NewReader(extendedOpsExampleMountstats) 415 _, err := parseMountStats(r) 416 if err != nil { 417 t.Errorf("failed to parse mount stats with extended per-op statistics: %v", err) 418 } 419} 420 421const ( 422 extendedOpsExampleMountstats = ` 423device fs.example.com:/volume4/apps/home-automation/node-red-data mounted on /var/lib/kubelet/pods/1c2215a7-0d92-4df5-83ce-a807bcc2f8c8/volumes/kubernetes.io~nfs/home-automation--node-red-data--pv0001 with fstype nfs4 statvers=1.1 424 opts: rw,vers=4.1,rsize=131072,wsize=131072,namlen=255,acregmin=3,acregmax=60,acdirmin=30,acdirmax=60,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.1.191,local_lock=none 425 age: 83520 426 impl_id: name='',domain='',date='0,0' 427 caps: caps=0x3fff7,wtmult=512,dtsize=32768,bsize=0,namlen=255 428 nfsv4: bm0=0xfdffafff,bm1=0xf9be3e,bm2=0x800,acl=0x0,sessions,pnfs=not configured,lease_time=90,lease_expired=0 429 sec: flavor=1,pseudoflavor=1 430 events: 52472 472680 16671 57552 2104 9565 749555 9568641 168 24103 1 267134 3350 20097 116581 18214 43757 111141 0 28 9563845 34 0 0 0 0 0 431 bytes: 2021340783 39056395530 0 0 1788561151 39087991255 442605 9557343 432 RPC iostats version: 1.1 p/v: 100003/4 (nfs) 433 xprt: tcp 940 0 2 0 1 938505 938504 0 12756069 0 32 254729 10823602 434 per-op statistics 435 NULL: 1 1 0 44 24 0 0 0 0 436 READ: 34096 34096 0 7103096 1792122744 2272 464840 467945 0 437 WRITE: 322308 322308 0 39161277084 56725504 401718334 10139998 411864389 0 438 COMMIT: 12541 12541 0 2709896 1304264 342 7179 7819 0 439 OPEN: 12637 12637 0 3923256 4659940 871 57185 58251 394 440 OPEN_CONFIRM: 0 0 0 0 0 0 0 0 0 441 OPEN_NOATTR: 98741 98741 0 25656212 31630800 3366 77710 82693 0 442 OPEN_DOWNGRADE: 0 0 0 0 0 0 0 0 0 443 CLOSE: 87075 87075 0 18778608 15308496 2026 49131 52399 116 444 SETATTR: 24576 24576 0 5825876 6522260 643 34384 35650 0 445 FSINFO: 1 1 0 168 152 0 0 0 0 446 RENEW: 0 0 0 0 0 0 0 0 0 447 SETCLIENTID: 0 0 0 0 0 0 0 0 0 448 SETCLIENTID_CONFIRM: 0 0 0 0 0 0 0 0 0 449 LOCK: 22512 22512 0 5417628 2521312 1088 17407 18794 2 450 LOCKT: 0 0 0 0 0 0 0 0 0 451 LOCKU: 21247 21247 0 4589352 2379664 315 8409 9003 0 452 ACCESS: 1466 1466 0 298160 246288 22 1394 1492 0 453 GETATTR: 52480 52480 0 10015464 12694076 2930 30069 34502 0 454 LOOKUP: 11727 11727 0 2518200 2886376 272 16935 17662 3546 455 LOOKUP_ROOT: 0 0 0 0 0 0 0 0 0 456 REMOVE: 833 833 0 172236 95268 15 4566 4617 68 457 RENAME: 11431 11431 0 3150708 1737512 211 52649 53091 0 458 LINK: 1 1 0 288 292 0 0 0 0 459 SYMLINK: 0 0 0 0 0 0 0 0 0 460 CREATE: 77 77 0 18292 23496 0 363 371 11 461 PATHCONF: 1 1 0 164 116 0 0 0 0 462 STATFS: 7420 7420 0 1394960 1187200 144 4672 4975 0 463 READLINK: 4 4 0 704 488 0 1 1 0 464 READDIR: 1353 1353 0 304024 2902928 11 4326 4411 0 465 SERVER_CAPS: 9 9 0 1548 1476 0 3 3 0 466 DELEGRETURN: 232 232 0 48896 37120 811 300 1115 0 467 GETACL: 0 0 0 0 0 0 0 0 0 468 SETACL: 0 0 0 0 0 0 0 0 0 469 FS_LOCATIONS: 0 0 0 0 0 0 0 0 0 470 RELEASE_LOCKOWNER: 0 0 0 0 0 0 0 0 0 471 SECINFO: 0 0 0 0 0 0 0 0 0 472 FSID_PRESENT: 0 0 0 0 0 0 0 0 0 473 EXCHANGE_ID: 2 2 0 464 200 0 0 0 0 474 CREATE_SESSION: 1 1 0 192 124 0 0 0 0 475 DESTROY_SESSION: 0 0 0 0 0 0 0 0 0 476 SEQUENCE: 0 0 0 0 0 0 0 0 0 477 GET_LEASE_TIME: 0 0 0 0 0 0 0 0 0 478 RECLAIM_COMPLETE: 1 1 0 124 88 0 81 81 0 479 LAYOUTGET: 0 0 0 0 0 0 0 0 0 480 GETDEVICEINFO: 0 0 0 0 0 0 0 0 0 481 LAYOUTCOMMIT: 0 0 0 0 0 0 0 0 0 482 LAYOUTRETURN: 0 0 0 0 0 0 0 0 0 483 SECINFO_NO_NAME: 0 0 0 0 0 0 0 0 0 484 TEST_STATEID: 0 0 0 0 0 0 0 0 0 485 FREE_STATEID: 10413 10413 0 1416168 916344 147 3518 3871 10413 486 GETDEVICELIST: 0 0 0 0 0 0 0 0 0 487 BIND_CONN_TO_SESSION: 0 0 0 0 0 0 0 0 0 488 DESTROY_CLIENTID: 0 0 0 0 0 0 0 0 0 489 SEEK: 0 0 0 0 0 0 0 0 0 490 ALLOCATE: 0 0 0 0 0 0 0 0 0 491 DEALLOCATE: 0 0 0 0 0 0 0 0 0 492 LAYOUTSTATS: 0 0 0 0 0 0 0 0 0 493 CLONE: 0 0 0 0 0 0 0 0 0 494 COPY: 0 0 0 0 0 0 0 0 0 495 OFFLOAD_CANCEL: 0 0 0 0 0 0 0 0 0 496 LOOKUPP: 0 0 0 0 0 0 0 0 0 497 LAYOUTERROR: 0 0 0 0 0 0 0 0 0 498` 499) 500