python-watcher 16.0.0__py3-none-any.whl → 16.0.1__py3-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.
- {python_watcher-16.0.0.dist-info → python_watcher-16.0.1.dist-info}/METADATA +1 -1
- {python_watcher-16.0.0.dist-info → python_watcher-16.0.1.dist-info}/RECORD +26 -26
- {python_watcher-16.0.0.dist-info → python_watcher-16.0.1.dist-info}/WHEEL +1 -1
- python_watcher-16.0.1.dist-info/pbr.json +1 -0
- watcher/common/service.py +2 -2
- watcher/conf/grafana_client.py +1 -0
- watcher/conf/maas_client.py +1 -0
- watcher/decision_engine/model/collector/base.py +10 -6
- watcher/decision_engine/model/notification/base.py +22 -0
- watcher/decision_engine/model/notification/cinder.py +4 -4
- watcher/decision_engine/model/notification/nova.py +1 -1
- watcher/decision_engine/strategy/strategies/workload_balance.py +81 -30
- watcher/tests/unit/common/test_service.py +22 -0
- watcher/tests/unit/decision_engine/cluster/test_cluster_data_model_collector.py +74 -0
- watcher/tests/unit/decision_engine/model/notification/test_notifications.py +1 -1
- watcher/tests/unit/decision_engine/model/notification/test_nova_notifications.py +17 -2
- watcher/tests/unit/decision_engine/strategy/strategies/test_workload_balance.py +22 -8
- python_watcher-16.0.0.dist-info/pbr.json +0 -1
- {python_watcher-16.0.0.data → python_watcher-16.0.1.data}/data/etc/apache2/watcher +0 -0
- {python_watcher-16.0.0.data → python_watcher-16.0.1.data}/data/etc/watcher/README-watcher.conf.txt +0 -0
- {python_watcher-16.0.0.data → python_watcher-16.0.1.data}/data/etc/watcher/oslo-config-generator/watcher.conf +0 -0
- {python_watcher-16.0.0.data → python_watcher-16.0.1.data}/data/etc/watcher/oslo-policy-generator/watcher-policy-generator.conf +0 -0
- {python_watcher-16.0.0.data → python_watcher-16.0.1.data}/scripts/watcher-api-wsgi +0 -0
- {python_watcher-16.0.0.dist-info → python_watcher-16.0.1.dist-info}/entry_points.txt +0 -0
- {python_watcher-16.0.0.dist-info → python_watcher-16.0.1.dist-info}/licenses/AUTHORS +0 -0
- {python_watcher-16.0.0.dist-info → python_watcher-16.0.1.dist-info}/licenses/LICENSE +0 -0
- {python_watcher-16.0.0.dist-info → python_watcher-16.0.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-watcher
|
|
3
|
-
Version: 16.0.
|
|
3
|
+
Version: 16.0.1
|
|
4
4
|
Summary: OpenStack Watcher provides a flexible and scalable resource optimization service for multi-tenant OpenStack-based clouds.
|
|
5
5
|
Home-page: https://docs.openstack.org/watcher/latest/
|
|
6
6
|
Author: OpenStack
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
python_watcher-16.0.
|
|
2
|
-
python_watcher-16.0.
|
|
3
|
-
python_watcher-16.0.
|
|
4
|
-
python_watcher-16.0.
|
|
5
|
-
python_watcher-16.0.
|
|
6
|
-
python_watcher-16.0.
|
|
7
|
-
python_watcher-16.0.
|
|
1
|
+
python_watcher-16.0.1.data/data/etc/apache2/watcher,sha256=urybYUTb0kAVIvbm4WK5oDfXR82QSK2lyMl4Z9dCz4Q,1190
|
|
2
|
+
python_watcher-16.0.1.data/data/etc/watcher/README-watcher.conf.txt,sha256=EIa_sjXDyl1XSg0ovsFwUjqw0euS0N2PYn4HtyBGJME,131
|
|
3
|
+
python_watcher-16.0.1.data/data/etc/watcher/oslo-config-generator/watcher.conf,sha256=ZN93-gX9IPi8a2_vO_Wl_GRjafnO2Xm7oEk2ou9Fv04,486
|
|
4
|
+
python_watcher-16.0.1.data/data/etc/watcher/oslo-policy-generator/watcher-policy-generator.conf,sha256=ZfIuVdtrj7IZm2BrRQvAW3JOL_kqCiILf-vBmwX3ufw,76
|
|
5
|
+
python_watcher-16.0.1.data/scripts/watcher-api-wsgi,sha256=YcIpUO3iRVW6Fu3Md_ooAeHkmJJasG031naMdf8cv2A,1829
|
|
6
|
+
python_watcher-16.0.1.dist-info/licenses/AUTHORS,sha256=A38NQqsvLAj9OTI09_UiZ7Wv0Bt-XmN2Ie-oVWyLMSo,8276
|
|
7
|
+
python_watcher-16.0.1.dist-info/licenses/LICENSE,sha256=XfKg2H1sVi8OoRxoisUlMqoo10TKvHmU_wU39ks7MyA,10143
|
|
8
8
|
watcher/__init__.py,sha256=Vs2olXPZ_IAV1rukHYRl3ITzYFBNXX9mIUtFXkMWYwg,640
|
|
9
9
|
watcher/_i18n.py,sha256=fmiL4Nl4XcrhO07jHIk8fkzPaJBuK6pvYlNlSty2p_c,1269
|
|
10
10
|
watcher/eventlet.py,sha256=z6WuDS9HJAq6vRZCg5ozYpWCs3lwhDuv9R70bj8tpQ0,1476
|
|
@@ -92,7 +92,7 @@ watcher/common/placement_helper.py,sha256=WtXNYCTRbs1JmrnHUXzjO3ebEyusnxLXHyeUSw
|
|
|
92
92
|
watcher/common/policy.py,sha256=8WGBY79RJItBN4RphBEacYj7JKtSHJLoVw3WW7oM5oA,4893
|
|
93
93
|
watcher/common/rpc.py,sha256=eM0yHwmMDKHq29oualMp3lbVhEol2ffGTZdIJ9CgO_4,4357
|
|
94
94
|
watcher/common/scheduling.py,sha256=tHf3qI1nZ_bJnC3QXkD7zM_ld7zkeGTNbuXrAP9PWTY,2186
|
|
95
|
-
watcher/common/service.py,sha256=
|
|
95
|
+
watcher/common/service.py,sha256=kdCLbEleIK1QCemedEYfb1djMwmmqK0J00AP3HHMj4k,13688
|
|
96
96
|
watcher/common/service_manager.py,sha256=KT-sKrQqhoTX1QSlmPcPZo0OJYnoykEp2yxOJd1XTVk,1387
|
|
97
97
|
watcher/common/utils.py,sha256=GC6rkrQlt5xmMhkdKogksXNzrQgvCbY6QNp-roFwnZ0,5948
|
|
98
98
|
watcher/common/loader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -128,11 +128,11 @@ watcher/conf/db.py,sha256=TkdSUfqmq3s7M02lHhg2F3WNw_KVHXQtgYo-Quyz1J8,1317
|
|
|
128
128
|
watcher/conf/decision_engine.py,sha256=A1ZfeKdKCIAJ1B8122FHA3sCBiafVBy7fVezIniZ-KQ,4030
|
|
129
129
|
watcher/conf/exception.py,sha256=k6BRqb7z27gndxuZffATm0b8qACzh1co2luJy3AJx2w,963
|
|
130
130
|
watcher/conf/gnocchi_client.py,sha256=NXD_ZSTobVOawUtXzd5uo4yn7cbSJbzFe9wNt9LcW-4,1563
|
|
131
|
-
watcher/conf/grafana_client.py,sha256=
|
|
131
|
+
watcher/conf/grafana_client.py,sha256=7JaaCyImzgwG5m5zZoKqyFUE0_txdx5J6QcyYWSjpv4,6910
|
|
132
132
|
watcher/conf/grafana_translators.py,sha256=-7I2lYtm6MGmDGteFR9TDWWyTby29vd8pLnWROAvYi8,1625
|
|
133
133
|
watcher/conf/ironic_client.py,sha256=6dItPh2QBVvbWmejnC_oZr4W_vEEi1wDhp74Cu_tCfo,1561
|
|
134
134
|
watcher/conf/keystone_client.py,sha256=mbMBo4nhZoKngAQNbqXvrzh1W3opULQ-4ETgFMk_ZGo,1324
|
|
135
|
-
watcher/conf/maas_client.py,sha256=
|
|
135
|
+
watcher/conf/maas_client.py,sha256=99UaXMNg_1GPyfU2q4TjjHpRmI6zAStcrYzvo6lDyyg,1305
|
|
136
136
|
watcher/conf/models.py,sha256=Fo1zQMcUjtEIroMZ4mx3kRLr2A_-3JldMZJQZ7SKIko,1390
|
|
137
137
|
watcher/conf/nova.py,sha256=4fjc72SVNVlWoVZfXvX6HBaZu4SkuBgLbi3Mb9jc2nU,4143
|
|
138
138
|
watcher/conf/nova_client.py,sha256=IWvE0jWfqD7ttpr00hVJhHBXyP4fVo6a7rI53k6ySBo,2764
|
|
@@ -208,7 +208,7 @@ watcher/decision_engine/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
|
|
|
208
208
|
watcher/decision_engine/model/base.py,sha256=fKQtaGLAaT0OaLxObKMa_MFVNPO_os9E3cYFyWyORSU,1097
|
|
209
209
|
watcher/decision_engine/model/model_root.py,sha256=iDMGHBmJuoB5R04jRVIFGJPFSDRum2oCphcJE2e6SKA,24893
|
|
210
210
|
watcher/decision_engine/model/collector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
211
|
-
watcher/decision_engine/model/collector/base.py,sha256=
|
|
211
|
+
watcher/decision_engine/model/collector/base.py,sha256=A4TyXdlwvKIABvR5sDO_GkCPWoDqYS9nlHvCSFnoCWY,9891
|
|
212
212
|
watcher/decision_engine/model/collector/cinder.py,sha256=nlkdeBdnsV4OcDzYx18IQ_G9mNfh-bSfB7yKkPjeJX4,11797
|
|
213
213
|
watcher/decision_engine/model/collector/ironic.py,sha256=BPMLbsilwwxl2JihK7Kbxx82FAAvrF5tS7DSKrNRAT4,4229
|
|
214
214
|
watcher/decision_engine/model/collector/manager.py,sha256=kaX4iX9JbpXdp_aQbMTXL-B_D0ohm0OAl8cwjOpOSa4,3229
|
|
@@ -222,10 +222,10 @@ watcher/decision_engine/model/element/node.py,sha256=vuyb6BsF61a2Zx-wskFoK2T5A3E
|
|
|
222
222
|
watcher/decision_engine/model/element/storage_resource.py,sha256=SxEANRSpzOoN08hzVip-Tw-zB6CGea2iIZRRFHHsAQI,902
|
|
223
223
|
watcher/decision_engine/model/element/volume.py,sha256=Jsscli7Y4hYzNgBB1e1xR7_UNcSiEVjiVuarkT61NGk,1830
|
|
224
224
|
watcher/decision_engine/model/notification/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
225
|
-
watcher/decision_engine/model/notification/base.py,sha256=
|
|
226
|
-
watcher/decision_engine/model/notification/cinder.py,sha256=
|
|
225
|
+
watcher/decision_engine/model/notification/base.py,sha256=I5ot0ycC3MKtAJ4WAxTGRpW5UrEWzLiT3bq0WHHXkgw,1796
|
|
226
|
+
watcher/decision_engine/model/notification/cinder.py,sha256=DsD7M0lZ4luJtzx4j7OMRzpxg4KHb9Zu1s8fIF7WHpk,15045
|
|
227
227
|
watcher/decision_engine/model/notification/filtering.py,sha256=XS7l9bSnM-UEYWxqeIxAhQHHlDdsVtRH1ZyqDdy3n3Q,2861
|
|
228
|
-
watcher/decision_engine/model/notification/nova.py,sha256=
|
|
228
|
+
watcher/decision_engine/model/notification/nova.py,sha256=x7kGz1QZy7FiUfXbZnlgdk1nznk-jgboaYSODlD-qgE,15231
|
|
229
229
|
watcher/decision_engine/planner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
230
230
|
watcher/decision_engine/planner/base.py,sha256=BqyOPXKt5LVyiyAqEtOqvxojSi_NWtMTaMYPsfEPA9g,3043
|
|
231
231
|
watcher/decision_engine/planner/manager.py,sha256=vznpcCBGnAChEevX_X32v0wuBuNlsYigV4Qc7GF3PYY,992
|
|
@@ -272,7 +272,7 @@ watcher/decision_engine/strategy/strategies/saving_energy.py,sha256=dpl5vjXlFCd1
|
|
|
272
272
|
watcher/decision_engine/strategy/strategies/storage_capacity_balance.py,sha256=7wOCAlSlBccRew6uzlZSU4NNsyN1aW44163DMLuLgpI,15588
|
|
273
273
|
watcher/decision_engine/strategy/strategies/uniform_airflow.py,sha256=f1TERvtj7ZcL9NOUcQX2FYd77o4aFWRsjOj3YxdcxS0,13345
|
|
274
274
|
watcher/decision_engine/strategy/strategies/vm_workload_consolidation.py,sha256=cQLAppqzAGskGEE8VkJFGHzEA6XAPqyoXtg9OmzghXw,27135
|
|
275
|
-
watcher/decision_engine/strategy/strategies/workload_balance.py,sha256=
|
|
275
|
+
watcher/decision_engine/strategy/strategies/workload_balance.py,sha256=taEvebcCZL7s-9z2goGHHOpP5PUE8JktfDRkrFdv04I,17596
|
|
276
276
|
watcher/decision_engine/strategy/strategies/workload_stabilization.py,sha256=0Sg_WSEHbypRuclkDRC9rLAzH6v3T-wr_lMy4jtEU1U,25392
|
|
277
277
|
watcher/decision_engine/strategy/strategies/zone_migration.py,sha256=Uj1hLXQO2o67OkVF-6BZDx46wJUIpBk4ShIxcqBQWKo,36168
|
|
278
278
|
watcher/hacking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -377,7 +377,7 @@ watcher/tests/unit/common/test_nova_helper.py,sha256=3Hqu1IZ20RGg3foFgV3YpyNLbSX
|
|
|
377
377
|
watcher/tests/unit/common/test_oslo_service_helper.py,sha256=X3M23ld-l4dB3HeGf-QGYUGvGWq0pvrbMjtu5OWYO94,1682
|
|
378
378
|
watcher/tests/unit/common/test_placement_helper.py,sha256=wXxvNjwCVerTC8uyjbHXgSHJ4Ajr5pCYP2FexlyXSFc,11740
|
|
379
379
|
watcher/tests/unit/common/test_scheduling.py,sha256=qKGYuwBmTit0tmAPNAfxSfoWh67cnlfOKdde9shq9SI,2240
|
|
380
|
-
watcher/tests/unit/common/test_service.py,sha256=
|
|
380
|
+
watcher/tests/unit/common/test_service.py,sha256=4BQ--qhdBoDqnVxcHltJP3YLya1JQb6KbHjU-d726SQ,16117
|
|
381
381
|
watcher/tests/unit/common/test_utils.py,sha256=P718Ob2VG9ps3rl2go_SKTeL3lYI-AKL1YfOxuLLUno,1702
|
|
382
382
|
watcher/tests/unit/common/utils.py,sha256=zkkQeeR9vmE7drsvGMN8q4sXbluCi0KSL23A2puOtx8,6169
|
|
383
383
|
watcher/tests/unit/common/loader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -419,7 +419,7 @@ watcher/tests/unit/decision_engine/audit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
|
419
419
|
watcher/tests/unit/decision_engine/audit/test_audit_handlers.py,sha256=wVTW722VhoWNx17jEBYyf6ZHu5bF7LBG7uouC_8NwZY,25408
|
|
420
420
|
watcher/tests/unit/decision_engine/cluster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
421
421
|
watcher/tests/unit/decision_engine/cluster/test_cinder_cdmc.py,sha256=uPPBs5mpSM0U3csQ14eMw5cMXsALMREHbRyOdtzN9F8,5259
|
|
422
|
-
watcher/tests/unit/decision_engine/cluster/test_cluster_data_model_collector.py,sha256=
|
|
422
|
+
watcher/tests/unit/decision_engine/cluster/test_cluster_data_model_collector.py,sha256=VQ-5NfmpqtzwUCv8wEJwRJZL3E-afBFj_L5pTbxJQfo,5770
|
|
423
423
|
watcher/tests/unit/decision_engine/cluster/test_nova_cdmc.py,sha256=QeMXtRitVMpK7wZGWkPrURVIGQRdnwEK9AKyusRdmpI,32796
|
|
424
424
|
watcher/tests/unit/decision_engine/datasources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
425
425
|
watcher/tests/unit/decision_engine/datasources/test_aetos_helper.py,sha256=xMYx5V-3j-DHodQXTHmQnFWxBCP0hlRknXlQy97X_tU,2138
|
|
@@ -469,8 +469,8 @@ watcher/tests/unit/decision_engine/model/data/storage_scenario_1.xml,sha256=bB_G
|
|
|
469
469
|
watcher/tests/unit/decision_engine/model/notification/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
470
470
|
watcher/tests/unit/decision_engine/model/notification/fake_managers.py,sha256=CTVikb7tmRPcYNPnbRkMZYPljrwloq9szEadb1ueBaE,2287
|
|
471
471
|
watcher/tests/unit/decision_engine/model/notification/test_cinder_notifications.py,sha256=gOCS1hwnv4lNJMxXLa-EMwOIGIuvgLHJxOLxN9hw4kE,30558
|
|
472
|
-
watcher/tests/unit/decision_engine/model/notification/test_notifications.py,sha256
|
|
473
|
-
watcher/tests/unit/decision_engine/model/notification/test_nova_notifications.py,sha256=
|
|
472
|
+
watcher/tests/unit/decision_engine/model/notification/test_notifications.py,sha256=icZH8XjoS8nWFsn6ZjKBx0bsB7_ESKrg_g0kyeDn4Vg,3677
|
|
473
|
+
watcher/tests/unit/decision_engine/model/notification/test_nova_notifications.py,sha256=zq5bnJj0Yj21qI50DmYTY_dYo1hSUKCOQ5mU-LSNkVE,39050
|
|
474
474
|
watcher/tests/unit/decision_engine/model/notification/data/capacity.json,sha256=CA-SuGK3Pu0guK9YKg-bR1a_Ek0QOPCG-EeyqTXYadw,318
|
|
475
475
|
watcher/tests/unit/decision_engine/model/notification/data/instance-create-end.json,sha256=aWw_hqFNqosDisDNqPkwlEwxS3fGWtnu6mnYnuMxy0U,4171
|
|
476
476
|
watcher/tests/unit/decision_engine/model/notification/data/instance-delete-end.json,sha256=sgRs0KW-DGncU59d2e5PvG7CsSPYAwKxnOWeV7bx0BI,1726
|
|
@@ -551,7 +551,7 @@ watcher/tests/unit/decision_engine/strategy/strategies/test_storage_capacity_bal
|
|
|
551
551
|
watcher/tests/unit/decision_engine/strategy/strategies/test_strategy_endpoint.py,sha256=kbScw8qxt_Rw1eb5we5b6PgZ7VeqK007l2ogLddg4lA,2865
|
|
552
552
|
watcher/tests/unit/decision_engine/strategy/strategies/test_uniform_airflow.py,sha256=GLrs8Le9xY7CUaZWbJEYl7vvTOi0X_K4stqLoNRLYWc,7225
|
|
553
553
|
watcher/tests/unit/decision_engine/strategy/strategies/test_vm_workload_consolidation.py,sha256=KU7IDQeF3vspveqFlYqZ-tmtn6cvgqhv2BSJFjnZ5Ec,19154
|
|
554
|
-
watcher/tests/unit/decision_engine/strategy/strategies/test_workload_balance.py,sha256=
|
|
554
|
+
watcher/tests/unit/decision_engine/strategy/strategies/test_workload_balance.py,sha256=FG-FzlaIOPIzUUibGSlG7r_nCcm0oYEnbdTysgsRBYQ,8555
|
|
555
555
|
watcher/tests/unit/decision_engine/strategy/strategies/test_workload_stabilization.py,sha256=U7dMJuSlcXhTB7wsoGl0LHQhpW6pbmUrGHf4J0GrObw,14556
|
|
556
556
|
watcher/tests/unit/decision_engine/strategy/strategies/test_zone_migration.py,sha256=aBsI2pSQJWlC3-qReJvmc7yFtdF-6TLw8Bn_EaHv6iA,85348
|
|
557
557
|
watcher/tests/unit/notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -575,9 +575,9 @@ watcher/tests/unit/objects/test_strategy.py,sha256=xPMHVzmcKaMaCluF7IG891266Ylzz
|
|
|
575
575
|
watcher/tests/unit/objects/utils.py,sha256=K2L0PRggr5_iwPyxLvqonL2y10EMAp2FYnhpJCu78aU,8063
|
|
576
576
|
watcher/wsgi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
577
577
|
watcher/wsgi/api.py,sha256=F1s1Q_CMuRdgS_f7Gl3XzavjZqdxqO9IHLxSWl6NOO4,789
|
|
578
|
-
python_watcher-16.0.
|
|
579
|
-
python_watcher-16.0.
|
|
580
|
-
python_watcher-16.0.
|
|
581
|
-
python_watcher-16.0.
|
|
582
|
-
python_watcher-16.0.
|
|
583
|
-
python_watcher-16.0.
|
|
578
|
+
python_watcher-16.0.1.dist-info/METADATA,sha256=FKxXjHp0iGB7G12hz6QoJ6Dz9IvEy1vdPLgHGHjRw0Q,3895
|
|
579
|
+
python_watcher-16.0.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
580
|
+
python_watcher-16.0.1.dist-info/entry_points.txt,sha256=TAIOgRZMToICTZ6FEGRIRfitQGXlta1r_TUvRc8iv0Y,4542
|
|
581
|
+
python_watcher-16.0.1.dist-info/pbr.json,sha256=8CUskEFHoMYPz_b6C9AEAB_Ppo8tlDtUeeepmzPtMBQ,47
|
|
582
|
+
python_watcher-16.0.1.dist-info/top_level.txt,sha256=O2B7Sbzshq5s-tZxb2a7VETvMDiJItSYieAiJ6Jm5yY,8
|
|
583
|
+
python_watcher-16.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"git_version": "05aa7074", "is_release": true}
|
watcher/common/service.py
CHANGED
|
@@ -240,7 +240,7 @@ class Service(service.ServiceBase):
|
|
|
240
240
|
)
|
|
241
241
|
|
|
242
242
|
def start(self):
|
|
243
|
-
LOG.debug("Connecting to
|
|
243
|
+
LOG.debug("Connecting to messaging transport")
|
|
244
244
|
if self.conductor_topic_handler:
|
|
245
245
|
self.conductor_topic_handler.start()
|
|
246
246
|
if self.notification_handler:
|
|
@@ -249,7 +249,7 @@ class Service(service.ServiceBase):
|
|
|
249
249
|
self.heartbeat.start()
|
|
250
250
|
|
|
251
251
|
def stop(self):
|
|
252
|
-
LOG.debug("Disconnecting from
|
|
252
|
+
LOG.debug("Disconnecting from messaging transport")
|
|
253
253
|
if self.conductor_topic_handler:
|
|
254
254
|
self.conductor_topic_handler.stop()
|
|
255
255
|
if self.notification_handler:
|
watcher/conf/grafana_client.py
CHANGED
|
@@ -28,6 +28,7 @@ GRAFANA_CLIENT_OPTS = [
|
|
|
28
28
|
# A similar structure to the database_map would solve this.
|
|
29
29
|
cfg.StrOpt('token',
|
|
30
30
|
default=None,
|
|
31
|
+
secret=True,
|
|
31
32
|
help="Authentication token to gain access"),
|
|
32
33
|
# TODO(Dantali0n) each individual metric could have its own base url.
|
|
33
34
|
# A similar structure to the database_map would solve this.
|
watcher/conf/maas_client.py
CHANGED
|
@@ -127,6 +127,7 @@ class BaseClusterDataModelCollector(loadable.LoadableSingleton,
|
|
|
127
127
|
super().__init__(config)
|
|
128
128
|
self.osc = osc if osc else clients.OpenStackClients()
|
|
129
129
|
self.lock = threading.RLock()
|
|
130
|
+
self.sync_lock = threading.RLock()
|
|
130
131
|
self._audit_scope_handler = None
|
|
131
132
|
self._cluster_data_model = None
|
|
132
133
|
self._data_model_scope = None
|
|
@@ -188,13 +189,16 @@ class BaseClusterDataModelCollector(loadable.LoadableSingleton,
|
|
|
188
189
|
"""Synchronize the cluster data model
|
|
189
190
|
|
|
190
191
|
Whenever called this synchronization will perform a drop-in replacement
|
|
191
|
-
with the existing cluster data model
|
|
192
|
+
with the existing cluster data model. The sync lock is held for the
|
|
193
|
+
full duration so notification handlers wait until the new model is
|
|
194
|
+
current before applying updates.
|
|
192
195
|
"""
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
with self.sync_lock:
|
|
197
|
+
try:
|
|
198
|
+
self.cluster_data_model = self.execute()
|
|
199
|
+
except Exception as e:
|
|
200
|
+
LOG.exception(e)
|
|
201
|
+
self.set_cluster_data_model_as_stale()
|
|
198
202
|
|
|
199
203
|
|
|
200
204
|
class BaseModelBuilder:
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
|
|
18
18
|
import abc
|
|
19
19
|
|
|
20
|
+
from oslo_log import log
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
LOG = log.getLogger(__name__)
|
|
24
|
+
|
|
20
25
|
|
|
21
26
|
class NotificationEndpoint(metaclass=abc.ABCMeta):
|
|
22
27
|
|
|
@@ -34,3 +39,20 @@ class NotificationEndpoint(metaclass=abc.ABCMeta):
|
|
|
34
39
|
@property
|
|
35
40
|
def cluster_data_model(self):
|
|
36
41
|
return self.collector.cluster_data_model
|
|
42
|
+
|
|
43
|
+
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
44
|
+
"""oslo.messaging entry point.
|
|
45
|
+
|
|
46
|
+
Acquires the collector sync lock before processing to ensure
|
|
47
|
+
notifications are never applied to a model that is about to be
|
|
48
|
+
replaced by an in-progress synchronization.
|
|
49
|
+
"""
|
|
50
|
+
with self.collector.sync_lock:
|
|
51
|
+
self.process_info(
|
|
52
|
+
ctxt, publisher_id, event_type, payload, metadata
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@abc.abstractmethod
|
|
56
|
+
def process_info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
57
|
+
"""Process the notification. Subclasses must implement this."""
|
|
58
|
+
raise NotImplementedError()
|
|
@@ -250,7 +250,7 @@ class CapacityNotificationEndpoint(CinderNotification):
|
|
|
250
250
|
event_type='capacity.pool',
|
|
251
251
|
)
|
|
252
252
|
|
|
253
|
-
def
|
|
253
|
+
def process_info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
254
254
|
ctxt.request_id = metadata['message_id']
|
|
255
255
|
ctxt.project_domain = event_type
|
|
256
256
|
LOG.info("Event '%(event)s' received from %(publisher)s "
|
|
@@ -285,7 +285,7 @@ class VolumeCreateEnd(VolumeNotificationEndpoint):
|
|
|
285
285
|
event_type='volume.create.end',
|
|
286
286
|
)
|
|
287
287
|
|
|
288
|
-
def
|
|
288
|
+
def process_info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
289
289
|
ctxt.request_id = metadata['message_id']
|
|
290
290
|
ctxt.project_domain = event_type
|
|
291
291
|
LOG.info("Event '%(event)s' received from %(publisher)s "
|
|
@@ -314,7 +314,7 @@ class VolumeUpdateEnd(VolumeNotificationEndpoint):
|
|
|
314
314
|
event_type='volume.update.end',
|
|
315
315
|
)
|
|
316
316
|
|
|
317
|
-
def
|
|
317
|
+
def process_info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
318
318
|
ctxt.request_id = metadata['message_id']
|
|
319
319
|
ctxt.project_domain = event_type
|
|
320
320
|
LOG.info("Event '%(event)s' received from %(publisher)s "
|
|
@@ -376,7 +376,7 @@ class VolumeDeleteEnd(VolumeNotificationEndpoint):
|
|
|
376
376
|
event_type='volume.delete.end',
|
|
377
377
|
)
|
|
378
378
|
|
|
379
|
-
def
|
|
379
|
+
def process_info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
380
380
|
ctxt.request_id = metadata['message_id']
|
|
381
381
|
ctxt.project_domain = event_type
|
|
382
382
|
LOG.info("Event '%(event)s' received from %(publisher)s "
|
|
@@ -357,7 +357,7 @@ class VersionedNotification(NovaNotification):
|
|
|
357
357
|
publisher_id=self.publisher_id_regex,
|
|
358
358
|
)
|
|
359
359
|
|
|
360
|
-
def
|
|
360
|
+
def process_info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
361
361
|
LOG.info("Event '%(event)s' received from %(publisher)s "
|
|
362
362
|
"with metadata %(metadata)s",
|
|
363
363
|
dict(event=event_type,
|
|
@@ -192,33 +192,77 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
|
|
|
192
192
|
workload = instance_data['workload']
|
|
193
193
|
# calculate the available resources
|
|
194
194
|
free_res = self.compute_model.get_node_free_resources(host)
|
|
195
|
-
if
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
195
|
+
if free_res['vcpu'] < required_cores:
|
|
196
|
+
LOG.debug(
|
|
197
|
+
"Host %(host)s rejected for instance %(instance)s: "
|
|
198
|
+
"insufficient vCPUs (available: %(available)s, "
|
|
199
|
+
"required: %(required)s)",
|
|
200
|
+
dict(
|
|
201
|
+
host=host.hostname,
|
|
202
|
+
instance=instance_to_migrate.uuid,
|
|
203
|
+
available=free_res['vcpu'],
|
|
204
|
+
required=required_cores,
|
|
205
|
+
),
|
|
206
|
+
)
|
|
207
|
+
continue
|
|
208
|
+
if free_res['memory'] < required_mem:
|
|
209
|
+
LOG.debug(
|
|
210
|
+
"Host %(host)s rejected for instance %(instance)s: "
|
|
211
|
+
"insufficient memory (available: %(available)s MB, "
|
|
212
|
+
"required: %(required)s MB)",
|
|
213
|
+
dict(
|
|
214
|
+
host=host.hostname,
|
|
215
|
+
instance=instance_to_migrate.uuid,
|
|
216
|
+
available=free_res['memory'],
|
|
217
|
+
required=required_mem,
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
continue
|
|
221
|
+
if free_res['disk'] < required_disk:
|
|
222
|
+
LOG.debug(
|
|
223
|
+
"Host %(host)s rejected for instance %(instance)s: "
|
|
224
|
+
"insufficient disk (available: %(available)s GB, "
|
|
225
|
+
"required: %(required)s GB)",
|
|
226
|
+
dict(
|
|
227
|
+
host=host.hostname,
|
|
228
|
+
instance=instance_to_migrate.uuid,
|
|
229
|
+
available=free_res['disk'],
|
|
230
|
+
required=required_disk,
|
|
231
|
+
),
|
|
232
|
+
)
|
|
233
|
+
continue
|
|
234
|
+
if self._meter == 'instance_cpu_usage':
|
|
235
|
+
usage = src_instance_workload + workload
|
|
236
|
+
usage_percent = usage / host.vcpus * 100
|
|
237
|
+
limit = self.threshold / 100 * host.vcpus
|
|
238
|
+
if usage < limit:
|
|
239
|
+
destination_hosts.append(instance_data)
|
|
240
|
+
LOG.debug(
|
|
241
|
+
"Host %s evaluated as destination for %s. "
|
|
242
|
+
"Host usage for cpu would be %s."
|
|
243
|
+
"The threshold is: %s. selected: %s",
|
|
244
|
+
host.hostname,
|
|
245
|
+
instance_to_migrate.uuid,
|
|
246
|
+
usage_percent,
|
|
247
|
+
self.threshold,
|
|
248
|
+
usage < limit,
|
|
249
|
+
)
|
|
250
|
+
if self._meter == 'instance_ram_usage':
|
|
251
|
+
usage = src_instance_workload + workload
|
|
252
|
+
usage_percent = usage / host.memory * 100
|
|
253
|
+
limit = self.threshold / 100 * host.memory
|
|
254
|
+
if usage < limit:
|
|
255
|
+
destination_hosts.append(instance_data)
|
|
256
|
+
LOG.debug(
|
|
257
|
+
"Host %s evaluated as destination for %s. "
|
|
258
|
+
"Host usage for ram would be %s."
|
|
259
|
+
"The threshold is: %s. selected: %s",
|
|
260
|
+
host.hostname,
|
|
261
|
+
instance_to_migrate.uuid,
|
|
262
|
+
usage_percent,
|
|
263
|
+
self.threshold,
|
|
264
|
+
usage < limit,
|
|
265
|
+
)
|
|
222
266
|
return destination_hosts
|
|
223
267
|
|
|
224
268
|
def group_hosts_by_cpu_or_ram_util(self):
|
|
@@ -326,6 +370,10 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
|
|
|
326
370
|
instance_to_migrate = self.choose_instance_to_migrate(
|
|
327
371
|
source_nodes, avg_workload, workload_cache)
|
|
328
372
|
if not instance_to_migrate:
|
|
373
|
+
LOG.debug(
|
|
374
|
+
"No suitable instance found to migrate from "
|
|
375
|
+
"any overloaded source node"
|
|
376
|
+
)
|
|
329
377
|
return self.solution
|
|
330
378
|
source_node, instance_src = instance_to_migrate
|
|
331
379
|
# find the hosts that have enough resource for the VM to be migrated
|
|
@@ -334,9 +382,12 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
|
|
|
334
382
|
# sort the filtered result by workload
|
|
335
383
|
# pick up the lowest one as dest server
|
|
336
384
|
if not destination_hosts:
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
385
|
+
LOG.warning(
|
|
386
|
+
"No proper target host could be found for instance "
|
|
387
|
+
"%(instance)s. Check debug logs for per-host rejection "
|
|
388
|
+
"details.",
|
|
389
|
+
dict(instance=instance_src.uuid),
|
|
390
|
+
)
|
|
340
391
|
return self.solution
|
|
341
392
|
destination_hosts = sorted(destination_hosts,
|
|
342
393
|
key=lambda x: (x[self._meter]))
|
|
@@ -108,6 +108,28 @@ class TestService(base.TestCase):
|
|
|
108
108
|
dummy_service.conductor_topic_handler,
|
|
109
109
|
om.rpc.server.RPCServer)
|
|
110
110
|
|
|
111
|
+
@mock.patch.object(service.LOG, 'debug', autospec=True)
|
|
112
|
+
def test_transport_url_not_logged(self, mock_debug):
|
|
113
|
+
transport_url = 'rabbit://api:secret-pass@example.com:5672/'
|
|
114
|
+
CONF.set_override('transport_url', transport_url)
|
|
115
|
+
|
|
116
|
+
dummy_service = service.Service(DummyManager)
|
|
117
|
+
dummy_service.conductor_topic_handler = None
|
|
118
|
+
dummy_service.notification_handler = None
|
|
119
|
+
dummy_service.heartbeat = None
|
|
120
|
+
|
|
121
|
+
dummy_service.start()
|
|
122
|
+
dummy_service.stop()
|
|
123
|
+
|
|
124
|
+
logged = ' '.join(
|
|
125
|
+
str(arg) for call in mock_debug.call_args_list for arg in call.args
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
self.assertNotIn(transport_url, logged)
|
|
129
|
+
self.assertNotIn('secret-pass', logged)
|
|
130
|
+
mock_debug.assert_any_call("Connecting to messaging transport")
|
|
131
|
+
mock_debug.assert_any_call("Disconnecting from messaging transport")
|
|
132
|
+
|
|
111
133
|
|
|
112
134
|
class TestServiceMonitoringBase(db_base.DbTestCase):
|
|
113
135
|
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
+
import threading
|
|
17
|
+
|
|
16
18
|
from unittest import mock
|
|
17
19
|
|
|
18
20
|
from watcher.decision_engine.model.collector import base
|
|
@@ -20,6 +22,7 @@ from watcher.decision_engine.model.collector import cinder
|
|
|
20
22
|
from watcher.decision_engine.model.collector import ironic
|
|
21
23
|
from watcher.decision_engine.model.collector import nova
|
|
22
24
|
from watcher.decision_engine.model import model_root
|
|
25
|
+
from watcher.decision_engine.model.notification import base as notif_base
|
|
23
26
|
from watcher.tests.unit import base as test_base
|
|
24
27
|
|
|
25
28
|
|
|
@@ -59,6 +62,77 @@ class TestClusterDataModelCollector(test_base.TestCase):
|
|
|
59
62
|
collector.get_latest_cluster_data_model())
|
|
60
63
|
|
|
61
64
|
|
|
65
|
+
class TestSyncLockNotificationRace(test_base.TestCase):
|
|
66
|
+
"""Regression test for notification updates lost during synchronization.
|
|
67
|
+
|
|
68
|
+
When synchronize() is rebuilding the model via execute(), a concurrent
|
|
69
|
+
notification must not apply its update to the old model that will be
|
|
70
|
+
discarded. Without sync_lock in synchronize() and in
|
|
71
|
+
NotificationEndpoint.info(), the notification races execute(), calls
|
|
72
|
+
add_node() on old_model, and the update is silently lost when
|
|
73
|
+
synchronize() replaces _cluster_data_model with new_model.
|
|
74
|
+
|
|
75
|
+
This test fails without the fix and passes once sync_lock is added to
|
|
76
|
+
both synchronize() and NotificationEndpoint.info().
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def test_notification_node_reaches_new_model_not_old(self):
|
|
80
|
+
collector = DummyClusterDataModelCollector(config=mock.Mock())
|
|
81
|
+
|
|
82
|
+
old_model = mock.Mock(spec=model_root.ModelRoot)
|
|
83
|
+
new_model = mock.Mock(spec=model_root.ModelRoot)
|
|
84
|
+
collector._cluster_data_model = old_model
|
|
85
|
+
|
|
86
|
+
execute_started = threading.Event()
|
|
87
|
+
execute_may_finish = threading.Event()
|
|
88
|
+
|
|
89
|
+
def slow_execute():
|
|
90
|
+
# execute start to build a new model
|
|
91
|
+
execute_started.set()
|
|
92
|
+
# Event to trigger the end of execute()
|
|
93
|
+
execute_may_finish.wait(timeout=5)
|
|
94
|
+
return new_model
|
|
95
|
+
|
|
96
|
+
dummy_node = mock.Mock()
|
|
97
|
+
|
|
98
|
+
# Notification endpoint that adds dummy_node to whatever model
|
|
99
|
+
# is current at info() time.
|
|
100
|
+
class AddNodeEndpoint(notif_base.NotificationEndpoint):
|
|
101
|
+
@property
|
|
102
|
+
def filter_rule(self):
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def process_info(
|
|
106
|
+
self, ctxt, publisher_id, event_type, payload, metadata
|
|
107
|
+
):
|
|
108
|
+
self.cluster_data_model.add_node(dummy_node)
|
|
109
|
+
|
|
110
|
+
endpoint = AddNodeEndpoint(collector)
|
|
111
|
+
|
|
112
|
+
with mock.patch.object(collector, 'execute', side_effect=slow_execute):
|
|
113
|
+
sync_thread = threading.Thread(
|
|
114
|
+
target=collector.synchronize, daemon=True
|
|
115
|
+
)
|
|
116
|
+
sync_thread.start()
|
|
117
|
+
execute_started.wait(timeout=3)
|
|
118
|
+
|
|
119
|
+
# Notification fires while execute() is still running.
|
|
120
|
+
notif_thread = threading.Thread(
|
|
121
|
+
target=endpoint.info,
|
|
122
|
+
args=(mock.Mock(), 'pub', 'event', {}, {}),
|
|
123
|
+
daemon=True,
|
|
124
|
+
)
|
|
125
|
+
notif_thread.start()
|
|
126
|
+
|
|
127
|
+
# finish execute() processing
|
|
128
|
+
execute_may_finish.set()
|
|
129
|
+
sync_thread.join(timeout=3)
|
|
130
|
+
notif_thread.join(timeout=3)
|
|
131
|
+
|
|
132
|
+
new_model.add_node.assert_called_once_with(dummy_node)
|
|
133
|
+
old_model.add_node.assert_not_called()
|
|
134
|
+
|
|
135
|
+
|
|
62
136
|
class TestComputeDataModelCollector(test_base.TestCase):
|
|
63
137
|
|
|
64
138
|
def test_model_scope_is_none(self):
|
|
@@ -45,7 +45,7 @@ class DummyNotification(base.NotificationEndpoint):
|
|
|
45
45
|
payload={'data': {'nested': r'^T.*'}},
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
-
def
|
|
48
|
+
def process_info(self, ctxt, publisher_id, event_type, payload, metadata):
|
|
49
49
|
pass
|
|
50
50
|
|
|
51
51
|
|
|
@@ -27,6 +27,7 @@ from watcher.common import exception
|
|
|
27
27
|
from watcher.common import nova_helper
|
|
28
28
|
from watcher.common import placement_helper
|
|
29
29
|
from watcher.common import service as watcher_service
|
|
30
|
+
from watcher.decision_engine.model.collector import base
|
|
30
31
|
from watcher.decision_engine.model import element
|
|
31
32
|
from watcher.decision_engine.model.notification import nova as novanotification
|
|
32
33
|
from watcher.tests.unit import base as base_test
|
|
@@ -111,6 +112,19 @@ class TestReceiveNovaNotifications(NotificationTestCase):
|
|
|
111
112
|
expected_message, self.FAKE_METADATA)
|
|
112
113
|
|
|
113
114
|
|
|
115
|
+
class DummyClusterDataModelCollector(base.BaseClusterDataModelCollector):
|
|
116
|
+
@property
|
|
117
|
+
def notification_endpoints(self):
|
|
118
|
+
return []
|
|
119
|
+
|
|
120
|
+
def get_audit_scope_handler(self, audit_scope):
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
def execute(self):
|
|
124
|
+
# Return none since there are not audits
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
|
|
114
128
|
@ddt.ddt
|
|
115
129
|
class TestNovaNotifications(NotificationTestCase):
|
|
116
130
|
|
|
@@ -906,8 +920,9 @@ class TestNovaNotifications(NotificationTestCase):
|
|
|
906
920
|
def test_info_no_cdm(self):
|
|
907
921
|
# Tests that a notification is received before an audit has been
|
|
908
922
|
# performed which would create the nova CDM.
|
|
909
|
-
|
|
910
|
-
|
|
923
|
+
m_config = mock.Mock()
|
|
924
|
+
_collector = DummyClusterDataModelCollector(config=m_config)
|
|
925
|
+
handler = novanotification.VersionedNotification(_collector)
|
|
911
926
|
payload = {
|
|
912
927
|
'nova_object.data': {
|
|
913
928
|
'uuid': '9966d6bd-a45c-4e1c-9d57-3054899a3ec7',
|
|
@@ -117,10 +117,17 @@ class TestWorkloadBalance(TestBaseStrategy):
|
|
|
117
117
|
'Higher than threshold 25.0: True'),
|
|
118
118
|
mock.call('Host usage for Node_1: host_cpu_usage_percent is 7.5. '
|
|
119
119
|
'Higher than threshold 25.0: False'),
|
|
120
|
-
mock.call(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
mock.call(
|
|
121
|
+
'Host %s evaluated as destination for %s. '
|
|
122
|
+
'Host usage for cpu would be %s.'
|
|
123
|
+
'The threshold is: %s. selected: %s',
|
|
124
|
+
'hostname_1',
|
|
125
|
+
'73b09e16-35b7-4922-804e-e8f5d9b740fc',
|
|
126
|
+
20.0,
|
|
127
|
+
25.0,
|
|
128
|
+
True,
|
|
129
|
+
),
|
|
130
|
+
]
|
|
124
131
|
mock_debug.assert_has_calls(expected_calls, any_order=True)
|
|
125
132
|
|
|
126
133
|
@mock.patch.object(workload_balance.LOG, 'debug', autospec=True)
|
|
@@ -143,10 +150,17 @@ class TestWorkloadBalance(TestBaseStrategy):
|
|
|
143
150
|
'37.121212121212125. Higher than threshold 30.0: True'),
|
|
144
151
|
mock.call('Host usage for Node_1: host_ram_usage_percent is '
|
|
145
152
|
'18.181818181818183. Higher than threshold 30.0: False'),
|
|
146
|
-
mock.call(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
mock.call(
|
|
154
|
+
'Host %s evaluated as destination for %s. '
|
|
155
|
+
'Host usage for ram would be %s.'
|
|
156
|
+
'The threshold is: %s. selected: %s',
|
|
157
|
+
'hostname_1',
|
|
158
|
+
'73b09e16-35b7-4922-804e-e8f5d9b740fc',
|
|
159
|
+
25.0,
|
|
160
|
+
30.0,
|
|
161
|
+
True,
|
|
162
|
+
),
|
|
163
|
+
]
|
|
150
164
|
mock_debug.assert_has_calls(expected_calls, any_order=True)
|
|
151
165
|
|
|
152
166
|
def test_execute_no_workload(self):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"git_version": "336ef360", "is_release": true}
|
|
File without changes
|
{python_watcher-16.0.0.data → python_watcher-16.0.1.data}/data/etc/watcher/README-watcher.conf.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|