Revision 76b62028

b/htools/Ganeti/HTools/Program/Hbal.hs
205 205
            case jids of
206 206
              Bad x -> return $ Bad x
207 207
              Ok x -> do
208
                putStrLn $ "Got job IDs " ++ commaJoin x
208
                putStrLn $ "Got job IDs " ++ commaJoin (map show x)
209 209
                waitForJobs client x
210 210
         )
211 211
  case jrs of
b/htools/Ganeti/Luxi.hs
44 44
  ) where
45 45

  
46 46
import Data.IORef
47
import Data.Ratio (numerator, denominator)
47 48
import Control.Monad
48 49
import Text.JSON (encodeStrict, decodeStrict)
49 50
import qualified Text.JSON as J
......
73 74
-- * Generic protocol functionality
74 75

  
75 76
-- | The Ganeti job type.
76
type JobId = String
77
type JobId = Int
77 78

  
78 79
$(declareSADT "QrViaLuxi"
79 80
  [ ("QRLock", 'qrLock)
......
107 108
     , ("lock",   [t| Bool     |], [| id |])
108 109
     ])
109 110
  , (luxiReqQueryJobs,
110
     [ ("ids",    [t| [Int]    |], [| map show |])
111
     [ ("ids",    [t| [Int]    |], [| id |])
111 112
     , ("fields", [t| [String] |], [| id |])
112 113
     ])
113 114
  , (luxiReqQueryExports,
......
129 130
     [ ("ops", [t| [[OpCode]] |], [| id |]) ]
130 131
    )
131 132
  , (luxiReqWaitForJobChange,
132
     [ ("job",      [t| Int     |], [| show |])
133
     [ ("job",      [t| Int     |], [| id |])
133 134
     , ("fields",   [t| [String]|], [| id |])
134 135
     , ("prev_job", [t| JSValue |], [| id |])
135 136
     , ("prev_log", [t| JSValue |], [| id |])
136 137
     , ("tmout",    [t| Int     |], [| id |])
137 138
     ])
138 139
  , (luxiReqArchiveJob,
139
     [ ("job", [t| Int |], [| show |]) ]
140
     [ ("job", [t| Int |], [| id |]) ]
140 141
    )
141 142
  , (luxiReqAutoArchiveJobs,
142 143
     [ ("age",   [t| Int |], [| id |])
143 144
     , ("tmout", [t| Int |], [| id |])
144 145
     ])
145 146
  , (luxiReqCancelJob,
146
     [ ("job", [t| Int |], [| show |]) ]
147
     [ ("job", [t| Int |], [| id |]) ]
147 148
    )
148 149
  , (luxiReqSetDrainFlag,
149 150
     [ ("flag", [t| Bool |], [| id |]) ]
......
267 268
  case call of
268 269
    ReqQueryJobs -> do
269 270
              (jid, jargs) <- fromJVal args
270
              rid <- mapM (tryRead "parsing job ID" . fromJSString) jid
271
              rid <- mapM parseJobId jid
271 272
              let rargs = map fromJSString jargs
272 273
              return $ QueryJobs rid rargs
273 274
    ReqQueryInstances -> do
......
307 308
                    J.readJSON d `ap`
308 309
                    J.readJSON e
309 310
                  _ -> J.Error "Not enough values"
310
              rid <- tryRead "parsing job ID" jid
311
              rid <- parseJobId jid
311 312
              return $ WaitForJobChange rid fields pinfo pidx wtmout
312 313
    ReqArchiveJob -> do
313 314
              [jid] <- fromJVal args
314
              rid <- tryRead "parsing job ID" jid
315
              rid <- parseJobId jid
315 316
              return $ ArchiveJob rid
316 317
    ReqAutoArchiveJobs -> do
317 318
              (age, tmout) <- fromJVal args
......
327 328
              return $ QueryTags kind name
328 329
    ReqCancelJob -> do
329 330
              [job] <- fromJVal args
330
              rid <- tryRead "parsing job ID" job
331
              rid <- parseJobId job
331 332
              return $ CancelJob rid
332 333
    ReqSetDrainFlag -> do
333 334
              [flag] <- fromJVal args
......
359 360

  
360 361
-- | Parses a job ID.
361 362
parseJobId :: JSValue -> Result JobId
362
parseJobId (JSString x) = Ok $ fromJSString x
363
parseJobId (JSString x) = tryRead "parsing job id" . fromJSString $ x
364
parseJobId (JSRational _ x) =
365
  if denominator x /= 1
366
    then Bad $ "Got fractional job ID from master daemon?! Value:" ++ show x
367
    -- FIXME: potential integer overflow here on 32-bit platforms
368
    else Ok . fromIntegral . numerator $ x
363 369
parseJobId x = Bad $ "Wrong type/value for job id: " ++ show x
364 370

  
365 371
-- | Parse job submission result.
......
383 389
-- | Custom queryJobs call.
384 390
queryJobsStatus :: Client -> [JobId] -> IO (Result [JobStatus])
385 391
queryJobsStatus s jids = do
386
  rval <- callMethod (QueryJobs (map read jids) ["status"]) s
392
  rval <- callMethod (QueryJobs jids ["status"]) s
387 393
  return $ case rval of
388 394
             Bad x -> Bad x
389 395
             Ok y -> case J.readJSON y::(J.Result [[JobStatus]]) of
b/lib/client/gnt_debug.py
312 312
    result = cl.SubmitManyJobs(jobs)
313 313
    if not (len(result) == 2 and
314 314
            compat.all(len(i) == 2 for i in result) and
315
            compat.all(isinstance(i[1], basestring) for i in result) and
315
            isinstance(result[0][1], int) and
316
            isinstance(result[1][1], basestring) and
316 317
            result[0][0] and not result[1][0]):
317 318
      raise errors.OpExecError("Submitting multiple jobs did not work as"
318 319
                               " expected, result %s" % result)
b/lib/client/gnt_job.py
1 1
#
2 2
#
3 3

  
4
# Copyright (C) 2006, 2007 Google Inc.
4
# Copyright (C) 2006, 2007, 2012 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
60 60
    raise errors.ProgrammerError("Unknown job status code '%s'" % value)
61 61

  
62 62

  
63
def _ParseJobIds(args):
64
  """Parses a list of string job IDs into integers.
65

  
66
  @param args: list of strings
67
  @return: list of integers
68
  @raise OpPrereqError: in case of invalid values
69

  
70
  """
71
  try:
72
    return [int(a) for a in args]
73
  except (ValueError, TypeError), err:
74
    raise errors.OpPrereqError("Invalid job ID passed: %s" % err,
75
                               errors.ECODE_INVAL)
76

  
77

  
63 78
def ListJobs(opts, args):
64 79
  """List the jobs
65 80

  
......
85 100
                     opts.separator, not opts.no_headers,
86 101
                     format_override=fmtoverride, verbose=opts.verbose,
87 102
                     force_filter=opts.force_filter, namefield="id",
88
                     qfilter=qfilter)
103
                     qfilter=qfilter, isnumeric=True)
89 104

  
90 105

  
91 106
def ListJobFields(opts, args):
......
203 218
    "opstart", "opexec", "opend", "received_ts", "start_ts", "end_ts",
204 219
    ]
205 220

  
206
  result = GetClient().Query(constants.QR_JOB, selected_fields,
207
                             qlang.MakeSimpleFilter("id", args)).data
221
  qfilter = qlang.MakeSimpleFilter("id", _ParseJobIds(args))
222
  result = GetClient().Query(constants.QR_JOB, selected_fields, qfilter).data
208 223

  
209 224
  first = True
210 225

  
b/lib/jqueue.py
1 1
#
2 2
#
3 3

  
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc.
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
220 220
      raise errors.GenericError("A job needs at least one opcode")
221 221

  
222 222
    self.queue = queue
223
    self.id = job_id
223
    self.id = int(job_id)
224 224
    self.ops = [_QueuedOpCode(op) for op in ops]
225 225
    self.log_serial = 0
226 226
    self.received_timestamp = TimeStampNow()
......
267 267
    """
268 268
    obj = _QueuedJob.__new__(cls)
269 269
    obj.queue = queue
270
    obj.id = state["id"]
270
    obj.id = int(state["id"])
271 271
    obj.received_timestamp = state.get("received_timestamp", None)
272 272
    obj.start_timestamp = state.get("start_timestamp", None)
273 273
    obj.end_timestamp = state.get("end_timestamp", None)
......
1384 1384

  
1385 1385
    @type job: L{_QueuedJob}
1386 1386
    @param job: Job object
1387
    @type dep_job_id: string
1387
    @type dep_job_id: int
1388 1388
    @param dep_job_id: ID of dependency job
1389 1389
    @type dep_status: list
1390 1390
    @param dep_status: Required status
1391 1391

  
1392 1392
    """
1393
    assert ht.TString(job.id)
1394
    assert ht.TString(dep_job_id)
1393
    assert ht.TJobId(job.id)
1394
    assert ht.TJobId(dep_job_id)
1395 1395
    assert ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))(dep_status)
1396 1396

  
1397 1397
    if job.id == dep_job_id:
......
1446 1446

  
1447 1447
    @attention: Do not call until L{CheckAndRegister} returned a status other
1448 1448
      than C{WAITDEP} for C{job_id}, or behaviour is undefined
1449
    @type job_id: string
1449
    @type job_id: int
1450 1450
    @param job_id: Job ID
1451 1451

  
1452 1452
    """
1453
    assert ht.TString(job_id)
1453
    assert ht.TJobId(job_id)
1454 1454

  
1455 1455
    self._lock.acquire()
1456 1456
    try:
......
1803 1803

  
1804 1804
    @type count: integer
1805 1805
    @param count: how many serials to return
1806
    @rtype: str
1807
    @return: a string representing the job identifier.
1806
    @rtype: list of int
1807
    @return: a list of job identifiers.
1808 1808

  
1809 1809
    """
1810 1810
    assert ht.TPositiveInt(count)
......
1870 1870
    for filename in utils.ListVisibleFiles(constants.QUEUE_DIR):
1871 1871
      m = constants.JOB_FILE_RE.match(filename)
1872 1872
      if m:
1873
        jlist.append(m.group(1))
1873
        jlist.append(int(m.group(1)))
1874 1874
    if sort:
1875
      jlist = utils.NiceSort(jlist)
1875
      jlist.sort()
1876 1876
    return jlist
1877 1877

  
1878 1878
  def _LoadJobUnlocked(self, job_id):
......
1882 1882
    existing, or try to load the job from the disk. If loading from
1883 1883
    disk, it will also add the job to the cache.
1884 1884

  
1885
    @type job_id: int
1885 1886
    @param job_id: the job id
1886 1887
    @rtype: L{_QueuedJob} or None
1887 1888
    @return: either None or the job object
......
1920 1921

  
1921 1922
    Given a job file, read, load and restore it in a _QueuedJob format.
1922 1923

  
1923
    @type job_id: string
1924
    @type job_id: int
1924 1925
    @param job_id: job identifier
1925 1926
    @type try_archived: bool
1926 1927
    @param try_archived: Whether to try loading an archived job
......
1968 1969
    In case of error reading the job, it gets returned as None, and the
1969 1970
    exception is logged.
1970 1971

  
1971
    @type job_id: string
1972
    @type job_id: int
1972 1973
    @param job_id: job identifier
1973 1974
    @type try_archived: bool
1974 1975
    @param try_archived: Whether to try loading an archived job
......
2181 2182
  def _GetJobStatusForDependencies(self, job_id):
2182 2183
    """Gets the status of a job for dependencies.
2183 2184

  
2184
    @type job_id: string
2185
    @type job_id: int
2185 2186
    @param job_id: Job ID
2186 2187
    @raise errors.JobLost: If job can't be found
2187 2188

  
2188 2189
    """
2189
    if not isinstance(job_id, basestring):
2190
      job_id = jstore.FormatJobID(job_id)
2191

  
2192 2190
    # Not using in-memory cache as doing so would require an exclusive lock
2193 2191

  
2194 2192
    # Try to load from disk
......
2229 2227
                        timeout):
2230 2228
    """Waits for changes in a job.
2231 2229

  
2232
    @type job_id: string
2230
    @type job_id: int
2233 2231
    @param job_id: Job identifier
2234 2232
    @type fields: list of strings
2235 2233
    @param fields: Which fields to check for changes
......
2264 2262

  
2265 2263
    This will only succeed if the job has not started yet.
2266 2264

  
2267
    @type job_id: string
2265
    @type job_id: int
2268 2266
    @param job_id: job ID of job to be cancelled.
2269 2267

  
2270 2268
    """
......
2331 2329

  
2332 2330
    This is just a wrapper over L{_ArchiveJobsUnlocked}.
2333 2331

  
2334
    @type job_id: string
2332
    @type job_id: int
2335 2333
    @param job_id: Job ID of job to be archived.
2336 2334
    @rtype: bool
2337 2335
    @return: Whether job was archived
......
2433 2431
    @param qfilter: Query filter
2434 2432

  
2435 2433
    """
2436
    (qobj, ctx, sort_by_name) = self._Query(fields, qfilter)
2434
    (qobj, ctx, _) = self._Query(fields, qfilter)
2437 2435

  
2438
    return query.GetQueryResponse(qobj, ctx, sort_by_name=sort_by_name)
2436
    return query.GetQueryResponse(qobj, ctx, sort_by_name=False)
2439 2437

  
2440 2438
  def OldStyleQueryJobs(self, job_ids, fields):
2441 2439
    """Returns a list of jobs in queue.
......
2449 2447
        the requested fields
2450 2448

  
2451 2449
    """
2450
    # backwards compat:
2451
    job_ids = [int(jid) for jid in job_ids]
2452 2452
    qfilter = qlang.MakeSimpleFilter("id", job_ids)
2453 2453

  
2454
    (qobj, ctx, sort_by_name) = self._Query(fields, qfilter)
2454
    (qobj, ctx, _) = self._Query(fields, qfilter)
2455 2455

  
2456
    return qobj.OldStyleQuery(ctx, sort_by_name=sort_by_name)
2456
    return qobj.OldStyleQuery(ctx, sort_by_name=False)
2457 2457

  
2458 2458
  @locking.ssynchronized(_LOCK)
2459 2459
  def PrepareShutdown(self):
b/lib/jstore.py
1 1
#
2 2
#
3 3

  
4
# Copyright (C) 2006, 2007 Google Inc.
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
174 174

  
175 175

  
176 176
def FormatJobID(job_id):
177
  """Convert a job ID to string format.
177
  """Convert a job ID to int format.
178 178

  
179
  Currently this just does C{str(job_id)} after performing some
180
  checks, but if we want to change the job id format this will
181
  abstract this change.
179
  Currently this just is a no-op that performs some checks, but if we
180
  want to change the job id format this will abstract this change.
182 181

  
183 182
  @type job_id: int or long
184 183
  @param job_id: the numeric job id
185
  @rtype: str
184
  @rtype: int
186 185
  @return: the formatted job id
187 186

  
188 187
  """
......
191 190
  if job_id < 0:
192 191
    raise errors.ProgrammerError("Job ID %s is negative" % job_id)
193 192

  
194
  return str(job_id)
193
  return job_id
195 194

  
196 195

  
197 196
def GetArchiveDirectory(job_id):
b/lib/qlang.py
298 298
    try:
299 299
      number = int(text)
300 300
    except (TypeError, ValueError), err:
301
      raise errors.OpPrereqError("Invalid integer passed: %s" % str(err),
301
      raise errors.OpPrereqError("Invalid job ID passed: %s" % str(err),
302 302
                                 errors.ECODE_INVAL)
303 303
    return [OP_EQUAL, namefield, number]
304 304
  elif _CheckGlobbing(text):
b/lib/query.py
746 746
        (status, name) = _ProcessResult(self._name_fn(ctx, item))
747 747
        assert status == constants.RS_NORMAL
748 748
        # TODO: Are there cases where we wouldn't want to use NiceSort?
749
        # Answer: if the name field is non-string...
749 750
        result.append((utils.NiceSortKey(name), idx, row))
750 751
      else:
751 752
        result.append(row)
......
2267 2268

  
2268 2269
  """
2269 2270
  fields = [
2270
    (_MakeField("id", "ID", QFT_TEXT, "Job ID"),
2271
    (_MakeField("id", "ID", QFT_NUMBER, "Job ID"),
2271 2272
     None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
2272 2273
    (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2273 2274
     None, 0, _JobUnavail(lambda job: job.CalcStatus())),
b/qa/qa_rapi.py
148 148

  
149 149

  
150 150
def _VerifyReturnsJob(data):
151
  AssertMatch(data, r"^\d+$")
151
  if not isinstance(data, int):
152
    AssertMatch(data, r"^\d+$")
152 153

  
153 154

  
154 155
def TestVersion():
b/test/ganeti.jqueue_unittest.py
1 1
#!/usr/bin/python
2 2
#
3 3

  
4
# Copyright (C) 2010, 2011 Google Inc.
4
# Copyright (C) 2010, 2011, 2012 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
702 702

  
703 703
      # Check job status
704 704
      self.assertEqual(job.CalcStatus(), constants.JOB_STATUS_ERROR)
705
      self.assertEqual(job.GetInfo(["id"]), [str(job_id)])
705
      self.assertEqual(job.GetInfo(["id"]), [job_id])
706 706
      self.assertEqual(job.GetInfo(["status"]), [constants.JOB_STATUS_ERROR])
707 707

  
708 708
      # Check opcode status
......
926 926
           for i in range(3)]
927 927

  
928 928
    # Create job
929
    job_id = str(28492)
929
    job_id = 28492
930 930
    job = self._CreateJob(queue, job_id, ops)
931 931

  
932 932
    self.assertEqual(job.CalcStatus(), constants.JOB_STATUS_QUEUED)
b/test/ganeti.jstore_unittest.py
36 36

  
37 37
class TestFormatJobID(testutils.GanetiTestCase):
38 38
  def test(self):
39
    self.assertEqual(jstore.FormatJobID(0), "0")
40
    self.assertEqual(jstore.FormatJobID(30498), "30498")
39
    self.assertEqual(jstore.FormatJobID(0), 0)
40
    self.assertEqual(jstore.FormatJobID(30498), 30498)
41 41
    self.assertEqual(jstore.FormatJobID(319472592764518609),
42
                     "319472592764518609")
42
                     319472592764518609)
43 43

  
44 44
  def testErrors(self):
45 45
    for i in [-1, -2288, -9667, -0.205641, 0.0, 0.1, 13041.4472, "", "Hello",
b/test/ganeti.query_unittest.py
1162 1162
    for (what, fielddefs) in query.ALL_FIELDS.items():
1163 1163
      if what == constants.QR_JOB:
1164 1164
        namefield = "id"
1165
      elif what == constants.QR_EXPORT:
1166
        namefield = "export"
1165
        nameval = 123
1166
        namevalempty = 0
1167
        genval = lambda i: i * 10
1168
        randvals = [17361, 22015, 13193, 15215]
1167 1169
      else:
1168
        namefield = "name"
1169
      nameval = "abc"
1170
      namevalempty = ""
1171
      genval = lambda i: "x%s" % i
1172
      randvals = ["x17361", "x22015", "x13193", "x15215"]
1170
        nameval = "abc"
1171
        namevalempty = ""
1172
        genval = lambda i: "x%s" % i
1173
        randvals = ["x17361", "x22015", "x13193", "x15215"]
1174
        if what == constants.QR_EXPORT:
1175
          namefield = "export"
1176
        else:
1177
          namefield = "name"
1173 1178

  
1174 1179
      assert namefield in fielddefs
1175 1180

  
......
1247 1252
    for (what, fielddefs) in query.ALL_FIELDS.items():
1248 1253
      if what == constants.QR_JOB:
1249 1254
        namefield = "id"
1255
        nameval = 123
1250 1256
      elif what == constants.QR_EXPORT:
1251 1257
        namefield = "export"
1258
        nameval = "value"
1252 1259
      else:
1253 1260
        namefield = "name"
1254
      nameval = "value"
1261
        nameval = "value"
1255 1262

  
1256 1263
      checks = [
1257 1264
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],

Also available in: Unified diff