code: def addJobShape(self, jobShape):
          
          chosenNodeShape = None
          for nodeShape in self.nodeShapes:
              if NodeReservation(nodeShape).fits(jobShape):
                  # This node shape is the first that fits this jobShape
                  chosenNodeShape = nodeShape
                  break
  
          if chosenNodeShape is None:
              logger.warning("Couldn't fit job with requirements %r into any nodes in the nodeTypes "
                             "list." % jobShape)
              return
  
          # grab current list of job objects appended to this nodeType
          nodeReservations = self.nodeReservations[chosenNodeShape]
          for nodeReservation in nodeReservations:
              if nodeReservation.attemptToAddJob(jobShape, chosenNodeShape, self.targetTime):
                  # We succeeded adding the job to this node reservation. Now we're done.
                  return
  
          reservation = NodeReservation(chosenNodeShape)
          currentTimeAllocated = chosenNodeShape.wallTime
          adjustEndingReservationForJob(reservation, jobShape, 0)
          self.nodeReservations[chosenNodeShape].append(reservation)
  
          # Extend the reservation if necessary to cover the job's entire runtime.
          while currentTimeAllocated < jobShape.wallTime:
              extendThisReservation = NodeReservation(reservation.shape)
              currentTimeAllocated += chosenNodeShape.wallTime
              reservation.nReservation = extendThisReservation
              reservation = extendThisReservation
docs: Function adds the job to the first node reservation in which it will fit (this is the
          bin-packing aspect).