Revision d7976f54

b/Makefile.am
1044 1044
	test/data/htools/hail-alloc-invalid-network.json \
1045 1045
	test/data/htools/hail-alloc-invalid-twodisks.json \
1046 1046
	test/data/htools/hail-alloc-restricted-network.json \
1047
	test/data/htools/hail-alloc-spindles.json \
1047 1048
	test/data/htools/hail-alloc-twodisks.json \
1048 1049
	test/data/htools/hail-change-group.json \
1049 1050
	test/data/htools/hail-invalid-reloc.json \
b/test/data/htools/hail-alloc-drbd.json
102 102
    "instance14": {
103 103
      "disks": [
104 104
        {
105
          "spindles": 1,
105 106
          "mode": "rw",
106 107
          "size": 128
107 108
        }
......
132 133
    "instance13": {
133 134
      "disks": [
134 135
        {
136
          "spindles": 1,
135 137
          "mode": "rw",
136 138
          "size": 512
137 139
        }
......
161 163
    "instance18": {
162 164
      "disks": [
163 165
        {
166
          "spindles": 1,
164 167
          "mode": "rw",
165 168
          "size": 128
166 169
        }
......
190 193
    "instance19": {
191 194
      "disks": [
192 195
        {
196
          "spindles": 1,
193 197
          "mode": "rw",
194 198
          "size": 128
195 199
        }
......
220 224
    "instance2": {
221 225
      "disks": [
222 226
        {
227
          "spindles": 1,
223 228
          "mode": "rw",
224 229
          "size": 128
225 230
        }
......
250 255
    "instance3": {
251 256
      "disks": [
252 257
        {
258
          "spindles": 1,
253 259
          "mode": "rw",
254 260
          "size": 256
255 261
        },
256 262
        {
263
          "spindles": 1,
257 264
          "mode": "rw",
258 265
          "size": 128
259 266
        }
......
283 290
    "instance4": {
284 291
      "disks": [
285 292
        {
293
          "spindles": 2,
286 294
          "mode": "rw",
287 295
          "size": 2048
288 296
        }
......
313 321
    "instance8": {
314 322
      "disks": [
315 323
        {
324
          "spindles": 1,
316 325
          "mode": "rw",
317 326
          "size": 256
318 327
        }
......
342 351
    "instance9": {
343 352
      "disks": [
344 353
        {
354
          "spindles": 1,
345 355
          "mode": "rw",
346 356
          "size": 128
347 357
        }
......
374 384
    "instance20": {
375 385
      "disks": [
376 386
        {
387
          "spindles": 1,
377 388
          "mode": "rw",
378 389
          "size": 512
379 390
        }
......
503 514
  "request": {
504 515
    "disks": [
505 516
      {
517
        "spindles": 1,
506 518
        "mode": "rw",
507 519
        "size": 1024
508 520
      }
b/test/data/htools/hail-alloc-invalid-twodisks.json
74 74
    "disk_template": "file",
75 75
    "disks": [
76 76
      {
77
        "spindles": 1,
77 78
        "size": 768
78 79
      },
79 80
      {
81
        "spindles": 1,
80 82
        "size": 768
81 83
      }
82 84
    ],
b/test/data/htools/hail-alloc-spindles.json
1
{
2
  "cluster_tags": [
3
    "htools:iextags:test",
4
    "htools:iextags:service-group"
5
  ],
6
  "nodegroups": {
7
    "uuid-group-1": {
8
      "ipolicy": {
9
        "std": {
10
          "nic-count": 1,
11
          "disk-size": 1024,
12
          "disk-count": 1,
13
          "memory-size": 128,
14
          "cpu-count": 1,
15
          "spindle-use": 1
16
        },
17
        "minmax": [
18
          {
19
            "min": {
20
              "nic-count": 1,
21
              "disk-size": 128,
22
              "disk-count": 1,
23
              "memory-size": 128,
24
              "cpu-count": 1,
25
              "spindle-use": 1
26
            },
27
            "max": {
28
              "nic-count": 8,
29
              "disk-size": 1048576,
30
              "disk-count": 16,
31
              "memory-size": 32768,
32
              "cpu-count": 8,
33
              "spindle-use": 2
34
            }
35
          }
36
        ],
37
        "vcpu-ratio": 4.0,
38
        "disk-templates": [
39
          "sharedfile",
40
          "diskless",
41
          "plain",
42
          "blockdev",
43
          "drbd",
44
          "file",
45
          "rbd"
46
        ],
47
        "spindle-ratio": 32.0
48
      },
49
      "alloc_policy": "preferred",
50
      "networks": [],
51
      "tags": [],
52
      "name": "group1"
53
    },
54
    "uuid-group-2": {
55
      "ipolicy": {
56
        "std": {
57
          "nic-count": 1,
58
          "disk-size": 1024,
59
          "disk-count": 1,
60
          "memory-size": 128,
61
          "cpu-count": 1,
62
          "spindle-use": 2
63
        },
64
        "minmax": [
65
          {
66
            "min": {
67
              "nic-count": 1,
68
              "disk-size": 128,
69
              "disk-count": 1,
70
              "memory-size": 128,
71
              "cpu-count": 1,
72
              "spindle-use": 2
73
            },
74
            "max": {
75
              "nic-count": 8,
76
              "disk-size": 1048576,
77
              "disk-count": 16,
78
              "memory-size": 32768,
79
              "cpu-count": 8,
80
              "spindle-use": 3
81
            }
82
          }
83
        ],
84
        "vcpu-ratio": 4.0,
85
        "disk-templates": [
86
          "sharedfile",
87
          "diskless",
88
          "plain",
89
          "blockdev",
90
          "drbd",
91
          "file",
92
          "rbd"
93
        ],
94
        "spindle-ratio": 32.0
95
      },
96
      "alloc_policy": "preferred",
97
      "networks": [],
98
      "tags": [],
99
      "name": "group2"
100
    }
101
  },
102
  "ipolicy": {
103
    "std": {
104
      "nic-count": 1,
105
      "disk-size": 1024,
106
      "memory-size": 128,
107
      "cpu-count": 1,
108
      "disk-count": 1,
109
      "spindle-use": 1
110
    },
111
    "minmax": [
112
      {
113
        "min": {
114
          "nic-count": 1,
115
          "disk-size": 1024,
116
          "memory-size": 128,
117
          "cpu-count": 1,
118
          "disk-count": 1,
119
          "spindle-use": 1
120
        },
121
        "max": {
122
          "nic-count": 8,
123
          "disk-size": 1048576,
124
          "memory-size": 32768,
125
          "cpu-count": 8,
126
          "disk-count": 16,
127
          "spindle-use": 8
128
        }
129
      }
130
    ],
131
    "vcpu-ratio": 4.0,
132
    "disk-templates": [
133
      "sharedfile",
134
      "diskless",
135
      "plain",
136
      "blockdev",
137
      "drbd",
138
      "file",
139
      "rbd"
140
    ],
141
    "spindle-ratio": 32.0
142
  },
143
  "enabled_hypervisors": [
144
    "xen-pvm",
145
    "xen-hvm"
146
  ],
147
  "cluster_name": "cluster",
148
  "instances": {
149
    "instance1": {
150
      "disks": [
151
        {
152
          "spindles": 1,
153
          "mode": "rw",
154
          "size": 650000
155
        }
156
      ],
157
      "disk_space_total": 650000,
158
      "hypervisor": "xen-pvm",
159
      "tags": [],
160
      "nics": [
161
        {
162
          "ip": null,
163
          "mac": "aa:00:00:7f:8c:91",
164
          "link": "xen-br1",
165
          "mode": "bridged",
166
          "bridge": "xen-br1"
167
        }
168
      ],
169
      "vcpus": 1,
170
      "spindle_use": 1,
171
      "admin_state": "up",
172
      "disk_template": "plain",
173
      "memory": 1024,
174
      "nodes": [
175
        "node1"
176
      ],
177
      "os": "instance-debootstrap"
178
    },
179
    "instance2": {
180
      "disks": [
181
        {
182
          "spindles": 2,
183
          "mode": "rw",
184
          "size": 256
185
        }
186
      ],
187
      "disk_space_total": 256,
188
      "hypervisor": "xen-pvm",
189
      "tags": [],
190
      "nics": [
191
        {
192
          "ip": null,
193
          "mac": "aa:00:00:7f:8c:92",
194
          "link": "xen-br1",
195
          "mode": "bridged",
196
          "bridge": "xen-br1"
197
        }
198
      ],
199
      "vcpus": 1,
200
      "spindle_use": 1,
201
      "admin_state": "up",
202
      "disk_template": "plain",
203
      "memory": 1024,
204
      "nodes": [
205
        "node2"
206
      ],
207
      "os": "instance-debootstrap"
208
    },
209
    "instance3": {
210
      "disks": [
211
        {
212
          "spindles": 1,
213
          "mode": "rw",
214
          "size": 650000
215
        }
216
      ],
217
      "disk_space_total": 650000,
218
      "hypervisor": "xen-pvm",
219
      "tags": [],
220
      "nics": [
221
        {
222
          "ip": null,
223
          "mac": "aa:00:00:7f:8c:93",
224
          "link": "xen-br1",
225
          "mode": "bridged",
226
          "bridge": "xen-br1"
227
        }
228
      ],
229
      "vcpus": 1,
230
      "spindle_use": 1,
231
      "admin_state": "up",
232
      "disk_template": "plain",
233
      "memory": 1024,
234
      "nodes": [
235
        "node3"
236
      ],
237
      "os": "instance-debootstrap"
238
    },
239
    "instance4": {
240
      "disks": [
241
        {
242
          "spindles": 2,
243
          "mode": "rw",
244
          "size": 256
245
        }
246
      ],
247
      "disk_space_total": 256,
248
      "hypervisor": "xen-pvm",
249
      "tags": [],
250
      "nics": [
251
        {
252
          "ip": null,
253
          "mac": "aa:00:00:7f:8c:94",
254
          "link": "xen-br1",
255
          "mode": "bridged",
256
          "bridge": "xen-br1"
257
        }
258
      ],
259
      "vcpus": 1,
260
      "spindle_use": 1,
261
      "admin_state": "up",
262
      "disk_template": "plain",
263
      "memory": 1024,
264
      "nodes": [
265
        "node4"
266
      ],
267
      "os": "instance-debootstrap"
268
    }
269
  },
270
  "version": 2,
271
  "nodes": {
272
    "node1": {
273
      "total_disk": 1377280,
274
      "total_cpus": 4,
275
      "group": "uuid-group-1",
276
      "secondary_ip": "192.168.2.1",
277
      "i_pri_up_memory": 1024,
278
      "tags": [],
279
      "master_candidate": true,
280
      "free_memory": 30722,
281
      "ndparams": {
282
        "spindle_count": 2,
283
        "oob_program": null,
284
        "exclusive_storage": false
285
      },
286
      "reserved_memory": 1017,
287
      "master_capable": true,
288
      "free_disk": 687280,
289
      "drained": false,
290
      "total_memory": 32763,
291
      "primary_ip": "192.168.1.1",
292
      "i_pri_memory": 1024,
293
      "vm_capable": true,
294
      "offline": false
295
    },
296
    "node2": {
297
      "total_disk": 1377280,
298
      "total_cpus": 4,
299
      "group": "uuid-group-1",
300
      "secondary_ip": "192.168.2.2",
301
      "i_pri_up_memory": 1024,
302
      "tags": [],
303
      "master_candidate": true,
304
      "free_memory": 30722,
305
      "ndparams": {
306
        "spindle_count": 2,
307
        "oob_program": null,
308
        "exclusive_storage": false
309
      },
310
      "reserved_memory": 1017,
311
      "master_capable": true,
312
      "free_disk": 1377024,
313
      "drained": false,
314
      "total_memory": 32763,
315
      "primary_ip": "192.168.1.2",
316
      "i_pri_memory": 1024,
317
      "vm_capable": true,
318
      "offline": false
319
    },
320
    "node3": {
321
      "total_disk": 1377280,
322
      "total_cpus": 4,
323
      "group": "uuid-group-2",
324
      "secondary_ip": "192.168.2.3",
325
      "i_pri_up_memory": 1024,
326
      "tags": [],
327
      "master_candidate": true,
328
      "free_memory": 30722,
329
      "ndparams": {
330
        "spindle_count": 2,
331
        "oob_program": null,
332
        "exclusive_storage": false
333
      },
334
      "reserved_memory": 1017,
335
      "master_capable": true,
336
      "free_disk": 687280,
337
      "drained": false,
338
      "total_memory": 32763,
339
      "primary_ip": "192.168.1.3",
340
      "i_pri_memory": 1204,
341
      "vm_capable": true,
342
      "offline": false
343
    },
344
    "node4": {
345
      "total_disk": 1377280,
346
      "total_cpus": 4,
347
      "group": "uuid-group-2",
348
      "secondary_ip": "192.168.2.4",
349
      "i_pri_up_memory": 1024,
350
      "tags": [],
351
      "master_candidate": true,
352
      "free_memory": 30722,
353
      "ndparams": {
354
        "spindle_count": 2,
355
        "oob_program": null,
356
        "exclusive_storage": false
357
      },
358
      "reserved_memory": 1017,
359
      "master_capable": true,
360
      "free_disk": 1377024,
361
      "drained": false,
362
      "total_memory": 32763,
363
      "primary_ip": "192.168.1.4",
364
      "i_pri_memory": 1024,
365
      "vm_capable": true,
366
      "offline": false
367
    }
368
  },
369
  "request": {
370
    "disks": [
371
      {
372
        "spindles": 1,
373
        "mode": "rw",
374
        "size": 1024
375
      }
376
    ],
377
    "required_nodes": 1,
378
    "name": "instance10",
379
    "tags": [],
380
    "hypervisor": "xen-pvm",
381
    "disk_space_total": 1024,
382
    "nics": [
383
      {
384
        "ip": null,
385
        "mac": "00:11:22:33:44:55",
386
        "bridge": null
387
      }
388
    ],
389
    "vcpus": 1,
390
    "spindle_use": 3,
391
    "os": "instance-debootstrap",
392
    "disk_template": "plain",
393
    "memory": 1024,
394
    "type": "allocate"
395
  }
396
}
b/test/data/htools/hail-alloc-twodisks.json
74 74
    "disk_template": "file",
75 75
    "disks": [
76 76
      {
77
        "spindles": 1,
77 78
        "size": 1536
78 79
      },
79 80
      {
81
        "spindles": 1,
80 82
        "size": 1536
81 83
      }
82 84
    ],
b/test/data/htools/hail-change-group.json
149 149
    "instance14": {
150 150
      "disks": [
151 151
        {
152
	  "spindles": 1,
152 153
          "mode": "rw",
153 154
          "size": 128
154 155
        }
......
179 180
    "instance13": {
180 181
      "disks": [
181 182
        {
183
          "spindles": 2,
182 184
          "mode": "rw",
183 185
          "size": 512
184 186
        }
......
208 210
    "instance18": {
209 211
      "disks": [
210 212
        {
213
          "spindles": 1,
211 214
          "mode": "rw",
212 215
          "size": 128
213 216
        }
......
237 240
    "instance19": {
238 241
      "disks": [
239 242
        {
243
          "spindles": 1,
240 244
          "mode": "rw",
241 245
          "size": 128
242 246
        }
......
267 271
    "instance2": {
268 272
      "disks": [
269 273
        {
274
          "spindles": 1,
270 275
          "mode": "rw",
271 276
          "size": 128
272 277
        }
......
297 302
    "instance3": {
298 303
      "disks": [
299 304
        {
305
          "spindles": 1,
300 306
          "mode": "rw",
301 307
          "size": 256
302 308
        },
......
330 336
    "instance4": {
331 337
      "disks": [
332 338
        {
339
          "spindles": 2,
333 340
          "mode": "rw",
334 341
          "size": 2048
335 342
        }
......
360 367
    "instance8": {
361 368
      "disks": [
362 369
        {
370
          "spindles": 1,
363 371
          "mode": "rw",
364 372
          "size": 256
365 373
        }
......
389 397
    "instance9": {
390 398
      "disks": [
391 399
        {
400
          "spindles": 1,
392 401
          "mode": "rw",
393 402
          "size": 128
394 403
        }
......
421 430
    "instance20": {
422 431
      "disks": [
423 432
        {
433
          "spindles": 1,
424 434
          "mode": "rw",
425 435
          "size": 512
426 436
        }
b/test/data/htools/hail-node-evac.json
98 98
    "instance14": {
99 99
      "disks": [
100 100
        {
101
          "spindles": 1,
101 102
          "mode": "rw",
102 103
          "size": 128
103 104
        }
......
128 129
    "instance13": {
129 130
      "disks": [
130 131
        {
132
          "spindles": 1,
131 133
          "mode": "rw",
132 134
          "size": 512
133 135
        }
......
157 159
    "instance18": {
158 160
      "disks": [
159 161
        {
162
          "spindles": 1,
160 163
          "mode": "rw",
161 164
          "size": 128
162 165
        }
......
186 189
    "instance19": {
187 190
      "disks": [
188 191
        {
192
          "spindles": 1,
189 193
          "mode": "rw",
190 194
          "size": 128
191 195
        }
......
216 220
    "instance2": {
217 221
      "disks": [
218 222
        {
223
          "spindles": 1,
219 224
          "mode": "rw",
220 225
          "size": 128
221 226
        }
......
246 251
    "instance3": {
247 252
      "disks": [
248 253
        {
254
          "spindles": 1,
249 255
          "mode": "rw",
250 256
          "size": 256
251 257
        },
252 258
        {
259
	  "spindles": 1,
253 260
          "mode": "rw",
254 261
          "size": 128
255 262
        }
......
279 286
    "instance4": {
280 287
      "disks": [
281 288
        {
289
          "spindles": 2,
282 290
          "mode": "rw",
283 291
          "size": 2048
284 292
        }
......
309 317
    "instance8": {
310 318
      "disks": [
311 319
        {
320
          "spindles": 1,
312 321
          "mode": "rw",
313 322
          "size": 256
314 323
        }
......
338 347
    "instance9": {
339 348
      "disks": [
340 349
        {
350
          "spindles": 1,
341 351
          "mode": "rw",
342 352
          "size": 128
343 353
        }
......
370 380
    "instance20": {
371 381
      "disks": [
372 382
        {
383
          "spindles": 1,
373 384
          "mode": "rw",
374 385
          "size": 512
375 386
        }
b/test/data/htools/hail-reloc-drbd.json
102 102
    "instance14": {
103 103
      "disks": [
104 104
        {
105
          "spindles": 1,
105 106
          "mode": "rw",
106 107
          "size": 128
107 108
        }
......
132 133
    "instance13": {
133 134
      "disks": [
134 135
        {
136
          "spindles": 1,
135 137
          "mode": "rw",
136 138
          "size": 512
137 139
        }
......
161 163
    "instance18": {
162 164
      "disks": [
163 165
        {
166
          "spindles": 1,
164 167
          "mode": "rw",
165 168
          "size": 128
166 169
        }
......
190 193
    "instance19": {
191 194
      "disks": [
192 195
        {
196
          "spindles": 1,
193 197
          "mode": "rw",
194 198
          "size": 128
195 199
        }
......
220 224
    "instance2": {
221 225
      "disks": [
222 226
        {
227
          "spindles": 1,
223 228
          "mode": "rw",
224 229
          "size": 128
225 230
        }
......
250 255
    "instance3": {
251 256
      "disks": [
252 257
        {
258
          "spindles": 1,
253 259
          "mode": "rw",
254 260
          "size": 256
255 261
        },
256 262
        {
263
          "spindles": 1,
257 264
          "mode": "rw",
258 265
          "size": 128
259 266
        }
......
283 290
    "instance4": {
284 291
      "disks": [
285 292
        {
293
          "spindles": 2,
286 294
          "mode": "rw",
287 295
          "size": 2048
288 296
        }
......
313 321
    "instance8": {
314 322
      "disks": [
315 323
        {
324
          "spindles": 1,
316 325
          "mode": "rw",
317 326
          "size": 256
318 327
        }
......
342 351
    "instance9": {
343 352
      "disks": [
344 353
        {
354
          "spindles": 1,
345 355
          "mode": "rw",
346 356
          "size": 128
347 357
        }
......
374 384
    "instance20": {
375 385
      "disks": [
376 386
        {
387
          "spindles": 1,
377 388
          "mode": "rw",
378 389
          "size": 512
379 390
        }
b/test/hs/offline-test.sh
67 67
echo -n Generating data files for IAllocator checks...
68 68
for evac_mode in primary-only secondary-only all; do
69 69
  sed -e 's/"evac_mode": "all"/"evac_mode": "'${evac_mode}'"/' \
70
    -e 's/"spindles": [0-9]\+,//' \
70 71
    < $TESTDATA_DIR/hail-node-evac.json \
71 72
    > $T/hail-node-evac.json.$evac_mode
72 73
done
74
for bf in hail-alloc-drbd hail-alloc-invalid-twodisks hail-alloc-twodisks \
75
  hail-change-group hail-node-evac hail-reloc-drbd hail-alloc-spindles; do
76
  f=$bf.json
77
  sed -e 's/"exclusive_storage": false/"exclusive_storage": true/' \
78
    < $TESTDATA_DIR/$f > $T/$f.excl-stor
79
  sed -e 's/"exclusive_storage": false/"exclusive_storage": true/' \
80
    -e 's/"spindles": [0-9]\+,//' \
81
    < $TESTDATA_DIR/$f > $T/$f.fail-excl-stor
82
done
73 83
echo OK
74 84

  
75 85
echo -n Checking file-based RAPI...
b/test/hs/shelltests/htools-hail.test
62 62
>>> /"success":true,"info":"Request successful: Selected group: Group 2.*/
63 63
>>>= 0
64 64

  
65
# Run some of the tests above, with exclusive storage enabled
66
./test/hs/hail $T/hail-alloc-drbd.json.excl-stor
67
>>> /"success":true,.*,"result":\["node2","node1"\]/
68
>>>= 0
69

  
70
./test/hs/hail $T/hail-reloc-drbd.json.excl-stor
71
>>> /"success":true,.*,"result":\["node1"\]/
72
>>>= 0
73

  
74
./test/hs/hail $T/hail-node-evac.json.excl-stor
75
>>> /"success":true,"info":"Request successful: 0 instances failed to move and 1 were moved successfully"/
76
>>>= 0
77

  
78
./test/hs/hail $T/hail-change-group.json.excl-stor
79
>>> /"success":true,"info":"Request successful: 0 instances failed to move and 1 were moved successfully"/
80
>>>= 0
81

  
82
./test/hs/hail $T/hail-alloc-twodisks.json.excl-stor
83
>>> /"success":true,.*,"result":\["node1"\]/
84
>>>= 0
85

  
86
./test/hs/hail $T/hail-alloc-invalid-twodisks.json.excl-stor
87
>>> /"success":false,.*FailDisk: 1"/
88
>>>= 0
89

  
90
# Same tests with exclusive storage enabled, but no spindles info in instances
91
# Operations involving relocation succed because policies are not checked
92
./test/hs/hail $T/hail-alloc-drbd.json.fail-excl-stor
93
>>> /"success":false,.*FailSpindles: 12"/
94
>>>= 0
95

  
96
./test/hs/hail $T/hail-reloc-drbd.json.fail-excl-stor
97
>>> /"success":true,.*,"result":\["node1"\]/
98
>>>= 0
99

  
100
./test/hs/hail $T/hail-node-evac.json.fail-excl-stor
101
>>> /"success":true,"info":"Request successful: 0 instances failed to move and 1 were moved successfully"/
102
>>>= 0
103

  
104
./test/hs/hail $T/hail-change-group.json.fail-excl-stor
105
>>> /"success":true,"info":"Request successful: 1 instances failed to move and 0 were moved successfully",.*FailSpindles: 2"/
106
>>>= 0
107

  
108
./test/hs/hail $T/hail-alloc-twodisks.json.fail-excl-stor
109
>>> /"success":false,.*FailSpindles: 1"/
110
>>>= 0
111

  
65 112
# check that hail can use the simu backend
66 113
./test/hs/hail --simu p,8,8T,16g,16 $TESTDATA_DIR/hail-alloc-drbd.json
67 114
>>> /"success":true,/
......
99 146
./test/hs/hail $T/hail-node-evac.json.all
100 147
>>> /"success":true,"info":"Request successful: 0 instances failed to move and 1 were moved successfully"/
101 148
>>>= 0
149

  
150
# Check interaction between policies and spindles
151
./test/hs/hail $TESTDATA_DIR/hail-alloc-spindles.json
152
>>> /"success":true,"info":"Request successful: Selected group: group2,.*FailSpindles: 2,.*"result":\["node4"\]/
153
>>>= 0
154

  
155
./test/hs/hail $T/hail-alloc-spindles.json.excl-stor
156
>>> /"success":true,"info":"Request successful: Selected group: group1,.*FailSpindles: 2",.*"result":\["node2"\]/
157
>>>= 0

Also available in: Unified diff