diff --git a/cyborg/accelerator/drivers/fpga/intel/driver.py b/cyborg/accelerator/drivers/fpga/intel/driver.py index b1e24625..7d0c7741 100644 --- a/cyborg/accelerator/drivers/fpga/intel/driver.py +++ b/cyborg/accelerator/drivers/fpga/intel/driver.py @@ -46,7 +46,7 @@ class IntelFPGADriver(FPGADriver): else: bdf = sysinfo.get_bdf_by_path(path) bdfs = sysinfo.split_bdf(bdf) - cmd = ["sudo", "fpgaconf"] + cmd = ["sudo", "/usr/bin/fpgaconf"] for i in zip(["-b", "-d", "-f"], bdfs): cmd.extend(i) cmd.append(image) diff --git a/cyborg/agent/manager.py b/cyborg/agent/manager.py index c1cdf486..5773ec72 100644 --- a/cyborg/agent/manager.py +++ b/cyborg/agent/manager.py @@ -18,6 +18,8 @@ from oslo_service import periodic_task from cyborg.accelerator.drivers.fpga.base import FPGADriver from cyborg.agent.resource_tracker import ResourceTracker +from cyborg.agent.rpcapi import AgentAPI +from cyborg.image.api import API as ImageAPI from cyborg.conductor import rpcapi as cond_api from cyborg.conf import CONF @@ -34,6 +36,8 @@ class AgentManager(periodic_task.PeriodicTasks): self.host = host or CONF.host self.fpga_driver = FPGADriver() self.cond_api = cond_api.ConductorAPI() + self.agent_api = AgentAPI() + self.image_api = ImageAPI() self._rt = ResourceTracker(host, self.cond_api) def periodic_tasks(self, context, raise_on_error=False): @@ -43,11 +47,27 @@ class AgentManager(periodic_task.PeriodicTasks): """List installed hardware.""" pass - def fpga_program(self, context, accelerator, image): + def fpga_program(self, context, deployable_uuid, image_uuid): """ Program a FPGA regoin, image can be a url or local file""" # TODO (Shaohe Feng) Get image from glance. # And add claim and rollback logical. - raise NotImplementedError() + path = self._download_bitstream(context, image_uuid) + dep = self.cond_api.deployable_get(context, deployable_uuid) + driver = self.fpga_driver.create(dep.vendor) + driver.program(dep.address, path) + + def _download_bitstream(self, context, bitstream_uuid): + """download the bistream + + :param context: the context + :param bistream_uuid: v4 uuid of the bitstream to reprogram + :returns: the path to bitstream downloaded, None if fail to download + """ + download_path = "/tmp/" + bitstream_uuid + ".bin" + self.image_api.download(context, + bitstream_uuid, + dest_path=download_path) + return download_path @periodic_task.periodic_task(run_immediately=True) def update_available_resource(self, context, startup=True): diff --git a/cyborg/agent/rpcapi.py b/cyborg/agent/rpcapi.py index f683dc01..2b9cd38b 100644 --- a/cyborg/agent/rpcapi.py +++ b/cyborg/agent/rpcapi.py @@ -21,7 +21,7 @@ import oslo_messaging as messaging from cyborg.common import constants from cyborg.common import rpc from cyborg.objects import base as objects_base - +from cyborg import objects CONF = cfg.CONF @@ -50,3 +50,20 @@ class AgentAPI(object): def hardware_list(self, context, values): """Signal the agent to find local hardware.""" pass + + def program_fpga_with_bitstream(self, + context, + deployable_uuid, + bitstream_uuid): + """Actiion to program a target FPGA""" + version = '1.0' + + dpl_get = objects.Deployable.get(context, deployable_uuid) + if not dpl_get: + # TODO (Li Liu) throw an exception here + return 0 + + cctxt = self.client.prepare(server=dpl_get.host, version=version) + return cctxt.call(context, 'fpga_program', + deployable_uuid=deployable_uuid, + image_uuid=bitstream_uuid) diff --git a/cyborg/api/controllers/v1/__init__.py b/cyborg/api/controllers/v1/__init__.py index 6b3d2085..20b13d72 100644 --- a/cyborg/api/controllers/v1/__init__.py +++ b/cyborg/api/controllers/v1/__init__.py @@ -22,6 +22,7 @@ from wsme import types as wtypes from cyborg.api.controllers import base from cyborg.api.controllers import link from cyborg.api.controllers.v1 import accelerators +from cyborg.api.controllers.v1 import deployables from cyborg.api import expose @@ -51,6 +52,7 @@ class Controller(rest.RestController): """Version 1 API controller root""" accelerators = accelerators.AcceleratorsController() + deployables = deployables.DeployablesController() @expose.expose(V1) def get(self): diff --git a/cyborg/api/controllers/v1/deployables.py b/cyborg/api/controllers/v1/deployables.py index c7c2ca69..94210ea5 100644 --- a/cyborg/api/controllers/v1/deployables.py +++ b/cyborg/api/controllers/v1/deployables.py @@ -28,6 +28,7 @@ from cyborg.common import exception from cyborg.common import policy from cyborg import objects from cyborg.quota import QUOTAS +from cyborg.agent.rpcapi import AgentAPI class Deployable(base.APIBase): @@ -151,10 +152,14 @@ class DeployablesController(base.CyborgController): """ image_uuid = program_info[0]['value'][0]['image_uuid'] + agent_api = AgentAPI() obj_dep = objects.Deployable.get(pecan.request.context, uuid) # Set attribute of the new bitstream/image information obj_dep.add_attribute(pecan.request.context, 'image_uuid', image_uuid) # TODO (Li Liu) Trigger the program api in Agnet. + agent_api.program_fpga_with_bitstream(pecan.request.context, + uuid, + image_uuid) return Deployable.convert_with_links(obj_dep) @policy.authorize_wsgi("cyborg:deployable", "create", False) diff --git a/cyborg/tests/unit/accelerator/drivers/fpga/base.py b/cyborg/tests/unit/accelerator/drivers/fpga/base.py index 20413300..0bd6ab69 100644 --- a/cyborg/tests/unit/accelerator/drivers/fpga/base.py +++ b/cyborg/tests/unit/accelerator/drivers/fpga/base.py @@ -92,7 +92,7 @@ class TestFPGADriver(base.TestCase): b = "0x5e" d = "0x00" f = "0x0" - expect_cmd = ['sudo', 'fpgaconf', '-b', b, + expect_cmd = ['sudo', '/usr/bin/fpgaconf', '-b', b, '-d', d, '-f', f, '/path/image'] mock_popen.return_value = p() intel = FPGADriver.create("intel") diff --git a/cyborg/tests/unit/accelerator/drivers/fpga/intel/driver.py b/cyborg/tests/unit/accelerator/drivers/fpga/intel/driver.py index 5760ecf7..6719d7ca 100644 --- a/cyborg/tests/unit/accelerator/drivers/fpga/intel/driver.py +++ b/cyborg/tests/unit/accelerator/drivers/fpga/intel/driver.py @@ -80,7 +80,7 @@ class TestIntelFPGADriver(base.TestCase): b = "0x5e" d = "0x00" f = "0x0" - expect_cmd = ['sudo', 'fpgaconf', '-b', b, + expect_cmd = ['sudo', '/usr/bin/fpgaconf', '-b', b, '-d', d, '-f', f, '/path/image'] mock_popen.return_value = p() intel = IntelFPGADriver() diff --git a/cyborg/tests/unit/api/controllers/v1/test_fpga_program.py b/cyborg/tests/unit/api/controllers/v1/test_fpga_program.py index 2e0db881..43734f31 100644 --- a/cyborg/tests/unit/api/controllers/v1/test_fpga_program.py +++ b/cyborg/tests/unit/api/controllers/v1/test_fpga_program.py @@ -19,6 +19,7 @@ from six.moves import http_client from cyborg.api.controllers.v1.deployables import Deployable from cyborg.tests.unit.api.controllers.v1 import base as v1_test from cyborg.tests.unit import fake_deployable +from cyborg.agent.rpcapi import AgentAPI class TestFPGAProgramController(v1_test.APITestV1): @@ -29,13 +30,15 @@ class TestFPGAProgramController(v1_test.APITestV1): self.deployable_uuids = ['0acbf8d6-e02a-4394-aae3-57557d209498'] @mock.patch('cyborg.objects.Deployable.get') - def test_program(self, mock_get_dep): + @mock.patch('cyborg.agent.rpcapi.AgentAPI.program_fpga_with_bitstream') + def test_program(self, mock_program, mock_get_dep): self.headers['X-Roles'] = 'admin' self.headers['Content-Type'] = 'application/json' dep_uuid = self.deployable_uuids[0] fake_dep = fake_deployable.fake_deployable_obj(self.context, uuid=dep_uuid) mock_get_dep.return_value = fake_dep + mock_program.return_value = None body = [{"image_uuid": "9a17439a-85d0-4c53-a3d3-0f68a2eac896"}] response = self.\ patch_json('/accelerators/deployables/%s/program' % dep_uuid,