55//! Tests basic disk support in the API 
66
77use  super :: instances:: instance_wait_for_state; 
8- use  super :: metrics_querier:: MetricsNotYet ; 
9- use  super :: metrics_querier:: MetricsQuerier ; 
10- use  chrono:: Utc ; 
118use  dropshot:: HttpErrorResponseBody ; 
129use  dropshot:: test_util:: ClientTestContext ; 
1310use  http:: StatusCode ; 
@@ -22,36 +19,30 @@ use nexus_db_queries::db::datastore::RegionAllocationParameters;
2219use  nexus_db_queries:: db:: fixed_data:: FLEET_ID ; 
2320use  nexus_test_utils:: SLED_AGENT_UUID ; 
2421use  nexus_test_utils:: http_testing:: AuthnMode ; 
25- use  nexus_test_utils:: http_testing:: Collection ; 
2622use  nexus_test_utils:: http_testing:: NexusRequest ; 
2723use  nexus_test_utils:: http_testing:: RequestBuilder ; 
2824use  nexus_test_utils:: identity_eq; 
2925use  nexus_test_utils:: resource_helpers:: create_default_ip_pool; 
3026use  nexus_test_utils:: resource_helpers:: create_disk; 
3127use  nexus_test_utils:: resource_helpers:: create_instance; 
32- use  nexus_test_utils:: resource_helpers:: create_instance_with; 
3328use  nexus_test_utils:: resource_helpers:: create_project; 
34- use  nexus_test_utils:: resource_helpers:: objects_list_page_authz; 
35- use  nexus_test_utils:: wait_for_producer; 
3629use  nexus_test_utils_macros:: nexus_test; 
3730use  nexus_types:: external_api:: params; 
3831use  nexus_types:: identity:: Asset ; 
3932use  nexus_types:: silo:: DEFAULT_SILO_ID ; 
33+ use  omicron_common:: api:: external:: ByteCount ; 
4034use  omicron_common:: api:: external:: Disk ; 
4135use  omicron_common:: api:: external:: DiskState ; 
4236use  omicron_common:: api:: external:: IdentityMetadataCreateParams ; 
4337use  omicron_common:: api:: external:: Instance ; 
4438use  omicron_common:: api:: external:: InstanceState ; 
4539use  omicron_common:: api:: external:: Name ; 
4640use  omicron_common:: api:: external:: NameOrId ; 
47- use  omicron_common:: api:: external:: { ByteCount ,  SimpleIdentityOrName  as  _} ; 
4841use  omicron_nexus:: Nexus ; 
4942use  omicron_nexus:: TestInterfaces  as  _; 
5043use  omicron_nexus:: app:: { MAX_DISK_SIZE_BYTES ,  MIN_DISK_SIZE_BYTES } ; 
5144use  omicron_uuid_kinds:: VolumeUuid ; 
5245use  omicron_uuid_kinds:: { GenericUuid ,  InstanceUuid } ; 
53- use  oximeter:: types:: Datum ; 
54- use  oximeter:: types:: Measurement ; 
5546use  sled_agent_client:: TestInterfaces  as  _; 
5647use  std:: collections:: HashSet ; 
5748use  std:: sync:: Arc ; 
@@ -1752,193 +1743,6 @@ async fn test_multiple_disks_multiple_zpools(
17521743    . unwrap ( ) ; 
17531744} 
17541745
1755- async  fn  create_instance_with_disk ( client :  & ClientTestContext )  { 
1756-     create_instance_with ( 
1757-         & client, 
1758-         PROJECT_NAME , 
1759-         INSTANCE_NAME , 
1760-         & params:: InstanceNetworkInterfaceAttachment :: Default , 
1761-         vec ! [ params:: InstanceDiskAttachment :: Attach ( 
1762-             params:: InstanceDiskAttach  {  name:  DISK_NAME . parse( ) . unwrap( )  } , 
1763-         ) ] , 
1764-         Vec :: < params:: ExternalIpCreate > :: new ( ) , 
1765-         true , 
1766-         Default :: default ( ) , 
1767-     ) 
1768-     . await ; 
1769- } 
1770- 
1771- const  ALL_METRICS :  [ & ' static  str ;  6 ]  =
1772-     [ "activated" ,  "read" ,  "write" ,  "read_bytes" ,  "write_bytes" ,  "flush" ] ; 
1773- 
1774- #[ nexus_test]  
1775- async  fn  test_disk_metrics ( cptestctx :  & ControlPlaneTestContext )  { 
1776-     let  metrics_querier = MetricsQuerier :: new ( cptestctx) ; 
1777-     let  client = & cptestctx. external_client ; 
1778-     DiskTest :: new ( & cptestctx) . await ; 
1779-     let  project_id = create_project_and_pool ( client) . await ; 
1780-     let  disk = create_disk ( & client,  PROJECT_NAME ,  DISK_NAME ) . await ; 
1781- 
1782-     // When grabbing a metric, we look for data points going back to the 
1783-     // start of this test all the way up to the current time. 
1784-     let  metric_url = |metric :  & str | { 
1785-         format ! ( 
1786-             "/v1/disks/{}/metrics/{}?start_time={:?}&end_time={:?}&project={}" , 
1787-             DISK_NAME , 
1788-             metric, 
1789-             cptestctx. start_time, 
1790-             Utc :: now( ) , 
1791-             PROJECT_NAME , 
1792-         ) 
1793-     } ; 
1794- 
1795-     // Try accessing metrics before we attach the disk to an instance. 
1796-     // 
1797-     // Observe that no metrics exist yet; no "upstairs" should have been 
1798-     // instantiated on a sled. 
1799-     let  measurements =
1800-         objects_list_page_authz :: < Measurement > ( client,  & metric_url ( "read" ) ) 
1801-             . await ; 
1802-     assert ! ( measurements. items. is_empty( ) ) ; 
1803- 
1804-     metrics_querier
1805-         . wait_for_latest_silo_metric ( 
1806-             "virtual_disk_space_provisioned" , 
1807-             Some ( project_id) , 
1808-             |measurement| { 
1809-                 if  measurement == i64:: from ( disk. size )  { 
1810-                     Ok ( ( ) ) 
1811-                 }  else  { 
1812-                     Err ( MetricsNotYet :: new ( format ! ( 
1813-                         "waiting for virtual_disk_space_provisioned={} \  
1814- , 
1815-                         disk. size, 
1816-                     ) ) ) 
1817-                 } 
1818-             } , 
1819-         ) 
1820-         . await ; 
1821- 
1822-     // Create an instance, attach the disk to it. 
1823-     create_instance_with_disk ( client) . await ; 
1824-     wait_for_producer ( & cptestctx. oximeter ,  disk. id ( ) ) . await ; 
1825- 
1826-     for  metric in  & ALL_METRICS  { 
1827-         metrics_querier
1828-             . wait_for_disk_metric ( PROJECT_NAME ,  DISK_NAME ,  metric,  |items| { 
1829-                 if  items. is_empty ( )  { 
1830-                     return  Err ( MetricsNotYet :: new ( format ! ( 
1831-                         "waiting for at least one item for metric={metric}" 
1832-                     ) ) ) ; 
1833-                 } 
1834-                 for  item in  & items { 
1835-                     let  cumulative = match  item. datum ( )  { 
1836-                         Datum :: CumulativeI64 ( c)  => c, 
1837-                         _ => panic ! ( "Unexpected datum type {:?}" ,  item. datum( ) ) , 
1838-                     } ; 
1839-                     assert ! ( cumulative. start_time( )  <= item. timestamp( ) ) ; 
1840-                 } 
1841-                 Ok ( ( ) ) 
1842-             } ) 
1843-             . await ; 
1844-     } 
1845- 
1846-     // Check the utilization info for the whole project too. 
1847-     metrics_querier
1848-         . wait_for_latest_silo_metric ( 
1849-             "virtual_disk_space_provisioned" , 
1850-             Some ( project_id) , 
1851-             |measurement| { 
1852-                 if  measurement == i64:: from ( disk. size )  { 
1853-                     Ok ( ( ) ) 
1854-                 }  else  { 
1855-                     Err ( MetricsNotYet :: new ( format ! ( 
1856-                         "waiting for virtual_disk_space_provisioned={} \  
1857- , 
1858-                         disk. size, 
1859-                     ) ) ) 
1860-                 } 
1861-             } , 
1862-         ) 
1863-         . await ; 
1864- } 
1865- 
1866- #[ nexus_test]  
1867- async  fn  test_disk_metrics_paginated ( cptestctx :  & ControlPlaneTestContext )  { 
1868-     let  client = & cptestctx. external_client ; 
1869-     DiskTest :: new ( & cptestctx) . await ; 
1870-     create_project_and_pool ( client) . await ; 
1871-     let  disk = create_disk ( & client,  PROJECT_NAME ,  DISK_NAME ) . await ; 
1872-     create_instance_with_disk ( client) . await ; 
1873-     wait_for_producer ( & cptestctx. oximeter ,  disk. id ( ) ) . await ; 
1874- 
1875-     let  metrics_querier = MetricsQuerier :: new ( cptestctx) ; 
1876-     for  metric in  & ALL_METRICS  { 
1877-         // Wait until we have at least two measurements. 
1878-         metrics_querier
1879-             . wait_for_disk_metric ( 
1880-                 PROJECT_NAME , 
1881-                 DISK_NAME , 
1882-                 metric, 
1883-                 |measurements| { 
1884-                     let  num_measurements = measurements. len ( ) ; 
1885-                     if  num_measurements >= 2  { 
1886-                         Ok ( ( ) ) 
1887-                     }  else  { 
1888-                         Err ( MetricsNotYet :: new ( format ! ( 
1889-                             "waiting for at least 2 measurements \  
1890- 
1891-                         ) ) ) 
1892-                     } 
1893-                 } , 
1894-             ) 
1895-             . await ; 
1896- 
1897-         let  collection_url = format ! ( 
1898-             "/v1/disks/{}/metrics/{}?project={}" , 
1899-             DISK_NAME ,  metric,  PROJECT_NAME 
1900-         ) ; 
1901-         let  initial_params = format ! ( 
1902-             "start_time={:?}&end_time={:?}" , 
1903-             cptestctx. start_time, 
1904-             Utc :: now( ) , 
1905-         ) ; 
1906- 
1907-         let  measurements_paginated:  Collection < Measurement >  =
1908-             NexusRequest :: iter_collection_authn ( 
1909-                 client, 
1910-                 & collection_url, 
1911-                 & initial_params, 
1912-                 Some ( 10 ) , 
1913-             ) 
1914-             . await 
1915-             . expect ( "failed to iterate over metrics" ) ; 
1916-         assert ! ( !measurements_paginated. all_items. is_empty( ) ) ; 
1917- 
1918-         let  mut  last_timestamp = None ; 
1919-         let  mut  last_value = None ; 
1920-         for  item in  & measurements_paginated. all_items  { 
1921-             let  cumulative = match  item. datum ( )  { 
1922-                 Datum :: CumulativeI64 ( c)  => c, 
1923-                 _ => panic ! ( "Unexpected datum type {:?}" ,  item. datum( ) ) , 
1924-             } ; 
1925-             assert ! ( cumulative. start_time( )  <= item. timestamp( ) ) ; 
1926- 
1927-             // Validate that the timestamps are non-decreasing. 
1928-             if  let  Some ( last_ts)  = last_timestamp { 
1929-                 assert ! ( last_ts <= item. timestamp( ) ) ; 
1930-             } 
1931-             // Validate that the values increase. 
1932-             if  let  Some ( last_value)  = last_value { 
1933-                 assert ! ( last_value < cumulative. value( ) ) ; 
1934-             } 
1935- 
1936-             last_timestamp = Some ( item. timestamp ( ) ) ; 
1937-             last_value = Some ( cumulative. value ( ) ) ; 
1938-         } 
1939-     } 
1940- } 
1941- 
19421746#[ nexus_test]  
19431747async  fn  test_disk_create_for_importing ( cptestctx :  & ControlPlaneTestContext )  { 
19441748    let  client = & cptestctx. external_client ; 
0 commit comments