50
50
)
51
51
from qiskit_serverless .core .client import BaseClient
52
52
from qiskit_serverless .core .decorators import trace_decorator_factory
53
+ from qiskit_serverless .core .enums import Channel
53
54
from qiskit_serverless .core .files import GatewayFilesClient
54
55
from qiskit_serverless .core .job import (
55
56
Job ,
62
63
)
63
64
64
65
from qiskit_serverless .exception import QiskitServerlessException
66
+ from qiskit_serverless .utils .http import get_headers
65
67
from qiskit_serverless .utils .json import (
66
68
safe_json_request_as_dict ,
67
69
safe_json_request_as_list ,
@@ -95,6 +97,8 @@ def __init__( # pylint: disable=too-many-positional-arguments
95
97
host : Optional [str ] = None ,
96
98
version : Optional [str ] = None ,
97
99
token : Optional [str ] = None ,
100
+ instance : Optional [str ] = None ,
101
+ channel : str = Channel .IBM_QUANTUM .value ,
98
102
verbose : bool = False ,
99
103
):
100
104
"""
@@ -105,6 +109,8 @@ def __init__( # pylint: disable=too-many-positional-arguments
105
109
host: host of gateway
106
110
version: version of gateway
107
111
token: authorization token
112
+ instance: IBM Cloud CRN
113
+ channel: identifies the method to use to authenticate the user
108
114
"""
109
115
name = name or "gateway-client"
110
116
host = host or os .environ .get (ENV_GATEWAY_PROVIDER_HOST )
@@ -121,30 +127,47 @@ def __init__( # pylint: disable=too-many-positional-arguments
121
127
"Authentication credentials must be provided in form of `token`."
122
128
)
123
129
124
- super ().__init__ (name , host , token )
130
+ try :
131
+ channel_enum = Channel (channel )
132
+ except ValueError as error :
133
+ raise QiskitServerlessException (
134
+ "Your channel value is not correct. Use one of the available channels: "
135
+ f"{ Channel .LOCAL .value } , { Channel .IBM_QUANTUM .value } , { Channel .IBM_CLOUD .value } "
136
+ ) from error
137
+
138
+ if channel_enum is Channel .IBM_CLOUD and instance is None :
139
+ raise QiskitServerlessException (
140
+ "Authentication with IBM Cloud requires to pass the CRN as an instance."
141
+ )
142
+
143
+ super ().__init__ (name , host , token , instance )
125
144
self .verbose = verbose
126
145
self .version = version
127
- self ._verify_token ( token )
146
+ self ._verify_credentials ( )
128
147
129
- self ._files_client = GatewayFilesClient (self .host , self .token , self .version )
148
+ self ._files_client = GatewayFilesClient (
149
+ self .host , self .token , self .version , self .instance
150
+ )
130
151
131
152
@classmethod
132
153
def from_dict (cls , dictionary : dict ):
133
154
return ServerlessClient (** dictionary )
134
155
135
- def _verify_token (self , token : str ):
136
- """Verify token ."""
156
+ def _verify_credentials (self ):
157
+ """Verify against the API that the credentials are correct ."""
137
158
try :
138
159
safe_json_request (
139
160
request = lambda : requests .get (
140
161
url = f"{ self .host } /api/v1/programs/" ,
141
- headers = { "Authorization" : f"Bearer { token } " } ,
162
+ headers = get_headers ( token = self . token , instance = self . instance ) ,
142
163
timeout = REQUESTS_TIMEOUT ,
143
164
),
144
165
verbose = self .verbose ,
145
166
)
146
167
except QiskitServerlessException as reason :
147
- raise QiskitServerlessException ("Cannot verify token." ) from reason
168
+ raise QiskitServerlessException (
169
+ "Credentials couldn't be verified."
170
+ ) from reason
148
171
149
172
####################
150
173
####### JOBS #######
@@ -161,7 +184,7 @@ def jobs(self, **kwargs) -> List[Job]:
161
184
request = lambda : requests .get (
162
185
f"{ self .host } /api/{ self .version } /jobs/" ,
163
186
params = kwargs ,
164
- headers = { "Authorization" : f"Bearer { self .token } " } ,
187
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
165
188
timeout = REQUESTS_TIMEOUT ,
166
189
)
167
190
)
@@ -200,7 +223,7 @@ def provider_jobs(self, function: QiskitFunction, **kwargs) -> List[Job]:
200
223
request = lambda : requests .get (
201
224
f"{ self .host } /api/{ self .version } /jobs/provider/" ,
202
225
params = kwargs ,
203
- headers = { "Authorization" : f"Bearer { self .token } " } ,
226
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
204
227
timeout = REQUESTS_TIMEOUT ,
205
228
)
206
229
)
@@ -216,7 +239,7 @@ def job(self, job_id: str) -> Optional[Job]:
216
239
response_data = safe_json_request_as_dict (
217
240
request = lambda : requests .get (
218
241
url ,
219
- headers = { "Authorization" : f"Bearer { self .token } " } ,
242
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
220
243
timeout = REQUESTS_TIMEOUT ,
221
244
)
222
245
)
@@ -266,7 +289,7 @@ def run(
266
289
request = lambda : requests .post (
267
290
url = url ,
268
291
json = data ,
269
- headers = { "Authorization" : f"Bearer { self .token } " } ,
292
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
270
293
timeout = REQUESTS_TIMEOUT ,
271
294
)
272
295
)
@@ -282,7 +305,7 @@ def status(self, job_id: str):
282
305
request = lambda : requests .get (
283
306
f"{ self .host } /api/{ self .version } /jobs/{ job_id } /" ,
284
307
params = {"with_result" : "false" },
285
- headers = { "Authorization" : f"Bearer { self .token } " } ,
308
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
286
309
timeout = REQUESTS_TIMEOUT ,
287
310
)
288
311
)
@@ -302,7 +325,7 @@ def stop(self, job_id: str, service: Optional[QiskitRuntimeService] = None):
302
325
response_data = safe_json_request_as_dict (
303
326
request = lambda : requests .post (
304
327
f"{ self .host } /api/{ self .version } /jobs/{ job_id } /stop/" ,
305
- headers = { "Authorization" : f"Bearer { self .token } " } ,
328
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
306
329
timeout = REQUESTS_TIMEOUT ,
307
330
json = data ,
308
331
)
@@ -315,7 +338,7 @@ def result(self, job_id: str):
315
338
response_data = safe_json_request_as_dict (
316
339
request = lambda : requests .get (
317
340
f"{ self .host } /api/{ self .version } /jobs/{ job_id } /" ,
318
- headers = { "Authorization" : f"Bearer { self .token } " } ,
341
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
319
342
timeout = REQUESTS_TIMEOUT ,
320
343
)
321
344
)
@@ -328,7 +351,7 @@ def logs(self, job_id: str):
328
351
response_data = safe_json_request_as_dict (
329
352
request = lambda : requests .get (
330
353
f"{ self .host } /api/{ self .version } /jobs/{ job_id } /logs/" ,
331
- headers = { "Authorization" : f"Bearer { self .token } " } ,
354
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
332
355
timeout = REQUESTS_TIMEOUT ,
333
356
)
334
357
)
@@ -368,12 +391,22 @@ def upload(self, program: QiskitFunction) -> Optional[RunnableQiskitFunction]:
368
391
if program .image is not None :
369
392
# upload function with custom image
370
393
function_uploaded = _upload_with_docker_image (
371
- program = program , url = url , token = self .token , span = span , client = self
394
+ program = program ,
395
+ url = url ,
396
+ token = self .token ,
397
+ span = span ,
398
+ client = self ,
399
+ instance = self .instance ,
372
400
)
373
401
elif program .entrypoint is not None :
374
402
# upload funciton with artifact
375
403
function_uploaded = _upload_with_artifact (
376
- program = program , url = url , token = self .token , span = span , client = self
404
+ program = program ,
405
+ url = url ,
406
+ token = self .token ,
407
+ span = span ,
408
+ client = self ,
409
+ instance = self .instance ,
377
410
)
378
411
else :
379
412
raise QiskitServerlessException (
@@ -388,7 +421,7 @@ def functions(self, **kwargs) -> List[RunnableQiskitFunction]:
388
421
response_data = safe_json_request_as_list (
389
422
request = lambda : requests .get (
390
423
f"{ self .host } /api/{ self .version } /programs" ,
391
- headers = { "Authorization" : f"Bearer { self .token } " } ,
424
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
392
425
params = kwargs ,
393
426
timeout = REQUESTS_TIMEOUT ,
394
427
)
@@ -417,7 +450,7 @@ def function(
417
450
response_data = safe_json_request_as_dict (
418
451
request = lambda : requests .get (
419
452
f"{ self .host } /api/{ self .version } /programs/get_by_title/{ title } " ,
420
- headers = { "Authorization" : f"Bearer { self .token } " } ,
453
+ headers = get_headers ( token = self .token , instance = self . instance ) ,
421
454
params = {"provider" : provider },
422
455
timeout = REQUESTS_TIMEOUT ,
423
456
)
@@ -511,7 +544,13 @@ class IBMServerlessClient(ServerlessClient):
511
544
client = IBMServerlessClient(token=<INSERT_IBM_QUANTUM_TOKEN>)
512
545
"""
513
546
514
- def __init__ (self , token : Optional [str ] = None , name : Optional [str ] = None ):
547
+ def __init__ (
548
+ self ,
549
+ token : Optional [str ] = None ,
550
+ name : Optional [str ] = None ,
551
+ instance : Optional [str ] = None ,
552
+ channel : str = Channel .IBM_QUANTUM .value ,
553
+ ):
515
554
"""
516
555
Initialize a client with access to an IBMQ-provided remote cluster.
517
556
@@ -528,15 +567,24 @@ def __init__(self, token: Optional[str] = None, name: Optional[str] = None):
528
567
Args:
529
568
token: IBM quantum token
530
569
name: Name of the account to load
570
+ instance: IBM Cloud CRN
571
+ channel: identifies the method to use to authenticate the user
531
572
"""
532
573
token = token or QiskitRuntimeService (name = name ).active_account ().get ("token" )
533
- super ().__init__ (token = token , host = IBM_SERVERLESS_HOST_URL )
574
+ super ().__init__ (
575
+ channel = channel ,
576
+ token = token ,
577
+ instance = instance ,
578
+ host = IBM_SERVERLESS_HOST_URL ,
579
+ )
534
580
535
581
@staticmethod
536
582
def save_account (
537
583
token : Optional [str ] = None ,
538
584
name : Optional [str ] = None ,
539
585
overwrite : Optional [bool ] = False ,
586
+ instance : Optional [str ] = None ,
587
+ channel : str = Channel .IBM_QUANTUM .value ,
540
588
) -> None :
541
589
"""
542
590
Save the account to disk for future use.
@@ -545,12 +593,25 @@ def save_account(
545
593
token: IBM Quantum API token
546
594
name: Name of the account to save
547
595
overwrite: ``True`` if the existing account is to be overwritten
596
+ instance: IBM Cloud CRN
597
+ channel: identifies the method to use to authenticate the user
548
598
"""
549
- QiskitRuntimeService .save_account (token = token , name = name , overwrite = overwrite )
599
+ QiskitRuntimeService .save_account (
600
+ token = token ,
601
+ name = name ,
602
+ overwrite = overwrite ,
603
+ instance = instance ,
604
+ channel = channel ,
605
+ )
550
606
551
607
552
- def _upload_with_docker_image (
553
- program : QiskitFunction , url : str , token : str , span : Any , client : RunService
608
+ def _upload_with_docker_image ( # pylint: disable=too-many-positional-arguments
609
+ program : QiskitFunction ,
610
+ url : str ,
611
+ token : str ,
612
+ span : Any ,
613
+ client : RunService ,
614
+ instance : Optional [str ],
554
615
) -> RunnableQiskitFunction :
555
616
"""Uploads function with custom docker image.
556
617
@@ -559,6 +620,7 @@ def _upload_with_docker_image(
559
620
url (str): upload gateway url
560
621
token (str): auth token
561
622
span (Any): tracing span
623
+ instance (Optional[str]): IBM Cloud crn
562
624
563
625
Returns:
564
626
str: uploaded function name
@@ -575,7 +637,7 @@ def _upload_with_docker_image(
575
637
"env_vars" : json .dumps (program .env_vars or {}),
576
638
"description" : program .description ,
577
639
},
578
- headers = { "Authorization" : f"Bearer { token } " } ,
640
+ headers = get_headers ( token = token , instance = instance ) ,
579
641
timeout = REQUESTS_TIMEOUT ,
580
642
)
581
643
)
@@ -587,8 +649,13 @@ def _upload_with_docker_image(
587
649
return RunnableQiskitFunction .from_json (response_data )
588
650
589
651
590
- def _upload_with_artifact (
591
- program : QiskitFunction , url : str , token : str , span : Any , client : RunService
652
+ def _upload_with_artifact ( # pylint: disable=too-many-positional-arguments
653
+ program : QiskitFunction ,
654
+ url : str ,
655
+ token : str ,
656
+ span : Any ,
657
+ client : RunService ,
658
+ instance : Optional [str ],
592
659
) -> RunnableQiskitFunction :
593
660
"""Uploads function with artifact.
594
661
@@ -597,6 +664,7 @@ def _upload_with_artifact(
597
664
url (str): endpoint for gateway upload
598
665
token (str): auth token
599
666
span (Any): tracing span
667
+ instance (Optional[str]): IBM Cloud crn
600
668
601
669
Raises:
602
670
QiskitServerlessException: if no entrypoint or size of artifact is too large.
@@ -645,7 +713,7 @@ def _upload_with_artifact(
645
713
"description" : program .description ,
646
714
},
647
715
files = {"artifact" : file },
648
- headers = { "Authorization" : f"Bearer { token } " } ,
716
+ headers = get_headers ( token = token , instance = instance ) ,
649
717
timeout = REQUESTS_TIMEOUT ,
650
718
)
651
719
)
0 commit comments