synapse 2.186.0__py311-none-any.whl → 2.187.1__py311-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

synapse/lib/modelrev.py CHANGED
@@ -2,14 +2,18 @@ import regex
2
2
  import logging
3
3
 
4
4
  import synapse.exc as s_exc
5
- import synapse.assets as s_assets
6
5
  import synapse.common as s_common
7
6
 
7
+ import synapse.lib.cache as s_cache
8
8
  import synapse.lib.layer as s_layer
9
+ import synapse.lib.msgpack as s_msgpack
10
+ import synapse.lib.spooled as s_spooled
11
+
12
+ import synapse.models.infotech as s_infotech
9
13
 
10
14
  logger = logging.getLogger(__name__)
11
15
 
12
- maxvers = (0, 2, 30)
16
+ maxvers = (0, 2, 31)
13
17
 
14
18
  class ModelRev:
15
19
 
@@ -45,6 +49,7 @@ class ModelRev:
45
49
  # Model revision 0.2.28 skipped
46
50
  ((0, 2, 29), self.revModel_0_2_29),
47
51
  ((0, 2, 30), self.revModel_0_2_30),
52
+ ((0, 2, 31), self.revModel_0_2_31),
48
53
  )
49
54
 
50
55
  async def _uniqSortArray(self, todoprops, layers):
@@ -791,15 +796,6 @@ class ModelRev:
791
796
  async def revModel_0_2_27(self, layers):
792
797
  await self._normPropValu(layers, 'it:dev:repo:commit:id')
793
798
 
794
- async def revModel_0_2_28(self, layers):
795
-
796
- opts = {'vars': {
797
- 'layridens': [layr.iden for layr in layers],
798
- }}
799
-
800
- text = s_assets.getStorm('migrations', 'model-0.2.28.storm')
801
- await self.runStorm(text, opts=opts)
802
-
803
799
  async def revModel_0_2_29(self, layers):
804
800
  await self._propToForm(layers, 'ou:industry:type', 'ou:industry:type:taxonomy')
805
801
 
@@ -814,6 +810,11 @@ class ModelRev:
814
810
  await self._normFormSubs(layers, 'inet:ipv6', cmprvalu='2001:20::/28')
815
811
  await self._normFormSubs(layers, 'inet:ipv6', cmprvalu='2001:30::/28')
816
812
 
813
+ async def revModel_0_2_31(self, layers):
814
+ migr = await ModelMigration_0_2_31.anit(self.core, layers)
815
+ await migr.revModel_0_2_31()
816
+ await self._normFormSubs(layers, 'it:sec:cpe')
817
+
817
818
  async def runStorm(self, text, opts=None):
818
819
  '''
819
820
  Run storm code in a schedcoro and log the output messages.
@@ -1205,3 +1206,785 @@ class ModelRev:
1205
1206
  }
1206
1207
  '''
1207
1208
  await self.runStorm(storm, opts=opts)
1209
+
1210
+ class ModelMigration_0_2_31:
1211
+ @classmethod
1212
+ async def anit(cls, core, layers):
1213
+ self = cls()
1214
+
1215
+ self.core = core
1216
+ self.layers = layers
1217
+
1218
+ self.meta = {'time': s_common.now(), 'user': self.core.auth.rootuser.iden}
1219
+
1220
+ self.editcount = 0
1221
+ self.nodeedits = {}
1222
+
1223
+ self.nodes = await s_spooled.Dict.anit(dirn=self.core.dirn)
1224
+ self.todos = await s_spooled.Set.anit(dirn=self.core.dirn)
1225
+
1226
+ self.core.onfini(self.nodes)
1227
+ self.core.onfini(self.todos)
1228
+
1229
+ try:
1230
+ await self.core.getCoreQueue('model_0_2_31:nodes')
1231
+ self.hasq = True
1232
+ except s_exc.NoSuchName:
1233
+ self.hasq = False
1234
+
1235
+ return self
1236
+
1237
+ async def _queueEdit(self, layriden, edit):
1238
+ self.nodeedits.setdefault(layriden, {})
1239
+ buid, formname, edits = edit
1240
+ self.nodeedits[layriden].setdefault(buid, (buid, formname, []))
1241
+ self.nodeedits[layriden][buid][2].extend(edits)
1242
+ self.editcount += 1
1243
+
1244
+ if self.editcount >= 1000: # pragma: no cover
1245
+ await self._flushEdits()
1246
+
1247
+ async def _flushEdits(self):
1248
+ for layriden, layredits in self.nodeedits.items():
1249
+ layer = self.core.getLayer(layriden)
1250
+ if layer is None: # pragma: no cover
1251
+ continue
1252
+
1253
+ await layer.storNodeEditsNoLift(list(layredits.values()), self.meta)
1254
+
1255
+ self.editcount = 0
1256
+ self.nodeedits = {}
1257
+
1258
+ # NOTE: For the edit* functions below, we only need precise state tracking for nodes and properties. Don't precisely
1259
+ # track the rest.
1260
+ async def editNodeAdd(self, layriden, buid, formname, formvalu, stortype):
1261
+ if not self.nodes.has(buid):
1262
+
1263
+ node = {
1264
+ 'formname': formname,
1265
+ 'formvalu': formvalu,
1266
+ 'sodes': {},
1267
+ 'nodedata': {},
1268
+ 'n1edges': {},
1269
+ 'n2edges': {},
1270
+ }
1271
+ await self.nodes.set(buid, node)
1272
+
1273
+ await self._queueEdit(layriden,
1274
+ (buid, formname, (
1275
+ (s_layer.EDIT_NODE_ADD, (formvalu, stortype), ()),
1276
+ )),
1277
+ )
1278
+
1279
+ async def editPropSet(self, layriden, buid, formname, propname, newvalu, oldvalu, stortype):
1280
+ assert self.nodes.has(buid)
1281
+ node = self.getNode(buid)
1282
+
1283
+ sode = node['sodes'].get(layriden, {})
1284
+ node['sodes'][layriden] = sode
1285
+
1286
+ props = sode.get('props', {})
1287
+ sode['props'] = props
1288
+
1289
+ if oldvalu is not None:
1290
+ assert props.get(propname) == (oldvalu, stortype), f'GOT: {props.get(propname)} EXPECTED: {(oldvalu, stortype)}'
1291
+ props[propname] = (newvalu, stortype)
1292
+
1293
+ await self.nodes.set(buid, node)
1294
+
1295
+ await self._queueEdit(layriden,
1296
+ (buid, formname, (
1297
+ (s_layer.EDIT_PROP_SET, (propname, newvalu, oldvalu, stortype), ()),
1298
+ )),
1299
+ )
1300
+
1301
+ async def editTagSet(self, layriden, buid, formname, tagname, newvalu, oldvalu):
1302
+ await self._queueEdit(layriden,
1303
+ (buid, formname, (
1304
+ (s_layer.EDIT_TAG_SET, (tagname, newvalu, oldvalu), ()),
1305
+ )),
1306
+ )
1307
+
1308
+ async def editTagpropSet(self, layriden, buid, formname, tagname, propname, newvalu, oldvalu, stortype):
1309
+ await self._queueEdit(layriden,
1310
+ (buid, formname, (
1311
+ (s_layer.EDIT_TAGPROP_SET, (tagname, propname, newvalu, oldvalu, stortype), ()),
1312
+ )),
1313
+ )
1314
+
1315
+ async def editNodedataSet(self, layriden, buid, formname, name, newvalu, oldvalu):
1316
+ await self._queueEdit(layriden,
1317
+ (buid, formname, (
1318
+ (s_layer.EDIT_NODEDATA_SET, (name, newvalu, oldvalu), ()),
1319
+ )),
1320
+ )
1321
+
1322
+ async def editEdgeAdd(self, layriden, buid, formname, verb, iden):
1323
+ await self._queueEdit(layriden,
1324
+ (buid, formname, (
1325
+ (s_layer.EDIT_EDGE_ADD, (verb, iden), ()),
1326
+ )),
1327
+ )
1328
+
1329
+ async def editNodeDel(self, layriden, buid, formname, formvalu):
1330
+ assert self.nodes.has(buid)
1331
+ node = self.nodes.pop(buid)
1332
+
1333
+ for layriden in node['layers']:
1334
+ await self._queueEdit(layriden,
1335
+ (buid, formname, (
1336
+ (s_layer.EDIT_NODE_DEL, formvalu, ()),
1337
+ )),
1338
+ )
1339
+
1340
+ async def editPropDel(self, layriden, buid, formname, propname, propvalu, stortype):
1341
+ assert self.nodes.has(buid)
1342
+ node = self.getNode(buid)
1343
+
1344
+ sode = node['sodes'][layriden]
1345
+ props = sode.get('props', {})
1346
+
1347
+ assert props.get(propname) == (propvalu, stortype), f'GOT: {props.get(propname)} EXPECTED: {(propvalu, stortype)}'
1348
+
1349
+ props.pop(propname)
1350
+
1351
+ await self.nodes.set(buid, node)
1352
+
1353
+ await self._queueEdit(layriden,
1354
+ (buid, formname, (
1355
+ (s_layer.EDIT_PROP_DEL, (propname, propvalu, stortype), ()),
1356
+ )),
1357
+ )
1358
+
1359
+ async def editTagDel(self, layriden, buid, formname, tagname, tagvalu):
1360
+ await self._queueEdit(layriden,
1361
+ (buid, formname, (
1362
+ (s_layer.EDIT_TAG_DEL, (tagname, tagvalu), ()),
1363
+ )),
1364
+ )
1365
+
1366
+ async def editTagpropDel(self, layriden, buid, formname, tagname, propname, propvalu, stortype):
1367
+ await self._queueEdit(layriden,
1368
+ (buid, formname, (
1369
+ (s_layer.EDIT_TAGPROP_DEL, (tagname, propname, propvalu, stortype), ()),
1370
+ )),
1371
+ )
1372
+
1373
+ async def editNodedataDel(self, layriden, buid, formname, name, valu):
1374
+ await self._queueEdit(layriden,
1375
+ (buid, formname, (
1376
+ (s_layer.EDIT_NODEDATA_DEL, (name, valu), ()),
1377
+ )),
1378
+ )
1379
+
1380
+ async def editEdgeDel(self, layriden, buid, formname, verb, iden):
1381
+ await self._queueEdit(layriden,
1382
+ (buid, formname, (
1383
+ (s_layer.EDIT_EDGE_DEL, (verb, iden), ()),
1384
+ )),
1385
+ )
1386
+
1387
+ def getNode(self, buid):
1388
+ node = self.nodes.get(buid, {})
1389
+ if not node:
1390
+ node.setdefault('refs', {})
1391
+ node.setdefault('sodes', {})
1392
+ node.setdefault('layers', [])
1393
+ node.setdefault('n1edges', {})
1394
+ node.setdefault('n2edges', {})
1395
+ node.setdefault('verdict', None)
1396
+ node.setdefault('nodedata', {})
1397
+ return node
1398
+
1399
+ async def _loadNode(self, layer, buid, node=None):
1400
+ if node is None:
1401
+ node = self.getNode(buid)
1402
+
1403
+ sode = await layer.getStorNode(buid)
1404
+ if sode:
1405
+ node['sodes'].setdefault(layer.iden, {})
1406
+ node['sodes'][layer.iden] = sode
1407
+
1408
+ if (formvalu := sode.get('valu')) is not None:
1409
+ if node.get('formvalu') is None:
1410
+ node['formvalu'] = formvalu[0]
1411
+ node['formname'] = sode.get('form')
1412
+
1413
+ if layer.iden not in node['layers']:
1414
+ layers = list(node['layers'])
1415
+ layers.append(layer.iden)
1416
+ node['layers'] = layers
1417
+
1418
+ # Get nodedata
1419
+ nodedata = [k async for k in layer.iterNodeData(buid)]
1420
+ if nodedata:
1421
+ node['nodedata'][layer.iden] = nodedata
1422
+
1423
+ # Collect N1 edges
1424
+ n1edges = [k async for k in layer.iterNodeEdgesN1(buid)]
1425
+ if n1edges:
1426
+ node['n1edges'][layer.iden] = n1edges
1427
+
1428
+ # Collect N2 edges
1429
+ n2edges = []
1430
+ async for verb, iden in layer.iterNodeEdgesN2(buid):
1431
+ n2edges.append((verb, iden))
1432
+
1433
+ await self.todos.add(('getvalu', (s_common.uhex(iden), False)))
1434
+
1435
+ if n2edges:
1436
+ node['n2edges'][layer.iden] = n2edges
1437
+
1438
+ await self.nodes.set(buid, node)
1439
+ return node
1440
+
1441
+ async def revModel_0_2_31(self):
1442
+
1443
+ form = self.core.model.form('it:sec:cpe')
1444
+
1445
+ logger.info(f'Collecting and classifying it:sec:cpe nodes in {len(self.layers)} layers')
1446
+
1447
+ # Pick up and classify all bad CPE nodes
1448
+ for idx, layer in enumerate(self.layers):
1449
+ logger.debug('Classifying nodes in layer %s %s', idx, layer.iden)
1450
+
1451
+ async for buid, sode in layer.getStorNodesByForm('it:sec:cpe'):
1452
+
1453
+ verdict = 'remove'
1454
+
1455
+ # Delete invalid v2_2 props while we're iterating
1456
+ props = sode.get('props', {})
1457
+ if (v2_2 := props.get('v2_2')) is not None:
1458
+ propvalu, stortype = v2_2
1459
+ if not s_infotech.isValidCpe22(propvalu):
1460
+ logger.debug(f'Queueing invalid v2_2 value for deletion iden={s_common.ehex(buid)} valu={propvalu}')
1461
+ await self._queueEdit(
1462
+ layer.iden,
1463
+ (buid, 'it:sec:cpe', (
1464
+ (s_layer.EDIT_PROP_DEL, ('v2_2', propvalu, stortype), ()),
1465
+ ))
1466
+ )
1467
+ else:
1468
+ verdict = 'migrate'
1469
+
1470
+ if (formvalu := sode.get('valu')) is None:
1471
+ continue
1472
+
1473
+ formvalu = formvalu[0]
1474
+ if s_infotech.isValidCpe23(formvalu):
1475
+ continue
1476
+
1477
+ node = self.getNode(buid)
1478
+ node['formvalu'] = formvalu
1479
+ node['formname'] = 'it:sec:cpe'
1480
+ node['verdict'] = verdict
1481
+ layers = list(node['layers'])
1482
+ layers.append(layer.iden)
1483
+ node['layers'] = layers
1484
+
1485
+ await self.nodes.set(buid, node)
1486
+
1487
+ await self._flushEdits()
1488
+
1489
+ invalid = len(self.nodes)
1490
+ logger.info(f'Processing {invalid} invalid it:sec:cpe nodes in {len(self.layers)} layers')
1491
+
1492
+ # Pick up all related CPE node info. The majority of the work happens in this loop
1493
+ for idx, layer in enumerate(self.layers):
1494
+ logger.debug('Processing nodes in layer %s %s', idx, layer.iden)
1495
+
1496
+ for buid, node in self.nodes.items():
1497
+ await self._loadNode(layer, buid, node=node)
1498
+
1499
+ formvalu = node.get('formvalu')
1500
+ formname = node.get('formname')
1501
+ formndef = (formname, formvalu)
1502
+
1503
+ refs = node['refs'].get(layer.iden, [])
1504
+
1505
+ for refinfo in self.getRefInfo(formname):
1506
+ (refform, refprop, reftype, isarray, isro) = refinfo
1507
+
1508
+ if reftype == 'ndef':
1509
+ propvalu = formndef
1510
+ else:
1511
+ propvalu = formvalu
1512
+
1513
+ async for refbuid, refsode in self.getSodeByPropValuNoNorm(layer, refform, refprop, propvalu):
1514
+ # Save the reference info
1515
+ refs.append((s_common.ehex(refbuid), refinfo))
1516
+
1517
+ # Add a todo to get valu and refs to the new nodes
1518
+ await self.todos.add(('getvalu', (refbuid, True)))
1519
+
1520
+ if refs:
1521
+ node['refs'][layer.iden] = refs
1522
+
1523
+ await self.nodes.set(buid, node)
1524
+
1525
+ logger.info('Processing invalid it:sec:cpe node references (this may happen multiple times)')
1526
+
1527
+ # Collect sources, direct references, second-degree references, etc.
1528
+ while len(self.todos):
1529
+ # Copy the list of todos and then clear the original list. This makes it so we will process all the todos
1530
+ # but we can add new todos (that will iterate over all the layers) below to gather supporting data as
1531
+ # needed.
1532
+ todotmp = await self.todos.copy()
1533
+ await self.todos.clear()
1534
+
1535
+ for idx, layer in enumerate(self.layers):
1536
+ logger.debug('Processing references in layer %s %s', idx, layer.iden)
1537
+
1538
+ async for entry in todotmp:
1539
+ match entry:
1540
+ case ('getvalu', (buid, fullnode)):
1541
+ if fullnode:
1542
+ node = await self._loadNode(layer, buid)
1543
+ formvalu = node.get('formvalu')
1544
+ if formvalu is None:
1545
+ continue
1546
+
1547
+ formname = node.get('formname')
1548
+
1549
+ await self.todos.add(('getrefs', (buid, formname, formvalu)))
1550
+ else:
1551
+ sode = await layer.getStorNode(buid)
1552
+
1553
+ if (formvalu := sode.get('valu')) is None:
1554
+ continue
1555
+
1556
+ formvalu = formvalu[0]
1557
+ formname = sode.get('form')
1558
+
1559
+ node = self.getNode(buid)
1560
+ node['formvalu'] = formvalu
1561
+ node['formname'] = formname
1562
+ layers = list(node['layers'])
1563
+ layers.append(layer.iden)
1564
+ node['layers'] = layers
1565
+
1566
+ await self.nodes.set(buid, node)
1567
+
1568
+ case ('getrefs', (buid, formname, formvalu)):
1569
+
1570
+ node = self.getNode(buid)
1571
+ formndef = (formname, formvalu)
1572
+
1573
+ node.setdefault('refs', {})
1574
+ refs = node['refs'].get(layer.iden, [])
1575
+
1576
+ for refinfo in self.getRefInfo(formname):
1577
+ (refform, refprop, reftype, isarray, isro) = refinfo
1578
+
1579
+ if reftype == 'ndef':
1580
+ propvalu = formndef
1581
+ else:
1582
+ propvalu = formvalu
1583
+
1584
+ async for refbuid, refsode in self.getSodeByPropValuNoNorm(layer, refform, refprop, propvalu):
1585
+ # Save the reference info
1586
+ refs.append((s_common.ehex(refbuid), refinfo))
1587
+
1588
+ # Add a todo to get valu and refs to the new nodes
1589
+ await self.todos.add(('getvalu', (refbuid, True)))
1590
+
1591
+ if refs:
1592
+ node['refs'][layer.iden] = refs
1593
+
1594
+ await self.nodes.set(buid, node)
1595
+
1596
+ await todotmp.fini()
1597
+
1598
+ logger.info(f'Migrating/removing {invalid} invalid it:sec:cpe nodes')
1599
+
1600
+ count = 0
1601
+ removed = 0
1602
+ migrated = 0
1603
+ for buid, node in self.nodes.items():
1604
+ action = node.get('verdict')
1605
+
1606
+ if action is None:
1607
+ continue
1608
+
1609
+ if action == 'migrate':
1610
+ propvalu = None
1611
+ for layriden, sode in node.get('sodes').items():
1612
+ props = sode.get('props', {})
1613
+ propvalu, stortype = props.get('v2_2', (None, None))
1614
+ if propvalu is not None:
1615
+ break
1616
+
1617
+ if propvalu is None:
1618
+ # We didn't find a v2_2 value so remove this node
1619
+ await self.removeNode(buid)
1620
+ removed += 1
1621
+
1622
+ else:
1623
+ # We did find a v2_2 value so try to norm it and use that new value to move the node. If this fails,
1624
+ # remove the node.
1625
+ try:
1626
+ newvalu, _ = form.type.norm(propvalu)
1627
+
1628
+ except s_exc.BadTypeValu:
1629
+ logger.debug('Unexpectedly encountered invalid v2_2 prop: iden=%s valu=%s', s_common.ehex(buid), propvalu)
1630
+ await self.removeNode(buid)
1631
+ removed += 1
1632
+
1633
+ else:
1634
+ await self.moveNode(buid, newvalu)
1635
+ migrated += 1
1636
+
1637
+ elif action == 'remove':
1638
+ newvalu = None
1639
+ # Before removing the node, iterate over the sodes looking for a good :v2_2 value
1640
+ for layriden, sode in node.get('sodes').items():
1641
+ props = sode.get('props', {})
1642
+ propvalu, stortype = props.get('v2_2', (None, None))
1643
+ if propvalu is None:
1644
+ continue
1645
+
1646
+ # This prop is going to be the new primary value so delete the secondary prop
1647
+ await self.editPropDel(layriden, buid, 'it:sec:cpe', 'v2_2', propvalu, stortype)
1648
+
1649
+ # We did find a v2_2 value so try to norm it and use that new value to move the node. If this fails,
1650
+ # remove the node.
1651
+ try:
1652
+ newvalu, _ = form.type.norm(propvalu)
1653
+ except s_exc.BadTypeValu:
1654
+ logger.debug('Unexpectedly encountered invalid v2_2 prop: iden=%s valu=%s', s_common.ehex(buid), propvalu)
1655
+ continue
1656
+
1657
+ # Oh yeah! Migrate the node instead of removing it
1658
+ await self.moveNode(buid, newvalu)
1659
+
1660
+ migrated += 1
1661
+ break
1662
+
1663
+ else:
1664
+ await self.removeNode(buid)
1665
+ removed += 1
1666
+
1667
+ count = migrated + removed
1668
+ if count % 1000 == 0: # pragma: no cover
1669
+ logger.info(f'Processed {count} it:sec:cpe nodes')
1670
+
1671
+ await self._flushEdits()
1672
+
1673
+ logger.info(f'Finished processing {count} it:sec:cpe nodes: {migrated} migrated, {removed} removed')
1674
+
1675
+ await self.todos.fini()
1676
+ await self.nodes.fini()
1677
+
1678
+ @s_cache.memoizemethod()
1679
+ def getRoProps(self, formname):
1680
+ roprops = []
1681
+
1682
+ form = self.core.model.form(formname)
1683
+ for propname, prop in form.props.items():
1684
+ if prop.info.get('ro', False):
1685
+ roprops.append(propname)
1686
+
1687
+ return roprops
1688
+
1689
+ @s_cache.memoizemethod()
1690
+ def getRefInfo(self, formname):
1691
+ props = []
1692
+ props.extend(self.core.model.getPropsByType(formname))
1693
+ props.extend(self.core.model.getPropsByType('array'))
1694
+ props.extend(self.core.model.getPropsByType('ndef'))
1695
+
1696
+ props = [k for k in props if k.form.name != formname]
1697
+
1698
+ refinfo = []
1699
+ for prop in props:
1700
+
1701
+ if prop.form.name == formname: # pragma: no cover
1702
+ continue
1703
+
1704
+ proptype = prop.type
1705
+
1706
+ if prop.type.isarray:
1707
+ proptype = prop.type.arraytype
1708
+
1709
+ if proptype.name not in (formname, 'ndef'):
1710
+ continue
1711
+
1712
+ refinfo.append((
1713
+ prop.form.name,
1714
+ prop.name,
1715
+ proptype.name,
1716
+ prop.type.isarray,
1717
+ prop.info.get('ro', False)
1718
+ ))
1719
+
1720
+ return refinfo
1721
+
1722
+ async def removeNode(self, buid):
1723
+ assert self.nodes.has(buid)
1724
+ node = self.getNode(buid)
1725
+
1726
+ await self.storeNode(buid)
1727
+
1728
+ formname = node.get('formname')
1729
+ formvalu = node.get('formvalu')
1730
+ formndef = (formname, formvalu)
1731
+ refs = node.get('refs')
1732
+
1733
+ # Delete references
1734
+ for reflayr, reflist in refs.items():
1735
+ for refiden, refinfo in reflist:
1736
+ refbuid = s_common.uhex(refiden)
1737
+ (refform, refprop, reftype, isarray, isro) = refinfo
1738
+
1739
+ if reftype == 'ndef':
1740
+ propvalu = formndef
1741
+ else:
1742
+ propvalu = formvalu
1743
+
1744
+ if isro:
1745
+ await self.removeNode(refbuid)
1746
+ continue
1747
+
1748
+ refnode = self.getNode(refbuid)
1749
+ refsode = refnode['sodes'].get(reflayr)
1750
+
1751
+ curv, stortype = refsode['props'].get(refprop, (None, None))
1752
+
1753
+ if isarray:
1754
+
1755
+ _curv = curv
1756
+
1757
+ newv = list(_curv).copy()
1758
+
1759
+ while propvalu in newv:
1760
+ newv.remove(propvalu)
1761
+
1762
+ if not newv:
1763
+ await self.editPropDel(reflayr, refbuid, refform, refprop, curv, stortype)
1764
+
1765
+ else:
1766
+ await self.editPropSet(reflayr, refbuid, refform, refprop, newv, curv, stortype)
1767
+
1768
+ else:
1769
+ await self.editPropDel(reflayr, refbuid, refform, refprop, curv, stortype)
1770
+
1771
+ await self.delNode(buid)
1772
+
1773
+ async def storeNode(self, buid):
1774
+ assert self.nodes.has(buid)
1775
+ node = self.getNode(buid)
1776
+
1777
+ formname = node.get('formname')
1778
+ formvalu = node.get('formvalu')
1779
+
1780
+ sources = set()
1781
+ # Resolve sources
1782
+ n2edges = {}
1783
+ for layriden, edges in node['n2edges'].items():
1784
+ n2edges.setdefault(layriden, [])
1785
+
1786
+ for verb, n2iden in edges:
1787
+ n2buid = s_common.uhex(n2iden)
1788
+ assert self.nodes.has(n2buid)
1789
+ n2node = self.nodes.get(n2buid)
1790
+ if n2node is None: # pragma: no cover
1791
+ continue
1792
+
1793
+ n2edges[layriden].append((verb, n2iden, n2node['formname']))
1794
+
1795
+ if verb == 'seen':
1796
+ formvalu = n2node.get('formvalu')
1797
+ assert formvalu is not None
1798
+ sources.add(formvalu)
1799
+
1800
+ # Make some changes before serializing
1801
+ item = s_msgpack.deepcopy(node)
1802
+ item.pop('verdict', None)
1803
+ item['iden'] = s_common.ehex(buid)
1804
+ item['sources'] = list(sources)
1805
+ item['n2edges'] = n2edges
1806
+
1807
+ roprops = self.getRoProps(formname)
1808
+ for layriden, sode in node['sodes'].items():
1809
+ props = sode.get('props')
1810
+ if props is None: # pragma: no cover
1811
+ continue
1812
+
1813
+ props = {name: valu for name, valu in list(props.items()) if name not in roprops}
1814
+ if props:
1815
+ item['sodes'][layriden]['props'] = props
1816
+ else:
1817
+ item['sodes'][layriden].pop('props')
1818
+
1819
+ if not self.hasq:
1820
+ await self.core.addCoreQueue('model_0_2_31:nodes', {})
1821
+ self.hasq = True
1822
+
1823
+ await self.core.coreQueuePuts('model_0_2_31:nodes', (item,))
1824
+
1825
+ async def getSodeByPropValuNoNorm(self, layer, formname, propname, valu, cmpr='='):
1826
+ prop = self.core.model.reqProp(f'{formname}:{propname}')
1827
+
1828
+ stortype = prop.type.stortype
1829
+
1830
+ # Normally we'd call proptype.getStorCmprs() here to get the cmprvals
1831
+ # but getStorCmprs() calls norm() which we're trying to avoid so build
1832
+ # cmprvals manually here.
1833
+
1834
+ if prop.type.isarray:
1835
+ stortype &= (~s_layer.STOR_FLAG_ARRAY)
1836
+ liftfunc = layer.liftByPropArray
1837
+ else:
1838
+ liftfunc = layer.liftByPropValu
1839
+
1840
+ cmprvals = ((cmpr, valu, stortype),)
1841
+
1842
+ async for _, buid, sode in liftfunc(formname, propname, cmprvals):
1843
+ yield buid, sode
1844
+
1845
+ async def delNode(self, buid):
1846
+ assert self.nodes.has(buid)
1847
+ node = self.getNode(buid)
1848
+
1849
+ formname = node.get('formname')
1850
+ formvalu = node.get('formvalu')
1851
+
1852
+ # Edits
1853
+ for layriden, sode in node['sodes'].items():
1854
+ props = sode.get('props', {}).copy()
1855
+ for propname, propvalu in props.items():
1856
+ propvalu, stortype = propvalu
1857
+ await self.editPropDel(layriden, buid, formname, propname, propvalu, stortype)
1858
+
1859
+ tags = sode.get('tags', {})
1860
+ for tagname, tagvalu in tags.items():
1861
+ await self.editTagDel(layriden, buid, formname, tagname, tagvalu)
1862
+
1863
+ tagprops = sode.get('tagprops', {})
1864
+ for tagname, propvalus in tagprops.items():
1865
+ for propname, propvalu in propvalus.items():
1866
+ propvalu, stortype = propvalu
1867
+ await self.editTagpropDel(layriden, buid, formname, tagname, propname, propvalu, stortype)
1868
+
1869
+ # Nodedata
1870
+ for layriden, data in node['nodedata'].items():
1871
+ for name, valu in data:
1872
+ await self.editNodedataDel(layriden, buid, formname, name, valu)
1873
+
1874
+ # Edges
1875
+ for layriden, edges in node['n1edges'].items():
1876
+ for verb, iden in edges:
1877
+ await self.editEdgeDel(layriden, buid, formname, verb, iden)
1878
+
1879
+ for layriden, edges in node['n2edges'].items():
1880
+ for verb, iden in edges:
1881
+ n2buid = s_common.uhex(iden)
1882
+
1883
+ n2node = self.nodes.get(n2buid)
1884
+ if n2node is None: # pragma: no cover
1885
+ continue
1886
+
1887
+ n2form = n2node.get('formname')
1888
+ await self.editEdgeDel(layriden, n2buid, n2form, verb, s_common.ehex(buid))
1889
+
1890
+ # Node
1891
+ await self.editNodeDel(layriden, buid, formname, formvalu)
1892
+
1893
+ async def moveNode(self, buid, newvalu):
1894
+ assert self.nodes.has(buid)
1895
+ node = self.getNode(buid)
1896
+
1897
+ formname = node.get('formname')
1898
+ formvalu = node.get('formvalu')
1899
+ refs = node.get('refs')
1900
+
1901
+ oldndef = (formname, formvalu)
1902
+ newndef = (formname, newvalu)
1903
+ newbuid = s_common.buid((formname, newvalu))
1904
+
1905
+ form = self.core.model.reqForm(formname)
1906
+
1907
+ # Node
1908
+ for layriden in node['layers']:
1909
+ # Create the new node in the same layers as the old node
1910
+ await self.editNodeAdd(layriden, newbuid, formname, newvalu, form.type.stortype)
1911
+
1912
+ # Edits
1913
+ for layriden, sode in node['sodes'].items():
1914
+ props = sode.get('props', {})
1915
+ for propname, propvalu in props.items():
1916
+ propvalu, stortype = propvalu
1917
+ await self.editPropSet(layriden, newbuid, formname, propname, propvalu, None, stortype)
1918
+
1919
+ tags = sode.get('tags', {})
1920
+ for tagname, tagvalu in tags.items():
1921
+ await self.editTagSet(layriden, newbuid, formname, tagname, tagvalu, None)
1922
+
1923
+ tagprops = sode.get('tagprops', {})
1924
+ for tagname, propvalus in tagprops.items():
1925
+ for propname, propvalu in propvalus.items():
1926
+ propvalu, stortype = propvalu
1927
+
1928
+ await self.editTagpropSet(layriden, newbuid, formname, tagname, propname, propvalu, None, stortype)
1929
+
1930
+ # Nodedata
1931
+ for layriden, data in node['nodedata'].items():
1932
+ for name, valu in data:
1933
+ await self.editNodedataSet(layriden, newbuid, formname, name, valu, None)
1934
+
1935
+ # Edges
1936
+ for layriden, edges in node['n1edges'].items():
1937
+ for verb, iden in edges:
1938
+ await self.editEdgeAdd(layriden, newbuid, formname, verb, iden)
1939
+
1940
+ for layriden, edges in node['n2edges'].items():
1941
+ for verb, iden in edges:
1942
+ n2buid = s_common.uhex(iden)
1943
+
1944
+ n2node = self.nodes.get(n2buid)
1945
+ if n2node is None: # pragma: no cover
1946
+ continue
1947
+
1948
+ n2form = n2node.get('formname')
1949
+ await self.editEdgeAdd(layriden, n2buid, n2form, verb, s_common.ehex(newbuid))
1950
+
1951
+ # Move references
1952
+ for reflayr, reflist in refs.items():
1953
+ for refiden, refinfo in reflist:
1954
+ refbuid = s_common.uhex(refiden)
1955
+ (refform, refprop, reftype, isarray, isro) = refinfo
1956
+
1957
+ if isro:
1958
+ await self.removeNode(refbuid)
1959
+ continue
1960
+
1961
+ if reftype == 'ndef':
1962
+ oldpropv = oldndef
1963
+ newpropv = newndef
1964
+ else:
1965
+ oldpropv = formvalu
1966
+ newpropv = newvalu
1967
+
1968
+ refnode = self.getNode(refbuid)
1969
+ refsode = refnode['sodes'].get(reflayr)
1970
+
1971
+ curv, stortype = refsode.get('props', {}).get(refprop, (None, None))
1972
+ assert stortype is not None
1973
+
1974
+ if isarray:
1975
+
1976
+ _curv = curv
1977
+
1978
+ newv = list(_curv).copy()
1979
+
1980
+ while oldpropv in newv:
1981
+ newv.remove(oldpropv)
1982
+
1983
+ newv.append(newpropv)
1984
+
1985
+ await self.editPropSet(reflayr, refbuid, refform, refprop, newv, curv, stortype)
1986
+
1987
+ else:
1988
+ await self.editPropSet(reflayr, refbuid, refform, refprop, newpropv, curv, stortype)
1989
+
1990
+ await self.delNode(buid)